【发布时间】:2014-09-29 06:52:50
【问题描述】:
最初的问题是不知道为什么PropertyChangedCallback 没有触发,它是由一些代码拼写错误引起的(对于 SO 来说过于本地化)。但是,我已经修改了问题以解决如何在自定义控件具有添加/删除的子项或子项具有更改的属性时重新呈现自定义控件。 请visit my answer深入了解如何触发重新渲染。
标题可能不是对问题的最佳描述,但是问题本身无论如何都不是很清楚。我认为发布这个问题可能会有一些用处,因为 PropertyChangedCallback 的触发非常不可预测。
我正在创作的自定义控件具有以下结构:
-
MyControl : FrameworkElement-
MyControl.Items-
MyItem : FrameworkContentElement-
MyItem.SubItemsMySubItem : FrameworkContentElementMySubItem- ....
-
MyItemMyItem- ....
-
-
所以基本上,我的控件有一个DependencyProperty,它存储一个ObservableCollection<MyItem>。
这个DependencyProperty 有一个PropertyChangedCallback,用于检测集合何时设置/取消设置。此回调函数用于订阅 CollectionChanged 事件,以便在从集合中添加/删除 MyItem 时重新呈现我的控件。
当集合项具有更改的属性时,我的控件也应该重新呈现。因此,MyItem 也实现了INotifyPropertyChanged,当MyItem 对象被添加/删除到集合中时,我订阅了PropertyChangedEventHandler。
到目前为止,一切都很好......
MyItem 类定义了更多的DependencyProperies,每个都有相同的PropertyChangedCallback 函数。 但令我惊讶的是,当我在 XAML 中修改 MyItem 属性之一时,并未触发此回调函数。
我希望了解的是为什么会发生这种情况以及为什么 PropertyChangedCallback 没有触发。另外,我想知道什么场景会导致回调触发。
我的目标是让控件在以下情况下重新呈现:
a) 其属性已更改
b) 添加/删除其子项
c) 其子项的属性已更改
d) 其子项的子项已添加/删除
e) 其子项的子项属性已更改.
代码示例
这是我注册MyControl.Items 属性的方法。此DependencyProperty 在触发属性更改事件时成功。
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register(
"Items",
typeof(ObservableCollection<MyItem>),
typeof(MyControl),
new FrameworkPropertyMetadata(
null, //Default to null. Instance-scope value is set in constructor.
FrameworkPropertyMetadataOptions.AffectsRender,
OnItemsPropertyChanged));
这是我对设置/取消设置 MyItems 集合的反应:
private static void OnItemsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{ //This callback is called when ObservableCollection<MyItem> is set/unset.
MyControl ctrl = (MyControl)obj;
INotifyCollectionChanged oldList = args.OldValue as INotifyCollectionChanged;
INotifyCollectionChanged newList = args.NewValue as INotifyCollectionChanged;
//If the old list implements the INotifyCollectionChanged interface, then unsubscribe to CollectionChanged events.
if (oldList != null)
oldList.CollectionChanged -= ctrl .OnItemsCollectionChanged;
//If the new list implements the INotifyCollectionChanged interface, then subscribe to CollectionChanged events.
if (newList != null)
newList.CollectionChanged += ctrl .OnItemsCollectionChanged;
}
在ObservableCollection<MyItem> 中添加或删除项目时调用以下函数
private void OnItemsCollectionChanged(object source, NotifyCollectionChangedEventArgs args)
{ //Invaliate the visual, causing it to re-layout and re-render.
InvalidateVisual();
//The contents of the Items collection was modified.
//Subscribe/Unsubcribe to the PropertyChanged event as necessary.
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (MyItem mi in args.NewItems)
mi.PropertyChanged += OnMyItemPropertyChanged;
break;
case NotifyCollectionChangedAction.Remove:
foreach (MyItem mi in args.OldItems)
mi.PropertyChanged -= OnMyItemPropertyChanged;
break;
case NotifyCollectionChangedAction.Replace:
foreach (MyItem mi in args.NewItems)
mi.PropertyChanged += OnMyItemPropertyChanged;
foreach (MyItem mi in args.OldItems)
mi.PropertyChanged -= OnMyItemPropertyChanged;
break;
case NotifyCollectionChangedAction.Reset:
foreach (MyItem mi in (source as IEnumerable<MyItem >))
mi.PropertyChanged += OnMyItemPropertyChanged;
break;
}
}
现在,当MyItem 的属性发生变化时,我需要能够做出反应。
因此,我为MyItem 有PropertyChanged 事件建立了一个回调函数:
private void OnMyItemPropertyChanged(object source, PropertyChangedEventArgs args)
{ //One of the MyItems had a property that was changed,
//invalidate the visual and re-render.
InvalidateVisual();
}
之前的函数永远不会被调用,因为MyItem's DependencyProperties 永远不会触发属性更改事件。下面说明了我如何设置我尝试在 XAML 中修改的DependencyProperties:
public static readonly DependencyProperty MyIntProperty = DependencyProperty.Register(
"MyInt",
typeof(int),
typeof(MyItem),
new PropertyMetadata(0, DependencyPropertyChanged));
public static readonly DependencyProperty MyDoubleProperty = DependencyProperty.Register(
"MyDouble",
typeof(double),
typeof(MyItem),
new PropertyMetadata(0d, DependencyPropertyChanged));
public static readonly DependencyProperty MyStringProperty = DependencyProperty.Register(
"MyString",
typeof(string),
typeof(MyItem),
new PropertyMetadata("", DependencyPropertyChanged));
以下函数是那些DependencyProperties 的回调。如果被解雇,它应该引发INotifyPropertyChanged.PropertyChangedEventHandler:
private static void DependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
MyItem item= (MyItem )obj;
item.RaisePropertyChanged(args.Property.Name);
}
protected void RaisePropertyChanged(string name)
{ //Notify listeners (such as the parent control) when a property changes.
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));;
}
嗯...我有一组类似的事件/处理程序,用于MyItem.SubItems DependencyProperty,但目前没有任何用处。
如果您能够就PropertyChangedCallback 的工作原理添加任何见解,我将不胜感激。感谢您阅读这篇相当长的帖子。
【问题讨论】:
-
MyItem中有依赖属性的原因是什么?难道不是标准的 CLR 属性在其设置器中引发 PropertyChanged 事件吗? -
好吧。主要原因是为了让 MyItem 可以接受绑定系统。另一方面,MySubItem 只是一小段数据,因此它只能具有 CLR 属性。 MyControl 是 Graph,MyItem 是 GraphLine,MySubItem 是 DataPoint。 GraphLine 有几个属性可以控制它和它的数据点如何以图形方式显示。也许这并不一定需要数据绑定,但 ListBoxItem 与 GraphLine 的目的相似。
-
@Clemens,显然,我使用的代码没有问题。我的项目有很多错别字,因此问题过于本地化。我已经发布了关于如何执行 OP 中描述的目标的深入教程。不过感谢您的建议;)
标签: c# wpf xaml wpf-controls dependency-properties