WPF中的数独-我应该在表格中使用哪些基本元素?

时间:2019-01-05 19:17:54

标签: c# wpf data-binding mvvm-light

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

现在,我想继续前进到一个新的水平,并使其美观实用。

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

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

  • 我尝试了Grid和Table,但通常最终碰到了死胡同。 XAML变得非常冗长和重复。
  • 我记得MS仅通过应用样式就显示了WPF的强大功能,其中ListBox显示为轮播。这是有效路径吗?
  • 自定义控制:是过度杀伤还是针对这种情况存在?

顺便说一下,我将充分利用MVVM-Light和数据绑定。

3 个答案:

答案 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个单元格分别创建BorderTextBox。边框的粗细是根据当前单元格的位置确定的。这将为您提供典型的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中?我想,要回答这个问题,我必须做一些单元测试。

非常感谢。