WPF 中的数独 - 我应该为表格使用什么基本元素?

我用 C#、XAML 和 MVVM-Light构建了一个非常基本的数独应用程序。MainWindow 有一个 9x9 的网格和每个单元格的 81 个文本框,而 MainViewModel 有 81 个 int 属性。不漂亮。数独表看起来不像经典的表,没有“更粗的边框”来区分每个象限。

现在,我想更上一层楼,让它变得漂亮实用。

每个单元格内容都必须是“响应式”的?即根据窗口的宽度/高度调整大小(字体大小)。我已经开始研究处理字体颜色、背景颜色等的 CellUserControl。

主窗口的基础应该是什么?网格、表格、列表框、自定义?

  • 我尝试了 Grid 和 Table,但通常最终会遇到死胡同。XAML 变得非常冗长和重复。

  • 我记得 MS 展示了 WPF 的强大功能,ListBox 显示为轮播,只需应用样式即可。这是一条有效的路径吗?

  • 自定义控件:它是矫枉过正还是完全适合这种情况?

顺便说一句,我将大量使用 MVVM-Light 和数据绑定。


慕的地6264312
浏览 161回答 3
3回答

拉风的咖菲猫

由于所有单元格都应具有相同的大小,因此您也可以使用UniformGrid。正如 Leo Bartkus 建议的那样,您可以使用代码隐藏来生成单元格和文本框。为此,首先在 XAML 中为数独表创建一个占位符:<!-- Placeholder for Sudoku table (filled in code-behind) --><Border x:Name="SudokuTable" />假设您使用的是Window,这是代码隐藏:public partial class MainWindow : Window{&nbsp; &nbsp; private const int InnerWidth = 3;&nbsp; &nbsp; private const int OuterWidth = InnerWidth * InnerWidth;&nbsp; &nbsp; private const int Thin = 1;&nbsp; &nbsp; private const int Thick = 3;&nbsp; &nbsp; public MainWindow()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; InitializeComponent();&nbsp; &nbsp; &nbsp; &nbsp; InitializeViewModel();&nbsp; &nbsp; &nbsp; &nbsp; InitializeSudokuTable();&nbsp; &nbsp; }&nbsp; &nbsp; public SudokuViewModel ViewModel => (SudokuViewModel)DataContext;&nbsp; &nbsp; private void InitializeViewModel()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; DataContext = new SudokuViewModel(OuterWidth);&nbsp; &nbsp; }&nbsp; &nbsp; private void InitializeSudokuTable()&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var grid = new UniformGrid&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Rows = OuterWidth,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Columns = OuterWidth&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; &nbsp; &nbsp; for (var i = 0; i < OuterWidth; i++)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (var j = 0; j < OuterWidth; j++)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var border = CreateBorder(i, j);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; border.Child = CreateTextBox(i, j);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; grid.Children.Add(border);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; SudokuTable.Child = grid;&nbsp; &nbsp; }&nbsp; &nbsp; private static Border CreateBorder(int i, int j)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var left = j % InnerWidth == 0 ? Thick : Thin;&nbsp; &nbsp; &nbsp; &nbsp; var top = i % InnerWidth == 0 ? Thick : Thin;&nbsp; &nbsp; &nbsp; &nbsp; var right = j == OuterWidth - 1 ? Thick : 0;&nbsp; &nbsp; &nbsp; &nbsp; var bottom = i == OuterWidth - 1 ? Thick : 0;&nbsp; &nbsp; &nbsp; &nbsp; return new Border&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BorderThickness = new Thickness(left, top, right, bottom),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BorderBrush = Brushes.Black&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; }&nbsp; &nbsp; private TextBox CreateTextBox(int i, int j)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var textBox = new TextBox&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; VerticalAlignment = VerticalAlignment.Center,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HorizontalAlignment = HorizontalAlignment.Center&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; &nbsp; &nbsp; var binding = new Binding&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Source = ViewModel,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Path = new PropertyPath($"[{i},{j}]"),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Mode = BindingMode.TwoWay&nbsp; &nbsp; &nbsp; &nbsp; };&nbsp; &nbsp; &nbsp; &nbsp; textBox.SetBinding(TextBox.TextProperty, binding);&nbsp; &nbsp; &nbsp; &nbsp; return textBox;&nbsp; &nbsp; }}嵌套循环为 81 个单元创建每个Border和TextBox。边框的粗细是根据当前单元格的位置确定的。这将为您提供典型的数独表外观。文本框数据绑定到视图模型的二维索引器属性。这是视图模型:public class SudokuViewModel : ViewModelBase{&nbsp; &nbsp; private readonly string[,] _values;&nbsp; &nbsp; public SudokuViewModel(int width)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _values = new string[width, width];&nbsp; &nbsp; }&nbsp; &nbsp; public string this[int i, int j]&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; get => _values[i, j];&nbsp; &nbsp; &nbsp; &nbsp; set => Set(ref _values[i, j], value);&nbsp; &nbsp; }}此索引器返回一个字符串,但您可能希望将其更改为整数并进行适当的转换和错误检查。PropertyChanged在任何情况下,它都使用 MVVM Light在索引器属性更新时引发事件。我在这里用我的解决方案创建了一个存储库:https ://github.com/redcurry/Sudoku 。

LEATH

每个人的答案都很好,你的想法让我专注于一个目标:)到目前为止,这就是我所在的位置。XAML:我正在使用详细的 XAML 方法和网格。网格:11x11 -(数独单元格为 9x9 + 边框为 2x2)。<Grid Grid.Row="1" >&nbsp; &nbsp; &nbsp; &nbsp; <Grid.ColumnDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="2px"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="2px"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; </Grid.ColumnDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; <Grid.RowDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="2px"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="2px"/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition/>&nbsp; &nbsp; &nbsp; &nbsp; </Grid.RowDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; <Rectangle Grid.Row="0" Grid.Column="3" Grid.RowSpan="11" Fill="Black"></Rectangle>&nbsp; &nbsp; &nbsp; &nbsp; <Rectangle Grid.Row="0" Grid.Column="7" Grid.RowSpan="11" Fill="Black"></Rectangle>&nbsp; &nbsp; &nbsp; &nbsp; <Rectangle Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="11" Fill="Black"></Rectangle>&nbsp; &nbsp; &nbsp; &nbsp; <Rectangle Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="11" Fill="Black"></Rectangle>&nbsp; &nbsp; &nbsp; &nbsp; <local:CellUserControl Grid.Row="0" Grid.Column="0"&nbsp; DataContext="{Binding Path=Cells[0], Source={StaticResource Locator}}"/>&nbsp; &nbsp; &nbsp; &nbsp; <local:CellUserControl Grid.Row="0" Grid.Column="1"&nbsp; DataContext="{Binding Path=Cells[1], Source={StaticResource Locator}}"/>...我很懒,所以我最终使用 Excel 电子表格来枚举 81 个单元格,并使用 Floor.Math、MOD 和 Concatenate 的组合 :)下一个挑战是将 81 MVVM 属性重构为更微不足道的东西。在 XAML 中:语法为 {Binding Path=Cells[0]} 并选择(暂时)将属性放在 ViewModelLocator 中。&nbsp; &nbsp; public IList<CellViewModel> Cells&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; get&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new List<CellViewModel>(ServiceLocator.Current.GetAllInstances<CellViewModel>());&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }XAML 和代码是干净的。我喜欢它,到目前为止。我仍在处理 IList 的正确位置 - 它应该保留在 ViewModelLocator 中还是应该实际上在 MainViewModel 中?我想,要回答这个问题,我必须做一些单元测试。

MMMHUHU

这是我如何用网格做数独的一个例子......<Grid>&nbsp;<Grid.RowDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="5"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="5"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <RowDefinition Height="*"/>&nbsp; &nbsp; </Grid.RowDefinitions>&nbsp; &nbsp; <Grid.ColumnDefinitions>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="5"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="5"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; &nbsp; &nbsp; <ColumnDefinition Width="*"/>&nbsp; &nbsp; </Grid.ColumnDefinitions>&nbsp; &nbsp; <TextBox x:Name="textBox00" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy1" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="2"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy2" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="4"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy3" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="5"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy4" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="6"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy5" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="2" Grid.Row="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy6" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="4" Grid.Row="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy7" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1" Grid.Column="5" Grid.Row="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy8" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="1" Grid.Row="1"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy9" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="1" Grid.Row="2"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy10" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="2" Grid.Row="2"/>&nbsp; &nbsp; <TextBox x:Name="textBox00_Copy11" TextWrapping="Wrap" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Text="1"&nbsp; Grid.Column="4" Grid.Row="2"/>&nbsp; &nbsp; <Rectangle Grid.Column="3" Fill="#FF663003" Grid.RowSpan="12"/>&nbsp; &nbsp; <Rectangle Grid.Column="7" Fill="#FF663003" Grid.RowSpan="12"/>&nbsp; &nbsp; <Rectangle Grid.Row="3" Fill="#FF663003" Grid.ColumnSpan="12"/>&nbsp; &nbsp; <Rectangle Grid.Row="7" Fill="#FF663003" Grid.ColumnSpan="12"/>这只是默认的文本框样式。您可以使用用户控件使其不同,或者您可以通过在 Blend for Visual Studio 中加载 xaml 来为文本框制作自定义样式,右键单击文本框并选择编辑模板.. -> 编辑副本
打开App,查看更多内容
随时随地看视频慕课网APP