【问题标题】:Making a list for each cell in a datagridview为datagridview中的每个单元格制作一个列表
【发布时间】:2021-02-06 15:39:09
【问题描述】:

我有一个 9x9 DataGridView,每个单元格只能包含 1-9 之间的数字(如数独)。我希望为每个包含所有 9 个数字的单元格提供某种列表。随着游戏的进行,我需要开始从不同单元格的“列表”中删除数字。我能得到一些关于在这种情况下应该使用什么的建议吗?有没有办法避免制作 81 个不同的列表并手动将所有 9 个数字插入所有 81 个列表?

for (int i = 0; i < 9; i++)
{
  for (int j = 0; j < 9; j++)
  {
    string listname;
    listname = "cell" + i.ToString() + j.ToString();
    List<int> listname = new List<int>();
  }
}

我在想这样的事情将有助于制作 81 个不同的列表,因为列表名称将是 cell(rownum)(columnnum) 但这显然行不通。

【问题讨论】:

  • 如何创建一个带有标志属性的枚举,分配一 = 1、二 = 2、三 = 4、四 = 8 .. 九 = 256。然后创建这些枚举的 9x9 数组,初始化每个值都有 511(所有位 1 到 9)。然后要删除一个数字,您只需将其从枚举中删除即可。比 81 个列表容易得多!
  • 对不起,我对枚举不太了解。有没有推荐的网页来了解更多信息? @JonathanWillcock

标签: c# winforms datagridview


【解决方案1】:

为了让您了解我的意思,请为您自己创建一个枚举,如下所示:

[Flags]
public enum SudokuValue
{
    None = 0,
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32,
    Seven = 64,
    Eight = 128,
    Nine = 256
}

Flags 属性很重要,我们稍后会看到。您现在可以在类似以下的类中利用此枚举:

public class SudokuHelper
{
    private SudokuValue[,] gridValues;
    private bool[,] gridFills;

    public SudokuHelper()
    {
        gridValues = new SudokuValue[9, 9];
        gridFills = new bool[9, 9];
        for (int i = 0; i < 9; i++)
        {
            for (int j = 0; j < 9; j++)
            {
                gridValues[i, j] = (SudokuValue)511;
                gridFills[i, j] = false;
            }
        }
    }
    public void UpdateGrid(int row, int col, int val)
    {
        gridFills[row, col] = true;
        SudokuValue sudoku = (SudokuValue)(Math.Pow(2, val - 1));
        gridValues[row, col] = sudoku;
        //Update same col
        for (int i = 0; i < 9; i++)
        {
            if (!gridFills[i, col] &&  gridValues[i, col].HasFlag(sudoku))
            {
                gridValues[i, col] -= sudoku;
            }
        }
        //Update same col
        for (int i = 0; i < 9; i++)
        {
            if (!gridFills[row, i] && gridValues[row, i].HasFlag(sudoku))
            {
                gridValues[row, i] -= sudoku;
            }
        }
        //Update same block
        int startBlockRow;
        int startBlockCol;
        if (row < 3)
        {
            startBlockRow = 0;
        }
        else if (row < 6)
        {
            startBlockRow = 3;
        }
        else
        {
            startBlockRow = 6;
        }
        if (col < 3)
        {
            startBlockCol = 0;
        }
        else if (col < 6)
        {
            startBlockCol = 3;
        }
        else
        {
            startBlockCol = 6;
        }
        for (int i = startBlockRow; i < startBlockRow + 3; i++)
        {
            for (int j = startBlockCol; j < startBlockCol + 3; j++)
            {
                if (!gridFills[i, j] && gridValues[i,j].HasFlag(sudoku))
                {
                    gridValues[i, j] -= sudoku;
                }
            }
        }
    }
    public string GetStringRepresentation(int row, int col)
    {
        var sB = new StringBuilder();
        if (gridValues[row, col].HasFlag(SudokuValue.One))
        {
            sB.Append(",1");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Two))
        {
            sB.Append(",2");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Three))
        {
            sB.Append(",3");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Four))
        {
            sB.Append(",4");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Five))
        {
            sB.Append(",5");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Six))
        {
            sB.Append(",6");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Seven))
        {
            sB.Append(",7");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Eight))
        {
            sB.Append(",8");
        }
        if (gridValues[row, col].HasFlag(SudokuValue.Nine))
        {
            sB.Append(",9");
        }
        //remove leading comma
        return sB.ToString().Substring(1);
    }
}

构造函数创建一个 SudokuValues 的初始二维数组,并用一个初始值填充它,该初始值表示所有可能的值 1 ... 9 (511)。请注意,虽然 511 未在枚举中列出,但我们仍然可以将其转换为 SudokuValue。这是因为默认情况下所有枚举都是整数。它还创建并填充一个二维布尔数组,以表示已为其提供值的那些单元格。

如果在其中一个单元格中输入了值,它会提供一种更新网格中值的方法。此方法采用三个参数,更新单元格的行和列,以及作为整数输入到单元格中的值(必须是 1 .. 9)。首先,它更新网格中的单元格本身(有效地消除所有其他可能性),并设置布尔标志。然后,它继续删除在同一列、同一行和同一 3x3 块中的其他单元格中可能输入的值。在这里,它在删除值之前检查之前的值是否仍然存在(使用 HasFlags 方法 - 可用,因为我们在上面设置了 Flags 属性),以避免单元格被输入两次。这里有一个小技巧:输入的值 (1 .. 9) 可以通过将其减 1 然后取 2 的结果幂来转换为其等效的枚举。

最后,帮助器提供任何给定单元格包含的内容的文本表示(如果设置了单个值,或者剩余的可能性)。

要对此进行测试,我们可以这样做(我使用 WinForms 应用程序进行测试,因此使用 MessageBox.Show()):

var sH = new SudokuHelper();
for (int i = 0; i < 9; i++)
{
    var sB = new StringBuilder();
    for (int j = 0; j < 9; j++)
    {
        sB.Append(" ; " + sH.GetStringRepresentation(i, j));
    }
    MessageBox.Show($"Row {i}: " + sB.ToString().Substring(3));
}

sH.UpdateGrid(4, 7, 9);
for (int i = 0; i < 9; i++)
{
    var sB = new StringBuilder();
    for (int j = 0; j < 9; j++)
    {
        sB.Append(" ; " + sH.GetStringRepresentation(i, j));
    }
    MessageBox.Show($"Row {i}: " + sB.ToString().Substring(3));
}

如果是我构建这个应用程序,我会使用一个 9x9 的 TextBoxes 网格(而不是 DataGridView),全部带有一个 KeyDown/KeyPress 事件,如果其中一个键 1 .. 调用 SudokuHelper 来更新网格9 被按下。但也许您出于其他原因需要 DataGridView?

【讨论】:

  • 非常感谢您的帮助!!是的,我需要使用 datagridview,因为涉及到 Microsoft 访问。这种方法似乎比我尝试做的要容易得多
【解决方案2】:

使您的列成为 DataGridViewComboBoxColumn。这样,这些列中的每个单元格都将是一个 DataGridViewcomboBoxCell。

属性 DataGridViewComboBoxColumn.DataSource 和 DataGridViewComboBoxCell.DataSource 包含所有可能的值。最初将您的列 DataSource 设置为所有初始值,创建单元格后,您可以限制 Cells 数据源中的值。

class SudokuRow
{
    public int C0 {get; set;}
    public int C1 {get; set;}
    public int C2 {get; set;}

    public int C3 {get; set;}
    public int C4 {get; set;}
    public int C5 {get; set;}

    public int C6 {get; set;}
    public int C7 {get; set;}
    public int C8 {get; set;}
}

使用 Visual Studio Designer 在虚拟项目中添加一些列,以查看哪些列 属性应该填写

DataGridView dgvSudoku = new DataGridView()
for (int i=0; i<9; ++i)
{
    DataGridViewComboBoxColumn column = new DataGridViewComboBoxColumn();
    column.Name = "column" + i.ToString();
    column.DataPropertyName = "C" + i.ToString();
    ...
    column.DataSource = enumerable.Range(1,9).ToList();  // allowed values
    dgvSudoko.Columns.Add(column);
}

如果要加空格,那么不要加数字,而是加字符0..9和空格。 sudokuRow 包含 char 而不是 int。

如果您使用 DataBinding 来显示项目,并且只希望它单向显示,则可以使用 List 分配给 DataSource。但是,如果您想读取操作员输入,则必须将数据放入 BindingList。

private BindingList<SudokuRow> SudokuData
{
    get => (BindingList<SudokuRow>)this.dgvSudoku.DataSource;
    set => this.dgvSudokuy.DataSource = value;
}

现在初始化你的datagridview:

List<SudokuData> initialSudokuData = ...
this.SudokuData = initialSudokuData;

在一些操作员输入之后,您想限制一个单元格的可能性:

LimitPossibilites(DataGridViewComboBoxCell cell, IEnumerable<int> allowedValues)
{
    // TODO: what to do if current value is not allowed?

    // limit the combobox values
    cell.DataSource = new BindingList<int>(allowedValues.ToList());
}

或者,如果您不想用新的 BindingList 替换 BindingList,请考虑使用 Bindling&lt;T&gt; 属性添加/删除项目。然而,这似乎是更多的工作。 }

【讨论】:

    猜你喜欢
    • 2020-12-10
    • 2019-03-16
    • 1970-01-01
    • 1970-01-01
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多