【问题标题】:C# Winforms filter datagridview from dynamicly created controlsC# Winforms 从动态创建的控件中过滤 datagridview
【发布时间】:2020-04-25 16:50:48
【问题描述】:

我创建基础Form 类,在其中创建默认样式、默认面板、datagridview 和工具栏。 当我创建新的特定表单时,我扩展了该基本抽象类,并且新类自动具有工具栏、datagridview 和过滤器。在新表单中,我只传递了 datagridview 数据源,并且我的所有表单都有完整的结果。我不会浪费时间来创建新面板、新工具栏、新样式等......

所以所有的工作问题都从过滤网格开始:

当我创建网格时,我还创建了用于根据网格列过滤网格中的数据的控件。 所以我得到了列集合,并基于我在侧边栏控件和该列的标签中生成的网格列类型。

这是我如何根据网格列生成控件的代码:

   public void InitializeDefaultDataGridViewFilter()
    {
        int i = 0;

        foreach (DataGridViewColumn col in _dataGridView.Columns)
        {
            i++;

           // RowStyle temp = _tableLayout.RowStyles[_tableLayout.RowCount - 1];
            _tableLayout.RowCount++;
            _tableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));

            _tableLayout.Controls.Add(new Label() { Text = col.HeaderText, TextAlign = System.Drawing.ContentAlignment.MiddleRight }, 0, _tableLayout.RowCount - 1);

            if (col is DataGridViewTextBoxColumn)
            {
                TextBox textBox = new TextBox()
                {
                    Dock = DockStyle.Fill,
                    Name = col.Name
                };

                _tableLayout.Controls.Add(textBox, 1, _tableLayout.RowCount - 1);
            }

            else if (col is DataGridViewCheckBoxColumn)
            {
                CheckBox checkBox = new CheckBox()
                {
                    Name = col.Name,
                    CheckState = CheckState.Checked
                };
                _tableLayout.Controls.Add(checkBox, 1, _tableLayout.RowCount - 1);
            }
            else
            {

            }
        }
    }

我还创建了Button,并将其放在过滤器下方并创建事件

private void filterButton_Click(object sender, EventArgs e)
{
   // here now i need to filter
}

现在有一个问题:现在如何知道哪些控件已填充? ID、NAME、PIB...等? 如何确定哪些控件有值,哪些控件值用于过滤网格。

我知道如何过滤网格,但我不知道如何确定哪些动态控件具有值以及该控件用于过滤网格中的特定列。

如果ID 的动态文本框具有值1232 该列的过滤网格。

【问题讨论】:

  • 问题 1 - 您正在处理 DGV 列。尝试使用DataTableDataView。问题-2。如果您想控制控件,请将它们添加到集合中,然后您可以对其进行迭代并检查它们的状态
  • 将控件实例保存在字典中是理想的,此外,您可以通过 foreach(Control filterControl in _tableLayout.Controls) 迭代控件并忽略标签,您可以查看它是否为文本框,如果不为空,然后可以获得它的名称,因为它等于列名并在该列上执行过滤器。 (显然,复选框的值检查应该不同)
  • 所以我需要用列名创建DataTableDataTable 来推入dvg 列?而当我想生成过滤器控件时,我需要将它们添加到Dictionary 和该文件中以在点击事件中使用来过滤数据?

标签: c# .net winforms datagridview


【解决方案1】:

正如其他人评论的那样,从网格使用的数据源而不是网格本身获取列“类型”可能是一种更好的方法,并且还可以“收集”用于“过滤”网格的控件.

可以遍历_tableLayout.Controls 并收集您需要过滤的控件,但是,每次用户“过滤”数据时“遍历所有控件”似乎是不必要的,因为控件不会更改每次过滤操作。因此建议Control“收藏”以避免这种不必要的工作。

此外,关于“如何”“过滤”网格的问题并不十分清楚。例如,图片显示了左侧的每一列,用户可以在其中输入文本以根据文本框中输入的值“过滤”网格。我假设如果用户在两 (2) 个或更多文本框中输入文本,那么过滤器将使用“AND”表达式来过滤用户输入的所有值。

这个“AND”、“OR”类型的过滤器不清楚,另外,Boolean“checkbox”控件上的“过滤”可能会带来问题,可能会让用户感到困惑,而且肯定会需要你做更多的工作。例如,对于文本框,用户可以直接通过“清除”文本框中的文本并再次单击“过滤器”按钮来将过滤器“重置”为“无”。

但是,复选框并非如此。用户不能“清除”“checked\unchecked”值。一个复选框要么被“选中”,要么没有,即使你为复选框设置了一些“三”状态,用户如何“直观地”看到这个“清除”的复选框?

鉴于此,当“最初”加载数据并且“之前”应用任何“过滤器”时,将向用户显示选中和未选中的“状态”值。但是,在应用 FIRST 过滤器后,如果没有某种类型的“信号”表明他们想要“忽略”“状态”值,用户将永远无法再次“看到”选中和未选中的“状态”值。在下面的示例中,添加了一个按钮以将网格“重置”回其原始状态。

下面的示例以两个全局变量开头:GridTableAllControls...

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&lt;Control, Type&gt; 的方法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&lt;Control, Type&gt; 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);
  }
}

希望这有意义并有所帮助。

【讨论】:

  • 呵呵大答,给我时间测试分析
  • 感谢您的宝贵时间。这项工作,你让我知道如何解决我的问题。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-17
  • 2020-10-23
  • 2011-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多