Since all cells should have the same size, you could also use a UniformGrid. As Leo Bartkus suggested, you could use code-behind to generate the cells and text boxes. To do this, start by creating a placeholder for the Sudoku table in XAML:
<Border x:Name="SudokuTable" />
Assuming you're using a Window
, here's the code-behind:
public partial class MainWindow : Window
{
private const int InnerWidth = 3;
private const int OuterWidth = InnerWidth * InnerWidth;
private const int Thin = 1;
private const int Thick = 3;
public MainWindow()
{
InitializeComponent();
InitializeViewModel();
InitializeSudokuTable();
}
public SudokuViewModel ViewModel => (SudokuViewModel)DataContext;
private void InitializeViewModel()
{
DataContext = new SudokuViewModel(OuterWidth);
}
private void InitializeSudokuTable()
{
var grid = new UniformGrid
{
Rows = OuterWidth,
Columns = OuterWidth
};
for (var i = 0; i < OuterWidth; i++)
{
for (var j = 0; j < OuterWidth; j++)
{
var border = CreateBorder(i, j);
border.Child = CreateTextBox(i, j);
grid.Children.Add(border);
}
}
SudokuTable.Child = grid;
}
private static Border CreateBorder(int i, int j)
{
var left = j % InnerWidth == 0 ? Thick : Thin;
var top = i % InnerWidth == 0 ? Thick : Thin;
var right = j == OuterWidth - 1 ? Thick : 0;
var bottom = i == OuterWidth - 1 ? Thick : 0;
return new Border
{
BorderThickness = new Thickness(left, top, right, bottom),
BorderBrush = Brushes.Black
};
}
private TextBox CreateTextBox(int i, int j)
{
var textBox = new TextBox
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
var binding = new Binding
{
Source = ViewModel,
Path = new PropertyPath($"[{i},{j}]"),
Mode = BindingMode.TwoWay
};
textBox.SetBinding(TextBox.TextProperty, binding);
return textBox;
}
}
The nested loop creates each Border
and TextBox
for the 81 cells. The border's thicknesses are determined based on the current cell's position. This will give you the typical Sudoku table look.
The text boxes are data-bound to the two-dimensional indexer property of the view model. Here's the view model:
public class SudokuViewModel : ViewModelBase
{
private readonly string[,] _values;
public SudokuViewModel(int width)
{
_values = new string[width, width];
}
public string this[int i, int j]
{
get => _values[i, j];
set => Set(ref _values[i, j], value);
}
}
This indexer returns a string, but you may want to change it to an integer and do the appropriate conversions and error checks. In any case, it's using MVVM Light to raise the PropertyChanged
event when the indexer property is updated.
I've created a repository with my solution here: https://github.com/redcurry/Sudoku.