[https://drive.google.com/file/d/0B1xZLc69ZnLkWWRuaE9ycmhIXzg/view?usp=sharing] This is the entire project - vs2013.
1You can see how the FindVisualChildren()
Without a good Minimal, Complete, and Verifiable example that reliably reproduces your problem, it's impossible to know what exactly that problem is. From your problem description, it sounds like you might be running some code in the constructor, which should instead be placed elsewhere so that it can safely run multiple times.
That said, I do agree with the basic motivation (though not the presentation at all) of the comment that explains that you should not be messing directly with the visual tree at all.
In many APIs, and WPF especially, the "right way" to do things is to create a custom class — your "view model — which is a type that maintains the state of your program in a completely separate and independent way from the visual aspect of your program. In your code example, this class would keep track of the word and letters to be displayed, and any other state for the program's underlying logic, as well as any methods for controlling or modifying that state.
As an example, I wrote a class that could serve as the view model in your program:
class Model : INotifyPropertyChanged
{
private static readonly Random _random = new Random();
private const int _kcolumnCount = 6;
private const int _krowCount = 4;
private string _word;
public ObservableCollection<char> Letters { get; private set; }
public string Word
{
get { return _word; }
set { _UpdateValue(ref _word, value); }
}
public int ColumnCount { get { return _kcolumnCount; } }
public int RowCount { get { return _krowCount; } }
public Model()
{
Letters = new ObservableCollection<char>();
for (int i = 0; i < ColumnCount * RowCount; i++)
{
Letters.Add(' ');
}
}
public void UpdateLetters()
{
char[] characters = new char[ColumnCount * RowCount];
for (int i = 0; i < characters.Length; i++)
{
if (Word != null && i < Word.Length)
{
characters[i] = Word[i];
}
else
{
characters[i] = (char)('a' + _random.Next(26));
}
}
for (int i = characters.Length - 1; i > 0 ; i--)
{
int j = _random.Next(i + 1);
if (i != j)
{
char chT = characters[i];
characters[i] = characters[j];
characters[j] = chT;
}
}
for (int i = 0; i < characters.Length; i++)
{
Letters[i] = characters[i];
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void _UpdateValue<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!object.Equals(field, newValue))
{
field = newValue;
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
It has three key features:
It also includes the values for the row and column counts of your grid.
You'll notice that this class is entirely comprehensible as the fundamental logic for your program, and yet has nothing in it that is directly related to the implementation of the UI.
Having written such a class, it is then very easy to create a simple XAML markup for the user interface:
<Window x:Class="TestSO36231872RandomLetters.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO36231872RandomLetters"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:Model/>
</Window.DataContext>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="3">
<TextBox Width="80" Text="{Binding Word}"/>
<Button HorizontalAlignment="Left" Content="Update Word"
Margin="3,0" Click="Button_Click"/>
</StackPanel>
<ItemsControl ItemsSource="{Binding Letters}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Rows="{Binding RowCount}" Columns="{Binding ColumnCount}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type s:Char}">
<Button Content="{Binding}" FontSize="16"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
The only thing left is the Click
event handler (there are other ways to deal with user input, but this is the simplest and suits this particular example just fine). This goes in the code-behind for the Window
class (i.e. "MainWindow.xaml.cs"):
private void Button_Click(object sender, RoutedEventArgs e)
{
Model model = (Model)DataContext;
model.UpdateLetters();
}
This causes the UpdateLetters()
method to be called with the "Update Word" button is clicked.
Of course, this is not your full program. I don't really fully understand what you mean by "the user must create the word using every letter in every button". If you expect the user to click the buttons in the grid, you'll need to hook up another handler in the template for the buttons, and of course deal with the context somehow. But that's a "whole 'nother ball o' wax". I hope the above suffices to get you pointed back in the correct direction for dealing with WPF.