【问题标题】:Datagrid filter on column that between two age range两个年龄范围之间的列上的 Datagrid 过滤器
【发布时间】:2019-06-18 19:11:27
【问题描述】:

我有一个数据网格视图,其中有一列类型为字符串,显示年龄范围的值,例如:

0-18
19-100
0-100

我还有一个过滤器文本框,需要根据年龄范围进行过滤

(dgv1.DataSource as DataTable).DefaultView.RowFilter = 
string.Format("AgeRange LIKE '%{0}' OR AgeRange LIKE '{0}%'", textBoxFilter.Text);

问题是如果用户输入像 18 这样的数字,网格不会返回 0-100 的行

如何让数据网格同时返回 0-18 和 0-100?

【问题讨论】:

    标签: c# datagridview filtering


    【解决方案1】:

    我认为您无法使用“LIKE”比较器执行此操作,因为您要查找的值是“数字”。要获得您正在寻找的过滤器,您需要一个带有“>=”和“

    一种“hacky”方法是创建一个返回新DataTable 的方法,其中仅包含落入给定目标范围内的行。为了帮助完成这项工作,第二种方法采用int(我们正在寻找的目标值)和DataRowView(我们正在比较“目标”值的AgeRange)。此“AgeRange”将位于行的第一列中。这里我们简单地取那个字符串范围(“0-18”)和目标值(“18”)来看看这个目标值是否在这个范围内,然后根据结果返回真或假。这可以使用string.split 方法拆分“AgeRange”字符串并使用int.TryParse 将字符串转换为数字来完成。下面是一个例子。

    private bool TargetIsInRange(int target, DataRowView row) {
      if (row.Row.ItemArray[0] != null) {
        string cellValue = row.Row.ItemArray[0].ToString();
        string[] splitArray = cellValue.Split('-');
        int startValue;
        int endValue;
        if (!int.TryParse(splitArray[0], out startValue)) {
          return false;
        }
        if (!int.TryParse(splitArray[1], out endValue)) {
          return false;
        }
        if (target >= startValue && target <= endValue)
          return true;
       }
      return false;
    }  
    

    在遍历网格行以确定哪些行进入新过滤器DataTable 时,上述方法应该会派上用场。接下来,一个方法循环遍历网格并返回过滤的DataTable。对于网格中的每一行,我们可以调用上述方法并添加返回true 的行。

    private DataTable GetFilterTable() {
      DataTable filterTable = ((DataTable)dgv1.DataSource).Clone();
      dgv1.DataSource = gridTable;
      int targetValue = -1;
      if (int.TryParse(textBox1.Text, out targetValue)) {
        foreach (DataGridViewRow row in dgv1.Rows) {
          DataRowView dataRow = (DataRowView)row.DataBoundItem;
          if (dataRow != null) {
            if (TargetIsInRange(targetValue, dataRow)) {
              filterTable.Rows.Add(dataRow.Row.ItemArray[0]);
            }
          }
        }
      }
      return filterTable;
    }
    

    不清楚您在哪里调用此过滤器,如果您正在过滤“字符串”,那么当用户在文本框中键入过滤器字符串时,网格将过滤用户按下的每个字符。这对字符串很好,但是,在这种情况下使用“数字”,我猜按钮会更合适。我想这是你必须决定的事情。使用Button click 事件将所有这些放在一起以指示何时过滤网格可能如下所示

    private DataTable gridTable;
    
    public Form1() {
      InitializeComponent();
    }
    
    private void Form1_Load(object sender, EventArgs e) {
      gridTable = GetTable();
      FillTable(gridTable);
      dgv1.DataSource = gridTable;
      textBox1.Text = "18";
    }
    
    private void FillTable(DataTable dt) {
      dt.Rows.Add("0-18");
      dt.Rows.Add("19-100");
      dt.Rows.Add("0-100");
      dt.Rows.Add("17-80");
      dt.Rows.Add("18-80");
    }
    
    private DataTable GetTable() {
      DataTable dt = new DataTable();
      dt.Columns.Add("AgeRange", typeof(string));
      return dt;
    }
    
    private void button1_Click(object sender, EventArgs e) {
      if (textBox1.Text == "") {
        dgv1.DataSource = gridTable;
        return;
      }
      dgv1.DataSource = GetFilterTable();
    }
    

    Hacky 方法 2

    第一种方法有效;但是,我猜如果有大量数据和大量过滤,这可能会成为性能问题。因此,在这种方法中,一开始就采取了额外的步骤来利用DataTables RowFilter 功能,正如发布的代码所做的那样。显然如前所述,我们不会使用“LIKE”比较器,而是使用“=”运算符。

    为了实现这一点,我们必须以某种方式将给定的string 范围“XX-XX”转换为两 (2) 个ints。然后将这些整数“添加”到DataTable。然后使用RowFilter 属性和小于和大于运算符过滤表将很容易。一个问题是我们需要“额外”的工作来正确设置网格的列,否则这额外的两列数据也会显示出来。

    这可以在“设计器”中完成,也可以在代码中手动完成。无需过多详细说明,请记住,如果您将 DataTable 作为数据源分配给网格,并且将网格 AutoGenerateColumns 属性设置为 false... 那么只有带有 @ 的网格列将显示与DataTable 列名之一“匹配”的 987654343@ 名称。在这种情况下,我们只希望显示带有“XX-XX”字符串的AgeRange 列,其他两个新列可以对用户保持隐藏。手动设置网格列可能如下所示,但是您可以在设计器中执行此操作。注意:设计器不会显示AutoGenerateColumns 属性,您必须在代码中执行此操作。

    private void AddGridColumn() {
      DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
      col.Name = "AgeRange";
      col.DataPropertyName = "AgeRange";
      col.HeaderText = "Age Range";
      dgv1.Columns.Add(col);
    }
    

    重要的一点是DataPropertyName 必须与DataTable 中的目标列名匹配,否则该列将不会显示。

    接下来是构建新的DataTable。这个方法被赋予了原始的DataTable。使用三 (3) 列、AgeRange-string(显示)、StartRange-int 和 EndRange-int 创建新的 DataTable。将不显示开始列和结束列。一旦构建了这个新表,就会开始一个foreach 循环,遍历原始表中的所有行。来自原始表格行的字符串数字被“解析”为实际数字,并与原始“范围”字符串一起添加到新的DataTable。此方法可能如下所示。下面还有一个帮助方法来帮助拆分年龄范围字符串并返回一个数字。

    private DataTable GetSplitTable(DataTable sourceTable) {
      DataTable dt = new DataTable();
      dt.Columns.Add("AgeRange", typeof(string));
      dt.Columns.Add("StartRange", typeof(int));
      dt.Columns.Add("EndRange", typeof(int));
      foreach (DataRow row in sourceTable.Rows) {
        int startValue = GetIntValue(row.ItemArray[0].ToString(), 0);
        int endValue = GetIntValue(row.ItemArray[0].ToString(), 1);
        dt.Rows.Add(row.ItemArray[0], startValue, endValue);
      }
      return dt;
    }
    
    private int GetIntValue(string rangeString, int index) {
      string[] splitArray = rangeString.Split('-');
      int value = 0;
      int.TryParse(splitArray[index], out value);
      return value;
    }
    

    将所有这些放在一起可能如下所示。请注意,按钮单击事件会检查文本框是否为空,如果是,则如果应用了当前过滤器,则会删除当前过滤器。

    private DataTable gridTable;
    private DataTable splitTable;
    
    public Form1() {
      InitializeComponent();
    }
    
    private void Form1_Load(object sender, EventArgs e) {
      gridTable = GetTable();
      FillTable(gridTable);
      splitTable = GetSplitTable(gridTable);
      AddGridColumn();
      dgv1.AutoGenerateColumns = false;
      dgv1.DataSource = splitTable;
      textBox1.Text = "18";
    }
    
    private void FillTable(DataTable dt) {
      dt.Rows.Add("0-18");
      dt.Rows.Add("19-100");
      dt.Rows.Add("0-100");
      dt.Rows.Add("17-80");
      dt.Rows.Add("15-75");
    }
    
    private DataTable GetTable() {
      DataTable dt = new DataTable();
      dt.Columns.Add("AgeRange", typeof(string));
      return dt;
    }
    
    private void AddGridColumn() {
      DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
      col.Name = "AgeRange";
      col.DataPropertyName = "AgeRange";
      col.HeaderText = "Age Range";
      dgv1.Columns.Add(col);
    }
    
    private void button1_Click(object sender, EventArgs e) {
      string filterString = "";
      DataView dv;
      if (textBox1.Text != "") {
        filterString = string.Format("StartRange <= {0} AND EndRange >= {0}", textBox1.Text);
      }
      dv = new DataView(splitTable);
      dv.RowFilter = filterString;
      dgv1.DataSource = dv;
    }
    

    【讨论】:

    • 谢谢!解决方案 1 对我来说非常好用,因为我不使用庞大的数据集。
    【解决方案2】:

    此代码:

    ("AgeRange LIKE '%{0}' OR AgeRange LIKE '{0}%'", textBoxFilter.Text)
    

    是冗余的,有两个AgeRange LIKE

    如果你想像textBoxFilter.Text一样搜索你可以试试

    ("AgeRange LIKE '%{0}%'", textBoxFilter.Text)
    

    或者

    StringBuilder rowFilter = new StringBuilder();
    rowFilter.Append("AgeRange  Like '%" + textBoxFilter.Text + "%'");
    (dgv1.DataSource as DataTable).DefaultView.RowFilter = rowFilter.ToString();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-22
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-02
      • 1970-01-01
      相关资源
      最近更新 更多