WedX - журнал о программировании и компьютерных науках

Фильтр WPF DataGrid (DataTable) на основе значений из нескольких текстовых полей MVVM

Я пытался настроить фильтр, основанный на 4 текстовых полях. Если первое текстовое поле не пусто -> затем фильтруйте на основе этого, если первое и второе текстовые поля не пусты, объедините фильтр из этих двух текстовых полей и т. д. Я ожидаю, что мой фильтр будет работать следующим образом https://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}*'";
  }
08.09.2020

Ответы:


1

Последнее решение, безусловно, самое короткое из всех, поэтому я бы предпочел его.
Вам просто нужно заменить ИЛИ на И (моя ошибка).

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

После исправления и улучшения всех трех решений все они были сведены к одному и тому же решению.

08.09.2020
  • Еще раз спасибо! Первое решение выглядит аккуратно, но оно не работает, если одно из текстовых полей пусто? С оператором AND ни одно из предоставленных решений не работает. Однако, если я заменю все И на ИЛИ в вашем первом решении, фильтр сработает, но в этом случае все текстовые поля не должны быть пустыми. Поэтому я должен ввести некоторые данные для всех из них. 08.09.2020
  • Я проверил это, и это работает точно так же, как в вашем примере. Первые входные фильтры TextBox. Второй вход фильтрует результаты предыдущего фильтра и так далее. Это работает, когда все TextBox пусты или пуст только один. Это приводит к предположению, что что-то еще не так 08.09.2020
  • Имеют ли текстовые поля какое-либо начальное значение? Не могли бы вы показать привязки из TextBox для просмотра модели? 08.09.2020
  • Рассмотрите как логические операторы, так и полученные ими результаты: И будет действовать как многоступенчатое или многоярусное сито. Первое выражение даст результат. Второе выражение даст результат на основе предыдущего набора результатов. При использовании ИЛИ каждый фильтр работает как несколько параллельных сит на исходном наборе данных. Первое выражение дает результат. Второе выражение добавляет свой результат на основе исходного набора к набору результатов первого выражения. Из вашего примера похоже, что вам нужно сложенное сито (логическое И). 08.09.2020
  • О, я снова пропустил это! Я перепутал крепления, поэтому два из них были соединены наоборот. Теперь это работает! Еще раз спасибо! Теперь я хотел бы фильтровать при вводе текста в TextBox, не переключаясь на следующий TextBox, чтобы обновить фильтр. Мне, наверное, нужно задать еще один вопрос или подписаться на комментарий Крызстофа? 08.09.2020
  • Честно говоря, я бы не рекомендовал Rx в этом сценарии. Просто настройте привязку данных и установите Binding.UpdateSourceTrigger на PropertyChanged. По умолчанию установлено значение LostFocus (для текстового поля). 08.09.2020

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

    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.

    08.09.2020
  • Довольно избыточно, чтобы объединить 4 строки. 08.09.2020
  • это зависит от того, если вы хотите, чтобы фильтр менялся во время ввода пользователем, а не при нажатии кнопки, это довольно круто. Вы можете просто взять часть string.Join 08.09.2020
  • @KrzysztofSkowronek Что такое WhenAnyValue - не компилируется? В конце не хватает одной скобки .Throttle(TimeSpan.FromMilliseconds(80))) 08.09.2020
  • Также их можно добавить, чтобы кому-то было интересно то же самое using System.Reactive.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; 08.09.2020
  • this.WhenAnyValue — это метод расширения из RxUI для объектов, реализующих INotifyPropertyChanged, где this в этом случае — модель представления. 08.09.2020
  • Я предположил, что YRNROSearchKey и другие являются связываемыми свойствами модели представления, привязанной к текстовым полям. 08.09.2020
  • кроме того, большая часть кода предназначена для автообновления - только string.Join для concat 08.09.2020
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]