【发布时间】:2020-02-02 22:00:18
【问题描述】:
给定一个“客户”实体:
public class CustomerEntity: EntityBase
{
public Dictionary<int, string> ClientsOfCustomer = new Dictionary<int, string>();
public CustomerEntity()
{
// Load ClientsOfCustomer...
}
}
以及两个或更多 WPF 组合框,它们的 ItemsSourceProperty 绑定到同一源(例如,上述“客户”实体的属性):
var comboBox1 = new ComboBox();
var comboBox2 = new ComboBox();
comboBox1.SetBinding(ItemsControl.ItemsSourceProperty,
new Binding(itemsSourceProperty) { ElementName = "ClientsOfCustomer" });
comboBox2.SetBinding(ItemsControl.ItemsSourceProperty,
new Binding(itemsSourceProperty) { ElementName = "ClientsOfCustomer" });
以上是在我们真正拥有底层对象的实例之前完成的。这会在应用程序中稍后发生:
var customer = new CustomerEntity();
parentPageOfComboboxes.DataContext = customer;
如果我们随后过滤ItemCollection 或ComboBox:
comboBox1.Items.Filter = i => ((KeyValuePair<int, string>)i).Value.StartsWith("a");
这最终也会过滤另一个ComboBox。
我知道这是因为ComboBox 是一个Selector,它是一个ItemsControl,并且ItemsControl 在ItemsSource DependencyProperty 更改时调用ItemCollection.SetItemsSource。然后ItemCollection 使用CollectionViewSource.GetDefaultCollectionView( _itemsSource, ModelParent, GetSourceItem) 从缓存中获取与给定_itemsSource 关联的默认CollectionView。因此,如果您在一个ComboBox 的底层ItemCollection 上设置过滤器,您实际上是在与ComboBox 绑定的ItemsSource 关联的缓存和共享CollectionView 上设置它。我猜 WPF 的人们没想到有人会这样做——不幸的是。但是我能做些什么呢?
我在 SO 上发现了两个类似的问题,但没有任何真正的答案:“Filtered Combobox ItemsSource binding issue”(似乎是关于自定义“FilteredComboBox”)和“Wpf ListBoxes' ItemsSource strange behaviour(涉及没有数据绑定的列表框)。
我还实现了一个相当丑陋的解决方案,我将在下面给出答案。但是,必须有更好的方法。
更新/澄清:我在最初的问题中没有清楚地表达自己。如果我们可以访问我们在数据绑定时绑定到的实体的实际实例,那么这个问题的答案是微不足道的。但是,我有一个复杂的应用程序,它可以从元数据“即时”生成表单(可以来自 C# 属性,也可以来自存储在数据库中的元数据,没关系)。在 SO 上提出的其他类似问题中,实际绑定源(数据上下文)始终可用,因此只需执行类似 Items = new ListCollectionView(...); 的操作(在代码中或在 XAML 中)是可能的。但是,如果实际的DataContext 只是基于元数据设置在应用程序生命周期的某个更远的地方呢?除了循环所有生成的 UI 元素并追溯更改绑定源之外,还有什么解决方案?我希望这个澄清是可以理解的。
【问题讨论】: