首页 > 解决方案 > 两个年龄范围之间的列上的 Datagrid 过滤器

问题描述

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

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#datagridviewfiltering

解决方案


我认为您无法使用“LIKE”比较器执行此操作,因为您要查找的值是“数字”。要获得您正在寻找的过滤器,您将需要一个带有“>=”和“<=”的过滤器来查看目标年龄是否在该范围内。不清楚最初是如何接收数据的,如果每行中的“年龄范围”是如图所示的字符串,那么我建议几种不同的 hacky 方式。此外,尚不清楚网格中还有哪些其他列。

一种“hacky”方法是创建一个方法,该方法只返回一个新DataTable的,其中只有落入给定目标范围内的行。为了帮助完成这项工作,第二种方法采用int(我们正在寻找的目标值)和DataRowViewAgeRange我们正在比较“目标”值)。此“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单击事件将所有这些放在一起以指示何时过滤网格可能如下所示

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();
}

哈奇方法 2

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

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

这可以在“设计器”中完成,也可以在代码中手动完成。无需过多详细说明,请记住,如果您将 aDataTable作为数据源分配给网格并将 gridsAutoGenerateColumns属性设置为false... THEN 只有名称与其中一个列名DataPropertyName“匹配”的网格列DataTable… 将显示。在这种情况下,我们只希望显示AgeRange带有“XX-XX”字符串的列,其他两个新列可以对用户保持隐藏。手动设置网格列可能如下所示,但是您可以在设计器中执行此操作。注意:设计器不显示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。这个方法是原始的DataTableDataTable使用三 (3) 列、AgeRange-string(显示)、StartRange-int 和 EndRange-int 创建一个新的。将不显示开始列和结束列。一旦构建了这个新表,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;
}

推荐阅读