WPF MVVM: фильтрация по члену, отображаемому в списке

У меня есть gridview в списке. Существует столбец «Отдел», который привязан к свойству «Отдел» в модели, а также связан с преобразователем.

Преобразователь принимает значение, полученное от свойства, и отображает его в другой форме (с другой строкой).

Например, если это свойство "Отдел" имеет значение "100AB" в столбце отображается как "Финансовый", если значение доходит до "200CB" в столбце отображается "Администрация" и так далее...

Моя проблема заключается в фильтрации списка с помощью фильтра. Внутренне он фильтрует по «100AB», «200CB» вместо отображаемых значений «Финансы» и «Администрирование», так как это решить?

Просмотр (xaml):

<ListView Grid.Row="1" Grid.Column="0"
          Name="MyListView" 
          ItemsSource="{Binding Path=View}" 

<GridViewColumn Header="Department" Width="190"
                DisplayMemberBinding="{Binding Department, Converter={StaticResource DeptTypeConverter}}">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
             <TextBlock TextAlignment="Right"/>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

Конвертер:

public class DeptTypeConverter: IValueConverter
{
    #region Constants

    private const string DeptFinancialType = "100AB";
    private const string DeptAdminType = "200CB";

    private const string DeptFinancialView = "Finanacial";
    private const string DeptAdminView = "Administration";

    #endregion

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Do the conversion from drink type to present it in the view
        string s = (string)value;
        if (s == DeptFinancialType )
            return DeptFinancialView;
        else if (s == DeptAdminType)
            return DeptAdminView;
        else
            throw new Exception(string.Format("Cannot convert, unknown value {0}", value));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Do the conversion from presentation to data type
        string s = (string)value;
        if (s.Equals(DeptFinancialView, StringComparison.InvariantCultureIgnoreCase))
            return DeptFinancialType;
        else if (s.Equals(DeptAdminView, StringComparison.InvariantCultureIgnoreCase))
            return DeptAdminType;
        else
            throw new Exception(string.Format("Cannot convert, unknown value {0}", value));
    }
}

Фильтр в модели просмотра:

private CollectionView view;

public CollectionView View
{
    get
    {
        return this.view;
    }

    private set
    {
        if (this.view == value)
        {
            return;
        }

        this.view = value;
        OnPropertyChanged("View");
    }
}

// This code in constructor
this.View = (CollectionView)CollectionViewSource.GetDefaultView(this.MyListView);
this.View.Filter = UserFilter;
//

private bool MyFilter(object item)
{
    if (String.IsNullOrEmpty(this.TextToFilter))
    {
        return true;
    }
    else
    {
        DataModel m = (item as DataModel);
        bool result = (m.Department.IndexOf(this.TextToFilter, StringComparison.OrdinalIgnoreCase) >= 0);

        return result;
    }
}

У меня есть другие поля в фильтре, по которым я фильтрую, но для простоты я не указал в методе MyFilter. В этих полях не используется конвертер, в данном случае он не нужен, нужен только в случае, который я предоставил.

DataModel — это модель данных, содержащая свойство «Отдел», к которому привязано представление.

this.TextToFilter — это текстовое поле в представлении для фильтрации.

ПОПЫТКА №1:

Вместо использования ConvertBack в DeptTypeConverter я использовал Convert (нет необходимости что-либо изменять в методе Convert). Ниже работает правильно.

private bool MyFilter(object item)
{
    if (String.IsNullOrEmpty(this.TextToFilter))
    {
        return true;
    }
    else
    {
        DataModel m = (item as DataModel);

        bool result = (new Converters.DeptTypeConverter().Convert(m.Department, null, null, null).ToString().IndexOf(this.TextToFilter, StringComparison.OrdinalIgnoreCase) >= 0);

        return result;
    }
}

m.Department содержит внутреннее сохраненное значение (не отображаемое).

Я думаю, что лучше использовать Convert, а не ConvertBack, поскольку пользователь вводит отображаемый текст (без значения, хранящегося внутри) при поиске. Использование ConvertBack требует большей логики для реализации, и это не так просто, как использование простого Convert.

Если у кого-то есть другие идеи получше, поделитесь. Любая идея или улучшения всегда приветствуются. Насколько это возможно, я хотел бы не нарушать архитектуру шаблона MVVM.


person Ralph    schedule 01.08.2017    source источник
comment
Используйте CollectionViewSource — wpftutorial.net/DataViews.html   -  person ajg    schedule 01.08.2017
comment
@ajg Да, я использую его, смотрите мой пост с обновлением, я разместил больше кода.   -  person Ralph    schedule 01.08.2017
comment
OK. Тогда не уверен, так как не думаю, что вы можете легко связать элемент дисплея обратно. Вы можете изменить свой метод ConvertBack, чтобы он не выдавал исключение, если совпадения нет, просто возвращайте string.Empty. Затем вызовите ConvertBack в методе фильтра и используйте значение для фильтрации вместо TextToFilter, если оно не пустое? Не уверен, что мне нравится это в MVVM.   -  person ajg    schedule 01.08.2017
comment
Вам, вероятно, лучше всего полностью избавиться от IValueConverter и выполнить преобразование в ViewModel - stackoverflow.com/questions/769511/   -  person ajg    schedule 01.08.2017
comment
@ajg Да, я думаю, что это лучший вариант воспользоваться конвертером. Но если я вызову convertBack из своего фильтра, какие параметры я должен передать методу? value — это значение, которое я хочу преобразовать, но как насчет других, я имею в виду тип цели, параметр и культуру?   -  person Ralph    schedule 01.08.2017
comment
вы не используете другие параметры, так что это не имеет значения. Передать нули.   -  person ajg    schedule 01.08.2017
comment
@ajg Наконец-то я использовал метод Convert вместо ConvertBack, см. мой обновленный пост. Попытка №1   -  person Ralph    schedule 02.08.2017


Ответы (1)


Я считаю, что лучший способ сделать это — вообще не использовать IValueConverter. Я бы изменил ваш класс DataModel, чтобы добавить новое свойство DepartmentDisplay. Используйте свойство DepartmentDisplay для привязки GridViewColumn и для фильтрации, а свойство Department — для всего, что вы делаете с данными после отображения сетки. Это будет самое простое и самое MVVM-решение, оно полностью удаляет IValueConverter.

Модель данных....

public string Department { get; set; }

public string DepartmentDisplay
{
    get
    {
        if (Department == "100AB")
            return "Financial";
        if (Department == "200CB")
            return "Administration";

        return "";
    }
}

Вид...

<GridViewColumn Header="Department" Width="190"
            DisplayMemberBinding="{Binding DepartmentDisplay}">

ПросмотретьМодель...

private bool MyFilter(object item)
{
    if (String.IsNullOrEmpty(this.TextToFilter))
    {
        return true;
    }
    else
    {
        DataModel m = (item as DataModel);
        bool result = (m.DepartmentDisplay.IndexOf(this.TextToFilter, StringComparison.OrdinalIgnoreCase) >= 0);

        return result;
    }
}
person ajg    schedule 02.08.2017