传递非静态参数以在CollectionViewSource中过滤

问题描述 投票:0回答:2

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

现在考虑在ObservableCollection<KeyValuePair<Guid, T>>中绑定位于对象层次结构根部的ItemsControl。在DataTemplate内部,我想将另一个集合的子集绑定到DependencyPropertyUserControl,其中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的过滤器。

我坦率地失去了,因为文档中没有任何内容。我无法相信我是第一个需要参数化动态过滤器的人,它不会绑定到文本字段...任何提示都非常感谢!

Update 2019-03-18

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

  • 上面的代码驻留在一个视图中,它有自己的视图模型作为数据上下文。在CustomUserControl中实例化的DataTemplate也有自己的视图模型。我在上面尝试的是将过滤结果(它是基于SubsetCollection当前ItemControl元素中包含的主键指示符的RootObjectCollection的子集)传递给CustomUserControl的相应字段。
  • 所有ObservableCollections都位于周围视图的视图模型中,名为Database。这个对象包含几个ObservableCollections,其中包括RootObjectCollectionSubsetCollection
wpf xaml data-binding wpf-controls
2个回答
0
投票

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

CollectionViewSource.Filter是一个事件,您不能传递任何自定义参数。你得到一个FilterEventArgs,它有一个对项目的只读引用和一个Accepted属性,你可以设置它来指示是否在过滤集合中包含该项目,但就是这样。

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

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

0
投票

当我发布这个问题时,我很匆忙,因为在一个非常重要的演示(产品的融资轮次)之前只缺少了几天。由于我无法弄清楚如何使用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();
    }
  }

由于这个解决方案完美无缺,我没有实现the one suggested by @mm8 above。然而,从技术上讲,建议的解决方案是我的问题的直接答案,而我的是诚实的,而不是解决方法。因此,我会接受@ mm8的答案,而不是我的答案。

© www.soinside.com 2019 - 2024. All rights reserved.