如何过滤 CollectionContainer

How to filter the CollectionContainer

我在结合 CompositeCollection.

执行 ICollectionView 过滤时遇到问题

插图描述了我想要实现的目标: Window with filter text box, items collection and "Add" button

要求:

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); 是与 Resources 中定义的 CollectionViewSource 关联的视图,而不是视图CollectionContainer。所以刷新错误的View,显示的View根本不刷新

我能够通过扩展 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 的视图?

提前致谢,很抱歉post。

我找到了解决问题的优雅方法。感谢 Thomas Levesque's .NET blog which was referenced in this answer: CollectionContainer doesn't bind my Collection 上的文章,我可以简单地执行绑定和 ICollectionView 获得的 CollectionViewSource.GetDefaultView(MyCollection); 刷新对了

BindingProxy.cs(感谢 Thomas Levesque)

public class BindingProxy : Freezable
{
    #region Overrides of Freezable
    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }
    #endregion

    public object MyData
    {
        get { return (object)GetValue(MyDataProperty); }
        set { SetValue(MyDataProperty, value); }
    }

    public static readonly DependencyProperty MyDataProperty =
        DependencyProperty.Register("MyData", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

更新后XAML:

<ItemsControl>
  <ItemsControl.Resources>
     <local:BindingProxy x:Key="proxy" MyData="{Binding Path=MyCollection}"/>
  </ItemsControl.Resources>
  <ItemsControl.ItemsSource>
     <CompositeCollection>
        <CollectionContainer Collection="{Binding Source={StaticResource proxy}, Path=MyData}"/>
        <Button Content="Add another one" Margin="5"/>
     </CompositeCollection>
  </ItemsControl.ItemsSource>
  ...
</ItemsControl>