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
Този отговор даде половината от това, което търсех. Другата половина идва от тук. По принцип в някои сценарии опресняването на изгледа хвърля изключение за нулева препратка, тъй като източникът, подкрепящ изгледа, е GC'd. Поправката е да съхраните източника в променлива с обхват на клас, вместо да го изхвърлите: _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