在我的应用程序中,我有一个类似数据库的结构,其中数据库对象本身包含几个ObservableCollection<KeyValuePair<Guid, T>>
集合。 Guid
s的行为类似于关系数据库中的主键,即它们在不同集合的对象(数据库中的“表”)之间提供1:1和1:n映射。
现在考虑在ObservableCollection<KeyValuePair<Guid, T>>
中绑定位于对象层次结构根部的ItemsControl
。在DataTemplate
内部,我想将另一个集合的子集绑定到DependencyProperty
的UserControl
,其中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
的过滤器。
我坦率地失去了,因为文档中没有任何内容。我无法相信我是第一个需要参数化动态过滤器的人,它不会绑定到文本字段...任何提示都非常感谢!
上面的代码现在应该更清楚一些。除此之外,还有一些背景信息,以澄清@ erotavlas的问题:
CustomUserControl
中实例化的DataTemplate
也有自己的视图模型。我在上面尝试的是将过滤结果(它是基于SubsetCollection
当前ItemControl
元素中包含的主键指示符的RootObjectCollection
的子集)传递给CustomUserControl
的相应字段。ObservableCollection
s都位于周围视图的视图模型中,名为Database
。这个对象包含几个ObservableCollections
,其中包括RootObjectCollection
和SubsetCollection
。但是,我需要动态地将
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;
//..
}
当我发布这个问题时,我很匆忙,因为在一个非常重要的演示(产品的融资轮次)之前只缺少了几天。由于我无法弄清楚如何使用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的答案,而不是我的答案。