我用C#,XAML和MVVM-Light构建了非常个基本的Sudoku应用程序。 MainWindow具有9x9网格和每个单元格81个文本框,而MainViewModel具有81个int属性。不漂亮。 Sudoku表看起来不像经典表,没有“厚边框”来区分每个象限。
现在,我想继续前进到一个新的水平,并使其美观实用。
每个单元格内容必须是“响应式”的吗?即根据窗口的宽度/高度调整大小(字体大小)。我已经开始研究处理字体颜色,背景颜色等的CellUserControl。
主窗口的基础应该是什么?网格,表格,列表框,自定义?
顺便说一下,我将充分利用MVVM-Light和数据绑定。
答案 0 :(得分:2)
由于所有单元格的大小均应相同,因此您也可以使用UniformGrid。正如Leo Bartkus所建议的那样,您可以使用后台代码生成单元格和文本框。为此,首先在XAML中为Sudoku表创建一个占位符:
<!-- Placeholder for Sudoku table (filled in code-behind) -->
<Border x:Name="SudokuTable" />
假设您使用的是Window
,下面是代码:
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;
}
}
嵌套循环为81个单元格分别创建Border
和TextBox
。边框的粗细是根据当前单元格的位置确定的。这将为您提供典型的Sudoku表格外观。
文本框数据绑定到视图模型的二维索引器属性。这是视图模型:
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);
}
}
此索引器返回一个字符串,但是您可能希望将其更改为整数并进行适当的转换和错误检查。无论如何,当索引器属性更新时,它都使用MVVM Light引发PropertyChanged
事件。
我已经在这里创建了一个带有解决方案的存储库:https://github.com/redcurry/Sudoku。
答案 1 :(得分:0)
这是我如何使用网格进行数独的示例...
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="textBox00" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"/>
<TextBox x:Name="textBox00_Copy" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="1"/>
<TextBox x:Name="textBox00_Copy1" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="2"/>
<TextBox x:Name="textBox00_Copy2" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="4"/>
<TextBox x:Name="textBox00_Copy3" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="5"/>
<TextBox x:Name="textBox00_Copy4" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="6"/>
<TextBox x:Name="textBox00_Copy5" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="2" Grid.Row="1"/>
<TextBox x:Name="textBox00_Copy6" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="4" Grid.Row="1"/>
<TextBox x:Name="textBox00_Copy7" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="5" Grid.Row="1"/>
<TextBox x:Name="textBox00_Copy8" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="1" Grid.Row="1"/>
<TextBox x:Name="textBox00_Copy9" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="1" Grid.Row="2"/>
<TextBox x:Name="textBox00_Copy10" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="2" Grid.Row="2"/>
<TextBox x:Name="textBox00_Copy11" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="4" Grid.Row="2"/>
<Rectangle Grid.Column="3" Fill="#FF663003" Grid.RowSpan="12"/>
<Rectangle Grid.Column="7" Fill="#FF663003" Grid.RowSpan="12"/>
<Rectangle Grid.Row="3" Fill="#FF663003" Grid.ColumnSpan="12"/>
<Rectangle Grid.Row="7" Fill="#FF663003" Grid.ColumnSpan="12"/>
这只是默认的文本框样式。您可以使用用户控件来使其与众不同,也可以通过在Blend for Visual Studio中加载xaml,为文本框创建自定义样式,右键单击该文本框并选择“编辑模板”。->编辑副本
答案 2 :(得分:0)
每个人都很好的答案,您的想法使我专注于目标:)
这是我现在的位置。
XAML:我正在使用冗长的XAML方法和一个网格。
网格:11x11-(数独单元格为9x9 +边框为2x2)。
<Grid Grid.Row="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="2px"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="2px"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="2px"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="2px"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Grid.Column="3" Grid.RowSpan="11" Fill="Black"></Rectangle>
<Rectangle Grid.Row="0" Grid.Column="7" Grid.RowSpan="11" Fill="Black"></Rectangle>
<Rectangle Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="11" Fill="Black"></Rectangle>
<Rectangle Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="11" Fill="Black"></Rectangle>
<local:CellUserControl Grid.Row="0" Grid.Column="0" DataContext="{Binding Path=Cells[0], Source={StaticResource Locator}}"/>
<local:CellUserControl Grid.Row="0" Grid.Column="1" DataContext="{Binding Path=Cells[1], Source={StaticResource Locator}}"/>
...
我很懒,所以我最终使用Excel电子表格枚举了81个单元格,并使用Floor.Math,MOD和Concatenate的组合:)
下一个挑战是将81个MVVM属性重构为更简单的内容。在XAML中:语法为{Binding Path = Cells [0]},并选择(现在)将属性放在ViewModelLocator中。
public IList<CellViewModel> Cells
{
get
{
return new List<CellViewModel>(ServiceLocator.Current.GetAllInstances<CellViewModel>());
}
}
XAML和代码很干净。我喜欢它,到目前为止。我仍在考虑IList的正确位置-应该将其保留在ViewModelLocator中还是应该将其实际保留在MainViewModel中?我想,要回答这个问题,我必须做一些单元测试。
非常感谢。