为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕的地6264312 2022-07-10 10:27:39
我用 C#、XAML 和 MVVM-Light构建了一个非常基本的数独应用程序。MainWindow 有一个 9x9 的网格和每个单元格的 81 个文本框,而 MainViewModel 有 81 个 int 属性。不漂亮。数独表看起来不像经典的表,没有“更粗的边框”来区分每个象限。现在,我想更上一层楼,让它变得漂亮实用。每个单元格内容都必须是“响应式”的?即根据窗口的宽度/高度调整大小(字体大小)。我已经开始研究处理字体颜色、背景颜色等的 CellUserControl。主窗口的基础应该是什么?网格、表格、列表框、自定义?我尝试了 Grid 和 Table,但通常最终会遇到死胡同。XAML 变得非常冗长和重复。我记得 MS 展示了 WPF 的强大功能,ListBox 显示为轮播,只需应用样式即可。这是一条有效的路径吗?自定义控件:它是矫枉过正还是完全适合这种情况?顺便说一句,我将大量使用 MVVM-Light 和数据绑定。
查看完整描述

3 回答

?
拉风的咖菲猫

TA贡献1995条经验 获得超2个赞

由于所有单元格都应具有相同的大小,因此您也可以使用UniformGrid。正如 Leo Bartkus 建议的那样,您可以使用代码隐藏来生成单元格和文本框。为此,首先在 XAML 中为数独表创建一个占位符:


<!-- 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。边框的粗细是根据当前单元格的位置确定的。这将为您提供典型的数独表外观。


文本框数据绑定到视图模型的二维索引器属性。这是视图模型:


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);

    }

}

此索引器返回一个字符串,但您可能希望将其更改为整数并进行适当的转换和错误检查。PropertyChanged在任何情况下,它都使用 MVVM Light在索引器属性更新时引发事件。


我在这里用我的解决方案创建了一个存储库:https ://github.com/redcurry/Sudoku 。


查看完整回答
反对 回复 2022-07-10
?
LEATH

TA贡献1936条经验 获得超6个赞

每个人的答案都很好,你的想法让我专注于一个目标:)


到目前为止,这就是我所在的位置。


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 中?我想,要回答这个问题,我必须做一些单元测试。


查看完整回答
反对 回复 2022-07-10
?
MMMHUHU

TA贡献1834条经验 获得超8个赞

这是我如何用网格做数独的一个例子......


<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 来为文本框制作自定义样式,右键单击文本框并选择编辑模板.. -> 编辑副本


查看完整回答
反对 回复 2022-07-10
  • 3 回答
  • 0 关注
  • 117 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号