WPF:PropertyChangedCallback只触发一次

我有一个用户控件,它公开了一个名为VisibileItems的DependencyProperty。每次该属性更新时,我都需要触发另一个事件。 为此,我添加了一个带有PropertyChangedCallback事件的FrameworkPropertyMetadata。

由于某种原因,此事件仅被调用一次,并且在下次VisibleItems更改时不会触发。

XAML:

 

CurrentTables是MyViewModel上的DependencyProperty。 CurrentTable经常变化。 我可以将另一个WPF控件绑定到CurrentTables,我看到UI中的更改。

这是我使用PropertyChangedCallback连接VisibleItems的方式

 public static readonly DependencyProperty VisibleItemsProperty = DependencyProperty.Register( "VisibleItems", typeof(IList), typeof(MyFilterList), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisiblePropertyChanged)) ); public IList VisibleItems { get { return (IList)GetValue(VisibleItemsProperty); } set { SetValue(VisibleItemsProperty, value); } } 

通过单步进入VisiblePropertyChanged,我可以看到第一次设置CurrentTables时会触发它。 但不是随后的时间。

UPDATE

当你们中的一些人质疑CurrentTables的修改方式时,它会在更改时完全重新分配:

 OnDBChange()... CurrentTables = new List(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase)); 

每次更改都会调用此行,但我的VisiblePropertyChanged处理程序仅在第一次调用时才会被调用。

UPDATE

如果我直接分配VisibleItems,每次都会调用处理程序!

 TestFilterList.VisibleItems = new List( Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList() ); 

所以,看起来问题源于DependencyProperty(VisibleItems)观看另一个DependencyProperty(CurrentTables)。 不知何故,绑定适用于第一次属性更改,但不适用于后续属性? 正如你们中的一些人建议的那样,试图用snoop检查这个问题。

您是否将“本地”值(即直接分配给依赖项属性设置器)设置为依赖属性,该属性也具有OneWay绑定? 如果是这样,设置本地值将删除绑定,如MSDN依赖项属性概述中所述:

绑定被视为本地值,这意味着如果您设置另一个本地值,您将消除绑定。

当要求在依赖项属性上存储本地值时,依赖项属性机制没有太多其他function。 它不能通过绑定发送值,因为绑定’指向’错误的方式。 设置为本地值后,它不再显示从绑定中获得的值。 由于它不再显示绑定的值,因此它会删除绑定。

一旦绑定消失,当绑定的source属性更改其值时,将不再调用PropertyChangedCallback 。 这可能就是没有调用回调的原因。

如果将绑定设置为TwoWay ,则绑定系统确实可以存储您设置的“本地”值:在绑定的source属性中。 在这种情况下,不需要消除绑定,因为依赖项属性机制可以将值存储在source属性中。

这种情况不会导致堆栈溢出,因为发生以下情况:

  • 依赖属性接收“本地”值。
  • 依赖属性机制沿着源属性绑定发送值“向后”,
  • Source属性设置属性值并触发PropertyChanged
  • 依赖属性机制接收PropertyChanged事件,检查source属性的新值,发现它没有更改,并且不做任何进一步的操作。

这里的关键点是,如果为其值未更改的属性触发PropertyChanged事件,则不会调用绑定到您的属性的依赖项属性上的任何PropertyChangedCallback

为简单起见,我在上面忽略了IValueConverter 。 如果您有转换器,请确保它正确地在两个方向上转换值。 我还假设另一端的属性是实现INotifyPropertyChanged的对象的视图模型属性。 在绑定的源端可能有另一个依赖属性。 依赖属性机制也可以处理它。

碰巧,WPF(和Silverlight)不包含堆栈溢出的检测。 如果在PropertyChangedCallback ,您将依赖项属性的值设置为与其新值不同(例如,通过递增整数值属性或将字符串附加到字符串值属性),您将获得堆栈溢出。

我的代码中有同样的问题,Luke是对的。 我在PropertyChangedCallback中通过mystake调用了SetValue,导致潜在的无限循环。 WPF防止这种静默禁用回调!!

我的WPF UserControl是

 PatchRenderer 

我的C#属性是注意:

  [Description("Note displayed with star icons"), Category("Data"), Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public int Note { get { return (int)GetValue(NoteProperty); } set { SetValue(NoteProperty, value); /* don't put anything more here */ } } 

我的WPF属性

 public static readonly DependencyProperty NoteProperty = DependencyProperty.Register("Note", typeof(int), typeof(PatchRenderer), new PropertyMetadata( new PropertyChangedCallback(PatchRenderer.onNoteChanged) )); private static void onNoteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { // this is the bug: calling the setter in the callback //((PatchRenderer)d).Note = (int)e.NewValue; // the following was wrongly placed in the Note setter. // it make sence to put it here. // this method is intended to display stars icons // to represent the Note ((PatchRenderer)d).UpdateNoteIcons(); } 

如果您只是实例化MyFilterList并通过以下代码设置VisibleItems

 var control = new MyFilterList(); control.VisibleItems = new List(); control.VisibleItems = new List(); 

您可能会每次都看到PropertyChangedCallback发生。 意思是,问题在于绑定,而不是回调。 确保你没有绑定错误,你正在提出PropertyChanged ,并且你没有破坏绑定(例如通过在代码中设置VisibleItems

您可能遇到集合内容正在发生变化而不是实际实例的问题。 在这种情况下,您将要使用ObservableCollection并执行以下操作:

 public static readonly DependencyProperty VisibleItemsProperty = DependencyProperty.Register( "VisibleItems", typeof(IList), typeof(MyFilterList), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(VisibleItemsChanged))); private static void VisibleItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var myList = d as MyFilterList; if (myList == null) return; myList.OnVisibleItemsChanged(e.NewValue as IList, e.OldValue as IList); } protected virtual void OnVisibleItemsChanged(IList newValue, IList oldValue) { var oldCollection = oldValue as INotifyCollectionChanged; if (oldCollection != null) { oldCollection.CollectionChanged -= VisibleItems_CollectionChanged; } var newCollection = newValue as INotifyCollectionChanged; if (newCollection != null) { newCollection.CollectionChanged += VisibleItems_CollectionChanged; } }