У меня проблема с выполнением фильтрации ICollectionView в сочетании с использованием CompositeCollection .
На рисунке показано, чего я хочу достичь: Окнос текстовым полем фильтра, коллекцией предметов и кнопкой «Добавить»
Требования:
- Кнопка «Добавить» должна быть частью WrapPanel
- Фильтрация должна выполняться через ICollectionView.View.Filter
DebugWindow.xaml:
<StackPanel>
<TextBox Text="{Binding TextBoxText, UpdateSourceTrigger=PropertyChanged}" Margin="5" BorderBrush="Black"/>
<ItemsControl BorderBrush="Gray">
<!-- Resources -->
<ItemsControl.Resources>
<CollectionViewSource x:Key="ColVSKey"
Source="{Binding MyCollection}"/>
</ItemsControl.Resources>
<!-- Items Source -->
<ItemsControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource ColVSKey}}"/>
<Button Content="Add another one" Margin="5"/>
</CompositeCollection>
</ItemsControl.ItemsSource>
<!-- Item Template -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
Background="Gray"
Margin="10"
Padding="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- Items Panel -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
DebugWindow.xaml.cs:
public partial class DebugWindow : Window, INotifyPropertyChanged
{
private string _textBoxText = "";
public ObservableCollection<string> MyCollection { get; set; }
public Predicate<object> FilterFunction { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private ICollectionView view;
public string TextBoxText
{
get
{
return _textBoxText;
}
set
{
if (value == _textBoxText)
return;
_textBoxText = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(nameof(TextBoxText)));
if (view != null)
view.Refresh();
}
}
public DebugWindow()
{
InitializeComponent();
MyCollection = new ObservableCollection<string>() { "one", "two", "Three", "four", "five", "six", "seven", "Eight" };
FilterFunction = new Predicate<object>((o) => Filter(o));
view = CollectionViewSource.GetDefaultView(MyCollection);
if (view != null)
view.Filter = new Predicate<object>((o) => Filter(o));
this.DataContext = this;
}
public bool Filter(object v)
{
string s = (string)v;
bool ret = false;
if (s.IndexOf(TextBoxText) != -1)
ret = true;
return ret;
}
}
Проблема в том, что view = CollectionViewSource.GetDefaultView(MyCollection);
является представлением, связанным с CollectionViewSource , определенным в Resources , а не представлением CollectionContainer .Таким образом, обновляется неправильный вид, а отображаемый вид вообще не обновляется.
Мне удалось добиться желаемого поведения с расширением CollectionContainer , подключив к событию CollectionChanged:
public class MyCollectionContainer : CollectionContainer
{
private ICollectionView _view;
public ICollectionView View
{
get
{
return _view;
}
}
public MyCollectionContainer()
{
this.CollectionChanged += MyCollectionContainer_CollectionChanged;
}
private void MyCollectionContainer_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (_view == null && Collection != null && MyFilter != null)
{
_view = CollectionViewSource.GetDefaultView(Collection);
_view.Filter += MyFilter;
}
}
}
и подвергая его программному обеспечению:
...in XAML...
<CompositeCollection>
<local:MyCollectionContainer x:Name="MyCollectionContainer" Collection="{Binding Source={StaticResource ColVSKey}}"/>
<Button Content="Add another one" Margin="5"/>
</CompositeCollection>
...in constructor...
MyCollectionContainer.MyFilter = new Predicate<object>((o) => Filter(o));
...in TextBoxText property set...
if(MyCollectionContainer.View!=null)
MyCollectionContainer.View.Refresh();
Вопросы: Есть ли способ добиться нужного мне поведения, не подвергая элемент управления программному обеспечению?Можно ли привязать MVVM к представлению CollectionContainer ?
Заранее спасибо и извините за длинный пост.