正如其他人评论的那样,从网格使用的数据源而不是网格本身获取列“类型”可能是一种更好的方法,并且还可以“收集”用于“过滤”网格的控件.
可以遍历_tableLayout.Controls 并收集您需要过滤的控件,但是,每次用户“过滤”数据时“遍历所有控件”似乎是不必要的,因为控件不会更改每次过滤操作。因此建议Control“收藏”以避免这种不必要的工作。
此外,关于“如何”“过滤”网格的问题并不十分清楚。例如,图片显示了左侧的每一列,用户可以在其中输入文本以根据文本框中输入的值“过滤”网格。我假设如果用户在两 (2) 个或更多文本框中输入文本,那么过滤器将使用“AND”表达式来过滤用户输入的所有值。
这个“AND”、“OR”类型的过滤器不清楚,另外,Boolean“checkbox”控件上的“过滤”可能会带来问题,可能会让用户感到困惑,而且肯定会需要你做更多的工作。例如,对于文本框,用户可以直接通过“清除”文本框中的文本并再次单击“过滤器”按钮来将过滤器“重置”为“无”。
但是,复选框并非如此。用户不能“清除”“checked\unchecked”值。一个复选框要么被“选中”,要么没有,即使你为复选框设置了一些“三”状态,用户如何“直观地”看到这个“清除”的复选框?
鉴于此,当“最初”加载数据并且“之前”应用任何“过滤器”时,将向用户显示选中和未选中的“状态”值。但是,在应用 FIRST 过滤器后,如果没有某种类型的“信号”表明他们想要“忽略”“状态”值,用户将永远无法再次“看到”选中和未选中的“状态”值。在下面的示例中,添加了一个按钮以将网格“重置”回其原始状态。
下面的示例以两个全局变量开头:GridTable 和 AllControls...
private DataTable GridTable;
private Dictionary<Control, Type> AllControls;
暴露grids数据源GridTable是保留一份原始数据的副本,也用于设置过滤器的时候。每次应用新过滤器时,代码都会使用此表来初始设置“过滤器”。此外,Dictionary AllControls 使用此表填充。
AllControlsDictionary 与控件的“类型”值一起用作过滤器控件的“集合”。请记住,具有此“类型”值并不是绝对必要的,因为代码总是使用相同的“AND”和 EQUALS(“=”)过滤器表达式,但是,如果您想过滤“部分或包含”使用“LIKE”关键字的字符串值,那么,我们需要区分“类型”以确保我们不在“数字”列上使用“LIKE”关键字,因为它会崩溃。这同样适用于复选框列。
由于字典会跟踪值的“类型”,因此可以方便地使用该值来确定要使用的控件类型,即strings 的文本框和数字以及booleans 的复选框.
在此示例中,最初,网格使用DataTable 作为DataSource。应用过滤器时,会从原始数据表中创建一个DataView,然后将一个过滤器应用于此DataView,并将其用作网格的DataSource。
一旦我们有了网格DataTable,我们就可以使用它来创建AllControls Dictionary。这里循环遍历表的列以获取列名以及列的“类型”。对于string 和数字类型列,我们将TextBox 及其值类型(字符串/整数)添加到字典中。对于布尔列,我们将CheckBox 添加到字典中。因此,采用GridTable 并返回Dictionary<Control, Type> 的方法GetControlsDictionary 可能会派上用场,如下所示……
private Dictionary<Control, Type> GetControlsDictionary(DataTable dt) {
Dictionary<Control, Type> controls = new Dictionary<Control, Type>();
Control theControl;
Type valueType;
foreach (DataColumn col in dt.Columns) {
valueType = col.DataType;
switch (valueType.ToString()) {
case "System.Int32":
case "System.String":
theControl = new TextBox() {
Dock = DockStyle.Fill,
Name = col.ColumnName
};
break;
case "System.Boolean":
theControl = new CheckBox() {
Name = col.ColumnName,
CheckState = CheckState.Unchecked
};
break;
default:
theControl = new TextBox() {
Dock = DockStyle.Fill,
Name = col.ColumnName
};
break;
}
controls.Add(theControl, valueType);
}
return controls;
}
使用AllControls 字典,我们可以创建一个方法SetFilterControls 来获取字典并设置过滤器文本框等。下面的代码与原始代码一样使用TableLayoutPanel 控件,它可能看起来像……。
public void SetFilterControls(Dictionary<Control, Type> controls) {
int rowIndex = 1;
foreach (KeyValuePair<Control, Type> controlItem in controls) {
_tableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
_tableLayout.Controls.Add(new Label() { Text = controlItem.Key.Name, TextAlign = ContentAlignment.MiddleRight }, 0, rowIndex);
_tableLayout.Controls.Add(controlItem.Key, 1, rowIndex++);
}
}
当我们想对网格应用过滤器时,AllControls 字典也会派上用场。构建过滤器字符串需要我们遍历字典,检查每个控件的值,然后将该值添加到过滤器字符串。例如,如果用户在“ID”过滤器框中输入“5”,然后单击过滤器按钮,过滤器字符串将类似于……“[ID] = ‘5’”。如果多个过滤器文本框有文本,那么这将创建一个复合“AND”过滤器字符串。例如,如果用户在“ID”文本框中输入“5”并在“PIB”文本框中输入“123”,那么过滤器字符串将如下所示……“[ID] = '5' AND [PIB] ='123'”。如果用户在与数字列匹配的文本框中键入非数字字符,则返回空字符串。此外,如果任何文本框为空,则返回一个空字符串。这个循环遍历字典的GetFilterString() 方法可能看起来像……
private string GetFilterString() {
StringBuilder sb = new StringBuilder();
string curFilter = "";
List<string> filters = new List<string>();
foreach (KeyValuePair<Control, Type> item in AllControls) {
curFilter = GetItemFilterString(item);
if (!String.IsNullOrEmpty(curFilter)) {
filters.Add(curFilter);
}
}
for (int i = 0; i < filters.Count; i++) {
sb.Append(filters[i]);
if (i < filters.Count - 1) {
sb.Append(" AND ");
}
}
return sb.ToString();
}
接下来是上面使用的GetItemFilterString(KeyValuePair<Control, Type> item) 方法,以获取每个单独控件的过滤器字符串。需要注意的是,这里可能需要更多的错误检查。
private string GetItemFilterString(KeyValuePair<Control, Type> item) {
switch (item.Value.ToString()) {
case "System.Int32":
// if the text is NOT a valid int... then an empty string is returned
if (String.IsNullOrEmpty(item.Key.Text) || (!int.TryParse(item.Key.Text, out int value))) {
return "";
}
return "[" + item.Key.Name + "] = '" + item.Key.Text + "'";
case "System.String":
if (String.IsNullOrEmpty(item.Key.Text)) {
return "";
}
return "[" + item.Key.Name + "] = '" + item.Key.Text + "'";
case "System.Boolean":
if (((CheckBox)item.Key).Checked) {
return "[" + item.Key.Name + "] = True";
}
return "[" + item.Key.Name + "] = False";
default:
return "";
}
}
接下来,过滤器按钮点击事件以使用上述方法并过滤网格……
private void button1_Click(object sender, EventArgs e) {
DataView dv = new DataView(GridTable);
dv.RowFilter = GetFilterString();
dataGridView1.DataSource = dv;
}
如前所述,我添加了另一个按钮以将数据重置为未过滤状态。
private void button2_Click(object sender, EventArgs e) {
dataGridView1.DataSource = GridTable;
}
将所有这些放在一起可能看起来像……。
private void Form1_Load(object sender, EventArgs e) {
GridTable = GetDT();
dataGridView1.DataSource = GridTable;
AllControls = GetControlsDictionary(GridTable);
SetFilterControls(AllControls);
}
private DataTable GetDT() {
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("PIB", typeof(int));
dt.Columns.Add("Registration Number", typeof(int));
dt.Columns.Add("Status", typeof(bool));
FillTable(dt);
return dt;
}
private void FillTable(DataTable dt) {
Random rand = new Random();
int pib;
int reg;
bool stat;
for (int i = 1; i < 21; i++) {
pib = rand.Next(0, 20);
reg = rand.Next(0, 3);
stat = rand.Next(2) == 1;
dt.Rows.Add(i, "Name_" + i, pib, reg, stat);
}
}
希望这有意义并有所帮助。