为数据网格视图中的每个单元格制作一个列表
Making a list for each cell in a datagridview
我有一个 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) 但这显然行不通。
为了让您了解我的意思,请自己创建一个枚举,如下所示:
[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 属性很重要,我们稍后会看到。您现在可以在 class 中利用此枚举,如下所示:
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 的文本框网格(而不是 DataGridView),所有文本框都带有一个 KeyDown/KeyPress 事件,如果其中一个事件调用 SudokuHelper 来更新网格按键 1 .. 9 被按下。但也许您出于其他原因需要 DataGridView?
将列设为 DataGridViewComboBoxColumn。这样,这些列中的每个单元格都将是 DataGridViewcomboBoxCell。
属性 DataGridViewComboBoxColumn.DataSource 和 DataGridViewComboBoxCell.DataSource 包含所有可能的值。最初将列数据源设置为所有初始值,在创建单元格后,您可以限制单元格数据源中的值。
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设计器在虚拟项目中添加一些列以查看哪些列
应填写属性
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;
}
现在初始化您的数据网格视图:
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<T>
属性添加/删除项目。但是,这似乎需要更多工作。
}
我有一个 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) 但这显然行不通。
为了让您了解我的意思,请自己创建一个枚举,如下所示:
[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 属性很重要,我们稍后会看到。您现在可以在 class 中利用此枚举,如下所示:
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 的文本框网格(而不是 DataGridView),所有文本框都带有一个 KeyDown/KeyPress 事件,如果其中一个事件调用 SudokuHelper 来更新网格按键 1 .. 9 被按下。但也许您出于其他原因需要 DataGridView?
将列设为 DataGridViewComboBoxColumn。这样,这些列中的每个单元格都将是 DataGridViewcomboBoxCell。
属性 DataGridViewComboBoxColumn.DataSource 和 DataGridViewComboBoxCell.DataSource 包含所有可能的值。最初将列数据源设置为所有初始值,在创建单元格后,您可以限制单元格数据源中的值。
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设计器在虚拟项目中添加一些列以查看哪些列 应填写属性
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;
}
现在初始化您的数据网格视图:
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<T>
属性添加/删除项目。但是,这似乎需要更多工作。
}