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();
}
1 ответ
Этот ответ помог мне с этой проблемой. Статический CollectionViewSource.GetDefaultView(coll)
Метод всегда будет возвращать одну и ту же ссылку для данной коллекции, поэтому использование нескольких представлений коллекции на одной и той же ссылке будет контрпродуктивным. Создавая представление следующим образом:
ICollectionView filteredView = new CollectionViewSource { Source=messageList }.View;
Теперь представление может быть отфильтровано / отсортировано / сгруппировано независимо от других. Затем вы можете применить свою фильтрацию.
Я знаю, что прошло пару месяцев, и вы, вероятно, уже решили свою проблему, но я столкнулся с этим вопросом, когда у меня возникла та же проблема, поэтому я решил добавить ответ.
Для тех, кто борется с проблемой того, что filterView не соблюдает sourceCollection (messageList в этом примере):
Я пришел с этим решением:
ICollectionView filteredView = new CollectionViewSource { Source=messageList }.View;
messageList.CollectionChanged += delegate { filteredView.Refresh(); };
Таким образом, он будет обновлять наш filterView всегда, когда событие CollectionChanged источника запускается. Конечно, вы можете реализовать это и так:
messageList.CollectionChanged += messageList_CollectionChanged;
private void messageList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
filteredView.Refresh();
}
Рассмотрите возможность использования PropertyChanged-Events, когда требуется фильтрация по определенному свойству.