【问题标题】:Filter WPF DataGrid (DataTable) based on values from several TextBoxes MVVM根据来自多个 TextBoxes MVVM 的值过滤 WPF DataGrid (DataTable)
【发布时间】:2020-09-08 06:59:04
【问题描述】:

我一直在尝试设置基于 4 个文本框的过滤器。如果第一个文本框不为空 -> 然后根据它进行过滤,如果第一个和第二个文本框不为空,则从这两个文本框等中组合过滤器。我希望我的过滤器能够像这样工作http://www.tablefilter.com/auto-filter.html

如您所见,我尝试了几种变体,但我不断收到下面提供的错误。有什么建议可以让它工作吗?

这是我的代码:

    public void EnableRowFiltering()
    {
        StringBuilder sb = new StringBuilder();

        if (this.YRNROSearchKey != string.Empty)
        {
            sb.Append($"YRNRO LIKE '%{this.YRNROSearchKey}%' AND ");
        }
        if (this.HAKUNIMISearchKey != string.Empty)
        {
            sb.Append($"HAKUNIMI LIKE '%{this.HAKUNIMISearchKey}%' AND ");
        }
        if (this.GROUPSearchKey != string.Empty)
        {
            sb.Append($"KONSERNI LIKE '%{this.GROUPSearchKey}%' AND ");
        }
        if (this.BUSINESSIDSearchKey != string.Empty)
        {
            sb.Append($"LY LIKE '%{this.BUSINESSIDSearchKey}%' AND ");
        }

        // I have tried also this way without success 
        // this.MainDataTable.DefaultView.RowFilter = YRNRO + HAKUNIMI + GROUP + BUSINESSID;
        string YRNRO = string.IsNullOrEmpty(this.YRNROSearchKey) ? "" : $"YRNRO LIKE '{this.YRNROSearchKey}*'";
        string HAKUNIMI = string.IsNullOrEmpty(this.HAKUNIMISearchKey) ? "" : $" AND HAKUNIMI LIKE '{this.HAKUNIMISearchKey}*'";
        string GROUP = string.IsNullOrEmpty(this.GROUPSearchKey) ? "" : $" AND KONSERNI LIKE '{this.GROUPSearchKey}*'";
        string BUSINESSID = string.IsNullOrEmpty(this.BUSINESSIDSearchKey) ? "" : $" AND LY LIKE '{this.BUSINESSIDSearchKey}*'";

        this.MainDataTable.DefaultView.RowFilter = sb.ToString();
    }

System.Data.SyntaxErrorException: '语法错误: 后面缺少操作数 '和'运算符。'

这一直有效,但我必须提供所有值(填写所有文本框)才能过滤:

  public void EnableRowFiltering()
  {
    this.MainDataTable.DefaultView.RowFilter = 
      $"YRNRO LIKE '{this.YRNROSearchKey}*' " + 
      $"OR HAKUNIMI LIKE '{this.HAKUNIMISearchKey}*'" +
      $"OR KONSERNI LIKE '{this.GROUPSearchKey}*'" +
      $"OR LY LIKE '{this.BUSINESSIDSearchKey}*'";
  }

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    最后一个解决方案是迄今为止最短的,所以我更喜欢它。
    您只需将 OR 替换为 AND(我的错)。

    public void EnableRowFiltering()
    {
      this.MainDataTable.DefaultView.RowFilter = 
        $"YRNRO LIKE '{this.YRNROSearchKey}*'" + 
        $"AND HAKUNIMI LIKE '{this.HAKUNIMISearchKey}*'" +
        $"AND KONSERNI LIKE '{this.GROUPSearchKey}*'" +
        $"AND LY LIKE '{this.BUSINESSIDSearchKey}*'";
    }
    

    第一个解决方案的表达式字符串有一个尾随AND 运算符,这会导致错误消息。用运算符为每个表达式(操作数)添加前缀可以修复它。

    请注意,必须删除表达式的前导 % 才能使其成为“开头为”表达式。有一个前导 % 和一个尾随 %(通配符运算符)将表示“包含在”。

    因为空字符串变量会产生类似"Column LIKE '%' 的表达式,并且因为${null} 返回""(一个空字符串),所以您可以缩短代码:

    public void EnableRowFiltering()
    {
      StringBuilder sb = new StringBuilder();
      sb.Append($"YRNRO LIKE '{this.YRNROSearchKey}%'");
      sb.Append($"AND KONSERNI LIKE '{this.GROUPSearchKey}%'");
      sb.Append($"AND LY LIKE '{this.BUSINESSIDSearchKey}%'");
    
      this.MainDataTable.DefaultView.RowFilter = sb.ToString();
    }
    

    第二个解决方案的表达式似乎是正确的,但不知何故,赋值混淆了。在定义过滤器表达式后分配它以修复它。

    因为空字符串变量会产生像"Column LIKE '*' 这样的表达式,并且因为${null} 返回""(一个空字符串),所以您可以缩短代码:

    public void EnableRowFiltering()
    { 
      string YRNRO = $"YRNRO LIKE '{this.YRNROSearchKey}*'";
      string HAKUNIMI = $" AND HAKUNIMI LIKE '{this.HAKUNIMISearchKey}*'";
      string GROUP = $" AND KONSERNI LIKE '{this.GROUPSearchKey}*'";
      string BUSINESSID = $" AND LY LIKE '{this.BUSINESSIDSearchKey}*'";
    
      this.MainDataTable.DefaultView.RowFilter = YRNRO + HAKUNIMI + GROUP + BUSINESSID;
    }
    

    三个解决方案都经过修复和改进后,都简化为基本相同的解决方案。

    【讨论】:

    • 再次感谢您!第一个解决方案看起来很整洁,但如果其中一个 Textbox 为空,它会失败吗?使用 AND 语句,提供的解决方案都不起作用。但是,如果我在您的第一个解决方案过滤器中将所有 AND 替换为 OR,但在这种情况下,所有 TextBoxes 都不应该为空。所以我必须向他们所有人输入一些数据。
    • 我已经对其进行了测试,它的工作原理与您的示例完全相同。第一个 TextBox 输入过滤器。第二个输入过滤前一个过滤器的结果,依此类推。它适用于所有 TextBox 为空或只有一个为空的情况。这导致假设其他事情是错误的
    • 文本框有没有初始值?能否请您显示从 TextBox 到视图模型的绑定?
    • 同时考虑逻辑运算符及其产生的结果:AND 的作用类似于多级筛或堆叠筛。第一个表达式将产生一个结果。第二个表达式将根据前一个结果集生成一个结果。使用 OR 时,每个过滤器的操作就像原始数据集上的多个并行筛子一样。第一个表达式产生一个结果。第二个表达式将其基于原始集的结果添加到第一个表达式的结果集中。从您的示例来看,您似乎想要堆叠筛子(逻辑与)。
    • 老实说,在这种情况下我不推荐 Rx。只需配置数据绑定并将Binding.UpdateSourceTrigger 设置为PropertyChanged。该值默认设置为LostFocus(对于文本框)。
    【解决方案2】:

    您必须处理 AND 运算符。不能随时添加,要看设置了多少条件(过滤器)。

    假设定义一个名为 int counter 的变量在开始时设置为零,并且每次设置过滤器时都会增加,每次清理过滤器时都会减少,您的代码应该更改为:

    public void EnableRowFiltering()
        {
            StringBuilder sb = new StringBuilder();
            var logicalOperator = counter > 0 ? " AND " : string.Empty;
    
            if (YRNROSearchKey != string.Empty)
            {
                logicalOperator = counter++ > 0 ? " AND " : string.Empty;
                sb.Append($"{logicalOperator}YRNRO LIKE '%{YRNROSearchKey}%'");
            }
            if (HAKUNIMISearchKey != string.Empty)
            {
                logicalOperator = counter++ > 0 ? " AND " : string.Empty;
                sb.Append($"{logicalOperator}HAKUNIMI LIKE '%{HAKUNIMISearchKey}%'");
            }
            if (GROUPSearchKey != string.Empty)
            {
                logicalOperator = counter++ > 0 ? " AND " : string.Empty;
                sb.Append($"{logicalOperator}KONSERNI LIKE '%{GROUPSearchKey}%'");
            }
            if (BUSINESSIDSearchKey != string.Empty)
            {
                logicalOperator = counter++ > 0 ? " AND " : string.Empty;
                sb.Append($"{logicalOperator}LY LIKE '%{BUSINESSIDSearchKey}%'");
            }
    
            this.MainDataTable.DefaultView.RowFilter = sb.ToString();
        }
    

    【讨论】:

      【解决方案3】:

      我会选择 ReactiveExtensions(尤其是来自 RxUI 的那些):

      // written without IDE
      var text1 = this.WhenAnyValue(x => x.YRNROSearchKey).Select(x => {
             if(string.IsNullOrEmpty(x))
                return null;
             return $"YRNRO LIKE '%{x}%";
           }); // get observable to monitor changes
      var text2 = ...
      
      var filterObservable = Observable.CombineLatest(
                      new []{text1, text2, text3} , 
                          (textParts) => {
                               return string.Join(" AND ",  textParts.Where(x => !string.IsNullOrEmpty(x)));
                          }
                   )
                  .Throttle(TimeSpan.FromMilliseconds(80));
      
      filterObservable.ObserveOnDispatcher().Subscribe(f => this.MainDataTable.DefaultView.RowFilter = f); 
      

      我真的很喜欢 Rx,所以我会这样做 - 也许您可以将其用作模板并以其他方式解决更改通知,但构建过滤器应该可以工作。

      RxUI 还带有 DyanmicData - 用于数据操作的出色库,它基本上是可观察的 LINQ

      【讨论】:

      • 为了连接 4 个字符串,有点矫枉过正。
      • 这取决于,如果您希望在用户键入而不是按下按钮时更改过滤器,这非常酷。您可以只使用string.Join 部分
      • @KrzysztofSkowronek 什么是WhenAnyValue - 无法编译?这最后少了一个括号.Throttle(TimeSpan.FromMilliseconds(80)))
      • 也可以添加这些,以便有人会想知道相同的using System.Reactive.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks;
      • this.WhenAnyValue 是 RxUI 的扩展方法,用于实现 INotifyPropertyChanged 的对象,其中 this 在这种情况下是一个视图模型
      猜你喜欢
      • 2020-12-25
      • 2013-06-20
      • 2018-02-07
      • 2016-03-15
      • 2023-04-02
      • 1970-01-01
      • 2017-09-02
      • 2020-06-07
      • 1970-01-01
      相关资源
      最近更新 更多