简体   繁体   中英

Filter WPF DataGrid (DataTable) based on values from several TextBoxes MVVM

I have been trying to setup filter that would be based on 4 TextBoxes. If first textbox is not empty -> then filter based on that, if first and second textbox is not empty combine filter from these two textboxes etc. I expect my filter to work like this http://www.tablefilter.com/auto-filter.html

As you can see I have tried several variants but I am constantly getting an error provided below. Any suggestions how to get it working?

Here is my code:

    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: 'Syntax error: Missing operand after 'And' operator.'

This has been working, but I have to provide all values (fill in all TextBoxes) in order to filter:

  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}*'";
  }

I would go with ReactiveExtensions (especially those from 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); 

I really like Rx so that is how I would do it - maybe you can use as a template and solve the change notification in some other manner, but constructing the filter should work.

RxUI also comes with DyanmicData - great lib for data manipulation, it's basically observable LINQ

You have to handle the AND operator. You can't add it any time, it depends how many conditions (filters) are set.

Supposing to defining a variable named int counter set equal to zero at the start-time and increased every time a filter is set and decreased any time a filter is cleaned, your code should be changed into this:

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

The last solution is by far the shortest of all, so I would prefer it.
You simply have to replace OR with AND (my bad).

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}*'";
}

The first solution's expression string has a trailing AND operator, which leads to the error message. Prefixing each expression (operand) with the operator would fix it.

Note that the leading % of the expression must be removed in order to make it a 'starts with' expression. Having a leading % and a trailing % (wildcard operator) would express a 'contains in'.

Because and empty string variable would result in an expression like "Column LIKE '%' and because ${null} returns "" (an empty string), you can shorten the code:

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

The second solution's expression seems to be correct, but somehow the assignment got confused. Assign the filter expression after you have defined it to fix it.

Because and empty string variable would result in an expression like "Column LIKE '*' and because ${null} returns "" (an empty string), you can shorten the code:

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

After fixing and improving all three solutions, they all were reduced to basically the same solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM