将非静态参数传递给 CollectionViewSource 中的过滤器
Pass non-static parameter to filter in CollectionViewSource
在我的应用程序中,我有一个类似数据库的结构,其中数据库对象本身包含几个 ObservableCollection<KeyValuePair<Guid, T>>
集合。 Guid
的行为类似于关系数据库中的主键,即它们提供 1:1 和 1:n 不同集合(数据库中的“表”)对象之间的映射。
现在考虑将位于对象层次结构根部的 ObservableCollection<KeyValuePair<Guid, T>>
绑定到 ItemsControl
。在 DataTemplate
中,我想 将另一个集合的子集 绑定到 UserControl
的 DependencyProperty
,其中 Guid
s 匹配第一个集合中每个对象携带的值。
正如 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 的问题:
上面的代码驻留在一个视图中,它有自己的视图模型作为数据上下文。在 DataTemplate
中实例化的 CustomUserControl
也有自己的视图模型。我在上面尝试的是将过滤结果(它是 SubsetCollection
的子集,基于 ItemControl
的当前 RootObjectCollection
元素中包含的主键指示符)传递给 CustomUserControl
的对应字段.
所有 ObservableCollection
都驻留在名为 Database
的对象中的周围视图的视图模型中。此对象包含其中的几个 ObservableCollections
,其中包括 RootObjectCollection
和 SubsetCollection
。
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 的回答而不是我的。
在我的应用程序中,我有一个类似数据库的结构,其中数据库对象本身包含几个 ObservableCollection<KeyValuePair<Guid, T>>
集合。 Guid
的行为类似于关系数据库中的主键,即它们提供 1:1 和 1:n 不同集合(数据库中的“表”)对象之间的映射。
现在考虑将位于对象层次结构根部的 ObservableCollection<KeyValuePair<Guid, T>>
绑定到 ItemsControl
。在 DataTemplate
中,我想 将另一个集合的子集 绑定到 UserControl
的 DependencyProperty
,其中 Guid
s 匹配第一个集合中每个对象携带的值。
正如 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 的问题:
上面的代码驻留在一个视图中,它有自己的视图模型作为数据上下文。在
DataTemplate
中实例化的CustomUserControl
也有自己的视图模型。我在上面尝试的是将过滤结果(它是SubsetCollection
的子集,基于ItemControl
的当前RootObjectCollection
元素中包含的主键指示符)传递给CustomUserControl
的对应字段.所有
ObservableCollection
都驻留在名为Database
的对象中的周围视图的视图模型中。此对象包含其中的几个ObservableCollections
,其中包括RootObjectCollection
和SubsetCollection
。
However, I need to dynamically pass a parameter of type
ObservableCollection<Guid>
to the filter of theCollectionViewSource
.
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();
}
}
由于这个解决方案完美无缺,我没有实施