将非静态参数传递给 CollectionViewSource 中的过滤器

Pass non-static parameter to filter in CollectionViewSource

在我的应用程序中,我有一个类似数据库的结构,其中数据库对象本身包含几个 ObservableCollection<KeyValuePair<Guid, T>> 集合。 Guid 的行为类似于关系数据库中的主键,即它们提供 1:1 和 1:n 不同集合(数据库中的“表”)对象之间的映射。

现在考虑将位于对象层次结构根部的 ObservableCollection<KeyValuePair<Guid, T>> 绑定到 ItemsControl。在 DataTemplate 中,我想 将另一个集合的子集 绑定到 UserControlDependencyProperty,其中 Guids 匹配第一个集合中每个对象携带的值。

正如 SO 上的大量答案所建议的那样,CollectionViewSource 是我所需要的,即

<ItemsControl ItemsSource="{Binding RootObjectCollection}>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <local:CustomUserControl>
        <local:CustomUserControl.SubsetCollection>
          <Binding>
            <Binding.Source>
              <CollectionViewSource Source="{Binding DataContext.Database.SubsetCollection, RelativeSource={RelativeSource AncestorType=UserControl}}"
                                    Filter="someFilter"
                               ???? FilterParameters="{Binding SelectedKeys}" />
            </Binding.Source>
          </Binding>
        </local:CustomUserControl.SubsetCollection>
      </local:CustomUserControl>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

但是,我需要动态传递类型为 ObservableCollection<Guid> 的参数给 CollectionViewSource 的过滤器。

坦率地说,我迷路了,因为文档对此没有任何内容。我不敢相信我是第一个需要不绑定到文本字段的参数化动态过滤器的人......非常感谢任何提示!

2019-03-18更新

上面的代码现在应该更清楚了。除此之外,还有一些背景信息可以澄清@erotavlas 的问题:

However, I need to dynamically pass a parameter of type ObservableCollection<Guid> to the filter of the CollectionViewSource.

CollectionViewSource.Filter 是一个事件,您不能将任何自定义参数传递给它。您会得到一个 FilterEventArgs,它具有对该项目的只读引用,以及一个 Accepted 属性,您可以设置它来指示是否将该项目包含在过滤集中,仅此而已。

您或许可以考虑创建一个扩展 CollectionViewSource 的 class 并添加您自己的自定义 dependency property。这应该使您能够绑定到像 SelectedKeys 这样的源 属性。然后,您可以通过在 Filter 事件处理程序中转换 sender 参数来检索依赖项 属性 的值,例如:

private void Cvs_Filter(object sender, FilterEventArgs e)
{
    YourCustomCollectionViewSource cvs = sender as YourCustomCollectionViewSource;
    //..
}

当我发布这个问题时,我很着急,因为距离非常重要的演示(产品融资轮)只差几天。由于我不知道如何解决 CollectionViewSource 的问题,我决定尝试使用旧 MultiValueConverter 方法的解决方案,同时充分意识到这会让我创建一个新的 ObservableCollection 的子集集合的值,根据 C# man page for ObservableCollection<T>(IEnumerable<T>),这只能单向工作,因为“元素被复制到 ObservableCollection<T>”。我认为最好显示视图是从数据库填充的,而不将更改反映回数据库,而不是什么都不显示。

当我发现手册页在这里并不完全正确时,想象一下我的惊讶:只复制原始值,复杂对象通过引用传递到新的 ObservableCollection<T>!这意味着以下代码片段是对我的问题完全有效的解决方案:

<ItemsControl ItemsSource="{Binding RootObjectCollection}>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <local:CustomUserControl>
        <local:CustomUserControl.SubsetCollection>
          <MultiBinding Converter="{StaticResource SubsetEntryFromRootObjectIdSelectionConverter}">
            <Binding Path="Value.SubsetIds" />
            <Binding Path="DataContext.Database.SubsetCollection" RelativeSource="{RelativeSource AncestorType=UserControl}" />
        </MultiBinding>
        </local:CustomUserControl.SubsetCollection>
      </local:CustomUserControl>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

这里重要的部分是MultiValueConverter本身,定义为

public class SubsetEntryFromRootObjectIdSelectionConverter: IMultiValueConverter {

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
      if (values[0] == null) // no reference ids contained
        return new ObservableCollection<SubsetItem>();

      if (!(values[0] is ObservableCollection<Guid>))
        throw new InvalidOperationException("Value must be a collection of Guids.");

      if (!(values[1] is ObservableCollection<KeyValuePair<Guid, SubsetItem>>))
        throw new InvalidOperationException("Value must be a collection of SubsetItems.");

      var selectedKeys = (ObservableCollection<Guid>)values[0];
      var originalCollection = (ObservableCollection<KeyValuePair<Guid, SubsetItem>>)values[1];
      var queryCollection = originalCollection.Where(kvp => selectedKeys.Contains(kvp.Key)).Select(kvp => kvp.Value);

      // it seems that the man page is misleading and that this constructor indeed copies references, not objects
      return new ObservableCollection<SubsetItem>(queryCollection);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
      throw new NotImplementedException();
    }
  }

由于这个解决方案完美无缺,我没有实施 。但是,从技术上讲,建议的解决方案是对我的问题的直接回答,而老实说,我的解决方案是一种解决方法。因此,我将接受@mm8 的回答而不是我的。