WPF Multiple CollectionView с разными фильтрами в одной коллекции

Я использую ObservableCollection с двумя ICollectionView для разных фильтров.

Один предназначен для фильтрации сообщений по какому-либо типу, а другой — для подсчета проверенных сообщений. Как видите, фильтр сообщений и счетчик сообщений работают нормально, но когда я снимаю флажок, сообщение исчезает из списка (счетчик все еще работает).

Кстати, извините за длинный пост, я хотел включить все важные вещи.

Код XAML:

<!-- Messages List -->
<DockPanel Grid.Row="1"
           Grid.Column="0"
           Grid.ColumnSpan="3"
           Height="500">
  <ListBox Name="listBoxZone"
           ItemsSource="{Binding filteredMessageList}"
           Background="Transparent"
           BorderThickness="0">
    <ListBox.ItemTemplate>
      <DataTemplate>
        <CheckBox Name="CheckBoxZone"
                  Content="{Binding text}"
                  Tag="{Binding id}"
                  Unchecked="CheckBoxZone_Unchecked"
                  Foreground="WhiteSmoke"
                  Margin="0,5,0,0"
                  IsChecked="{Binding isChecked}" />
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</DockPanel>
<Button Content="Test Add New"
        Grid.Column="2"
        Height="25"
        HorizontalAlignment="Left"
        Margin="34,2,0,0"
        Click="button1_Click" />
<Label Content="{Binding checkedMessageList.Count}"
       Grid.Column="2"
       Height="25"
       Margin="147,2,373,0"
       Width="20"
       Foreground="white" />

Снимок экрана: введите здесь описание изображения

Код:

/* ViewModel Class */
public class MainViewModel : INotifyPropertyChanged
{

    // Constructor
    public MainViewModel()
    {
        #region filteredMessageList
        // connect the ObservableCollection to CollectionView
        _filteredMessageList = CollectionViewSource.GetDefaultView(messageList);
        // set filter 
        _filteredMessageList.Filter = delegate(object item)
        {
            MessageClass temp = item as MessageClass;

            if ( selectedFilter.Equals(AvailableFilters.All) )
            {
                return true;
            }
            else
            {
                return temp.filter.Equals(_selectedFilter);
            }
        };
        #endregion

        #region checkedMessageList
        // connect the ObservableCollection to CollectionView
        _checkedMessageList = CollectionViewSource.GetDefaultView(messageList);
        // set filter 
        _checkedMessageList.Filter = delegate(object item) { return (item as MessageClass).isChecked; };
        #endregion
    }

    // message List
    private ObservableCollection<MessageClass> _messageList =
            new ObservableCollection<MessageClass>();
    public ObservableCollection<MessageClass> messageList
    {
        get { return _messageList; }
        set { _messageList = value; }
    }

    // CollectionView (filtered messageList)
    private ICollectionView _filteredMessageList;
    public ICollectionView filteredMessageList
    {
        get { return _filteredMessageList; }
    }

    // CollectionView (filtered messageList)
    private ICollectionView _checkedMessageList;
    public ICollectionView checkedMessageList
    {
        get { return _checkedMessageList; }
    }

    // SelectedFilter property
    private AvailableFilters _selectedFilter = AvailableFilters.All; // Default is set to all
    public AvailableFilters selectedFilter
    {
        get { return _selectedFilter; }
        set
        {
            _selectedFilter = value;
            RaisePropertyChanged("selectedFilter");
            _filteredMessageList.Refresh(); // refresh list upon update
        }
    }

    // FilterList (Convert Enum To Collection)
    private List<KeyValuePair<string, AvailableFilters>> _AvailableFiltersList;
    public List<KeyValuePair<string, AvailableFilters>> AvailableFiltersList
    {
        get
        {
            /* Check if such list available, if not create for first use */
            if (_AvailableFiltersList == null)
            {
                _AvailableFiltersList = new List<KeyValuePair<string, AvailableFilters>>();
                foreach (AvailableFilters filter in Enum.GetValues(typeof(AvailableFilters)))
                {
                    string Description;
                    FieldInfo fieldInfo = filter.GetType().GetField(filter.ToString());
                    DescriptionAttribute[] attributes =
                                (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

                    /* if not null get description */
                    if (attributes != null && attributes.Length > 0)
                    {
                        Description = attributes[0].Description;
                    }
                    else
                    {
                        Description = string.Empty;
                    }

                    /* add as new item to filterList */
                    KeyValuePair<string, AvailableFilters> TypeKeyValue =
                                new KeyValuePair<string, AvailableFilters>(Description, filter);

                    _AvailableFiltersList.Add(TypeKeyValue);
                }
            }
            return _AvailableFiltersList;
        }
    }

    #region Implement INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

Код для функции снятия отметки

private void CheckBoxZone_Unchecked(object sender, RoutedEventArgs e)
{
    CheckBox chkZone = (CheckBox)sender;
    ucSystemMessageVM.checkedMessageList.Refresh();
}

person drtf    schedule 19.05.2013    source источник
comment
Дэйв, это не столько ответ, сколько может помочь вам в будущем. Недавно выполняя контрактную работу с WPF, я просто не мог найти правильное решение для фильтрации, подкачки и сортировки, как я хотел. Я построил этот общий класс. Подумал, может тебе захочется в нем поковыряться. origin1.com/downloads/PagedObservableCollection.txt. очевидно изменить доп.   -  person origin1tech    schedule 19.05.2013
comment
При поиске подобной проблемы я наткнулся на этот вопрос и ответ. Очень сложно понять, что здесь происходит -- определение для MessageClass и AvailableFilters не включено, и есть ряд функций C#/.NET, которые сделали бы этот код гораздо более сжатым и более понятным с первого взгляда -- автосвойства, лямбда-выражения, LINQ.   -  person Zev Spitz    schedule 06.05.2018


Ответы (2)


Этот ответ помог мне решить именно эту проблему. Статический метод CollectionViewSource.GetDefaultView(coll) всегда будет возвращать одну и ту же ссылку для данной коллекции, поэтому основывать несколько представлений коллекции на одной и той же ссылке будет контрпродуктивно. Создавая представление следующим образом:

ICollectionView filteredView = new CollectionViewSource { Source=messageList }.View;

Представление теперь можно фильтровать/сортировать/группировать независимо от любых других. Затем вы можете применить фильтрацию.

Я знаю, что прошла пара месяцев, и вы, вероятно, уже решили свою проблему, но я столкнулся с этим вопросом, когда у меня была такая же проблема, поэтому я решил добавить ответ.

person randcd    schedule 15.07.2013
comment
Спасибо, на самом деле это очень полезно, так как я не смог этого добиться и в результате получил большой уродливый обходной путь. - person drtf; 21.07.2013
comment
У меня есть проблема с этим методом: filteredView, похоже, не наблюдает за messageList, поэтому он не реагирует на какие-либо изменения в исходной коллекции. - person Jakub Pawlinski; 06.07.2016
comment
@JakubPawlinski, я вижу то же самое. Вы когда-нибудь находили решение? - person Pancake; 30.08.2018
comment
@JakubPawlinski ,@Pancake . Я нашел решение. Непосредственно создайте представление коллекции списков. ICollectionView filterdView = новый ListCollectionView (sourceCollection); Спасибо - person Null Pointer; 13.09.2018
comment
Мне нужен пример, который использует эту технику. Непонятно, что делать с предоставленной единственной строкой кода. Он не вписывается ни в один из других примеров CollectionView, которые я нашел. - person Scott Solmer; 09.12.2019
comment
@Okuma.Scott Вместо использования: ICollectionView filteredView = new CollectionViewSource { Source=messageList }.View; вы используете: ICollectionView filterdView=new ListCollectionView(messageList); Это сработало идеально для меня! - person Axilleas Kar; 17.02.2020
comment
Этот ответ дал половину того, что я искал. Другая половина взята из здесь. По сути, в некоторых сценариях обновление представления вызывает исключение нулевой ссылки, потому что источник, поддерживающий представление, был подвергнут сборке мусора. Исправление состоит в том, чтобы сохранить исходный код в переменной класса, а не выбрасывать его: _viewSource = new CollectionViewSource { Source = messageList }; var filteredView = _viewSource.View; - person Travis; 29.05.2020

Для тех, кто борется с проблемой, что filteredView не наблюдает sourceCollection (messageList в этом примере):

Я пришел с этим решением:

ICollectionView filteredView = new CollectionViewSource { Source=messageList }.View;
messageList.CollectionChanged += delegate { filteredView.Refresh(); };

Таким образом, он будет обновлять наш filteredView каждый раз, когда запускается событие CollectionChanged источника. Конечно, вы можете реализовать это и так:

messageList.CollectionChanged += messageList_CollectionChanged;

private void messageList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    filteredView.Refresh(); 
}

Рассмотрите возможность использования PropertyChanged-Events, когда требуется фильтрация по определенному свойству.

person wulf11    schedule 29.01.2020
comment
Если вы хотите, чтобы ваше отфильтрованное представление автоматически переоценивало каждый раз, когда изменяется соответствующее свойство (любого из элементов в коллекции), рассмотрите возможность использования LiveFilteringProperties. Тогда вам не нужно явно писать код для обновления docs.microsoft.com/de-de/dotnet/api/ Однако afaics он предназначен только для реагирования на события PropertyChanged элемента. Он не слушает CollectionChanged (если я правильно помню). - person lidqy; 24.05.2021