【问题标题】:Pass non-static parameter to filter in CollectionViewSource将非静态参数传递给 CollectionViewSource 中的过滤器
【发布时间】:2019-03-16 23:49:14
【问题描述】:

在我的应用程序中,我有一个类似数据库的结构,其中数据库对象本身包含多个ObservableCollection<KeyValuePair<Guid, T>> 集合。 Guids 的作用类似于关系数据库中的主键,即它们在不同集合的对象(数据库中的“表”)之间提供 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&lt;Guid&gt; 类型的参数动态传递给CollectionViewSource 的过滤器。

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

2019-03-18 更新

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

  • 上面的代码驻留在一个视图中,它有自己的视图模型作为数据上下文。在DataTemplate 中实例化的CustomUserControl 也有自己的视图模型。我在上面尝试的是将过滤结果(它是SubsetCollection 的子集,基于ItemControl 的当前RootObjectCollection 元素中包含的主键指示符)到CustomUserControl 的相应字段。

  • 所有ObservableCollections 都驻留在名为Database 的对象中的周围视图的视图模型中。此对象包含其中几个ObservableCollections,其中包括RootObjectCollectionSubsetCollection

【问题讨论】:

  • 你的用户控件数据上下文是如何设置的?它是自己的视图模型还是继承父控件的数据上下文?每个 observablecollection 驻留在哪里?
  • @erotavlas,我已经更新了我的问题,以澄清你在问什么。请看一看,感谢您的支持!

标签: wpf xaml data-binding wpf-controls


【解决方案1】:

但是,我需要将ObservableCollection&lt;Guid&gt; 类型的参数动态传递给CollectionViewSource 的过滤器。

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

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

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

【讨论】:

  • 谢谢!由于时间压力,我不得不走一条我知道的路,找到了perfectly feasible workaround solution。我没有实施您的建议,但由于它 a) 看起来可行,并且 b) 是对我的问题的直接回答(而不是我自己的解决方法),因此我接受您的建议作为对我的问题的回答。
【解决方案2】:

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

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

<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 的回答而不是我的回答。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-17
    • 2021-12-02
    • 2012-07-29
    • 2011-02-15
    • 2013-08-17
    • 1970-01-01
    相关资源
    最近更新 更多