【问题标题】:DependencyObject that also implements INotifyPropertyChanged?也实现 INotifyPropertyChanged 的​​ DependencyObject?
【发布时间】:2014-07-30 06:42:41
【问题描述】:

对于 Silverlight 和 WPF 应用程序,我有一个自定义控件,其中包含一个 ObservableCollection 作为依赖项属性。该控件的一个元素 Border 需要根据 ObservableCollection 中项目的组成来改变颜色。

例如,假设集合是动物、蔬菜和矿物,名为ObjectList。如果至少有一只动物,我希望边框是红色的;如果没有动物,但至少有一种蔬菜,它是绿色的;否则集合中只有矿物,所以会显示为蓝色。

我创建了一个转换器,它可以获取集合并确定颜色,所以有一个像这样的绑定:

<Border Background="{Binding ObjectList, 
                     RelativeSource={RelativeSource Self}, 
                     Converter={StaticResource MyColorConverter}}" />

挑战在于,当从ObjectList 添加/删除项目时,我需要触发对背景颜色的重新评估;但是,ObjectList 本身并没有改变。我想我有三个选项,但不确定哪个可能是最佳做法:

  1. 每次添加或删除对象时创建一个新集合。这看起来很严厉,但会导致 ObjectList 被更改,从而触发后台更新。

  2. 在ObjectList 的CollectionChanged 回调中为背景属性调用UpdateTarget。由于 UpdateTarget 不适用于 Silverlight,我只是删除并重新添加绑定 - 再次有点笨拙。

  3. 在我的自定义控件上实现 INotifyPropertyChanged 并在 ObjectList 的实现中调用 PropertyChanged CollectionChanged

我最喜欢 3,但事实上我有一个也实现 INPC 的 DependencyObject 似乎很奇怪。是吗?有没有更优雅的方法?

【问题讨论】:

  • 第 3 点是一个轻量级解决方案,在这三个解决方案中,它之所以引人注目。对于我自己,我会将背景绑定到您控件中的附加 DP,然后根据 ObjectList 的更改对其进行更新。由于 ObservableCollection 继承自 IList,因此底层的 ListCollectionView 非常有用。
  • 我实际上忽略了那个,我正在使用一个额外的 DP,但它看起来就像另一个(冗余)移动部件,仍然需要以某种方式“改变”
  • 当您的集合变大时,您可能不会意识到更新列表属性对性能的影响。此外,如果列表在 Animal 上被过滤,并且您添加了新的 Mineral,那么您将迭代整个列表。所以它不一定是多余的,这取决于你希望你的控制有多复杂。

标签: c# wpf silverlight inotifypropertychanged dependencyobject


【解决方案1】:

有a way of doing this recommended by the MSDN documentation(向下滚动到使用 VisualStateManager 的最佳实践;它是为完整的 .Net 编写的,但本部分也非常适合 Silverlight)。 每当您的VisualStates 依赖于您的自定义Control 的属性/状态时,建议为每个影响VisualState 的属性设置一个ChangedHandler,并从那里调用一个私有UpdateVisualStates 方法。评估您的条件并在此方法中以编程方式设置 VisualStates。

即使您不使用VisualStates 进行颜色更改,我也建议您遵循相同的模式。

为简洁起见,以下代码不完整:

public ObservableCollection ObjectList {...}
public static readonly DependencyProperty ObjectListProperty =
    DependencyProperty.Register(...OnObjectListChanged...);

private static void OnObjectListChanged(...)
{ObjectList.CollectionChanged += OnObjectListCollectionChanged;}

private void OnObjectListCollectionChanged(...){ UpdateVisualStates(); }

private void UpdateVisualStates()
{
    //actually you have to instatiate a SolidColorBrush here
    if (ContainsAtLeastOneAnimal()) { m_border.Background = Colors.Red; }
    else if (ContainsAtLeastOneVegetable()) {m_border.Background = Colors.Green;}
    else { m_border.Background = Colors.Blue; }
}

如果您不想引用边框,请随意引入 DependencyProperty BorderColor 并从您的 xaml 绑定到它。没关系。拥有另一个移动部件确实没有问题。这比模拟整个 ObjectList 实例更改要好得多。

【讨论】:

  • 感谢您的文档指针和示例。看起来您实际上已将我的转换器移动到特定于控件的代码(引用边框),但正如您所说,添加另一个依赖属性可以缓解这种情况。对我来说,另一个属性似乎仍然没有必要,而且恕我直言会降低可维护性,因为我需要确保对象是同步的。有点像使用“计数”变量而不是使用 list.Count。
  • @JimO'Neil:我能想到一个更合你口味的替代方案:你可以写一个BackgroundColorBehavior 并将它附加到Border 元素上。让行为具有 INotifyCollectionChanged 类型的 DependencyProperty(绑定 ObjectList)并将转换器代码拉入您的新行为。所以你不必乱扔控件的代码。
猜你喜欢
  • 2010-10-04
  • 1970-01-01
  • 2014-06-02
  • 1970-01-01
  • 1970-01-01
  • 2014-09-30
  • 2011-04-08
  • 2021-06-29
  • 2012-04-05
相关资源
最近更新 更多