WPF:PropertyChangedCallback只触发一次

Son*_*oul 10 .net c# wpf dependency-properties propertychanged

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

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

XAML:

<cc:MyFilterList VisibleItems="{Binding CurrentTables}"  />
Run Code Online (Sandbox Code Playgroud)

CurrentTables是MyViewModel上的DependencyProperty.CurrentTables经常变化.我可以将另一个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); }
}
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

有些人质疑CurrentTables的修改方式,它会在更改时完全重新分配:

OnDBChange()...
CurrentTables = new List<string>(MainDatabaseDataAdapter.GetTables(this.SelectedServer, this.SelectedDatabase));
Run Code Online (Sandbox Code Playgroud)

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

UPDATE

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

TestFilterList.VisibleItems = new List<string>( Enumerable.Range(1, DateTime.Now.Second).ToList().Select(s => s.ToString()).ToList() );
Run Code Online (Sandbox Code Playgroud)

因此,它看起来像这个问题从的DependencyProperty(VisibleItems)茎看其他的DependencyProperty(CurrentTables).不知何故,绑定适用于第一次属性更改,但不适用于后续属性?正如你们中的一些人所建议的,试图用snoop检查这个问题.

Luk*_*ard 14

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

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

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

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

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

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

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

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

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

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


小智 5

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

我的WPF UserControl是

PatchRenderer
Run Code Online (Sandbox Code Playgroud)

我的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 */ }
    }
Run Code Online (Sandbox Code Playgroud)

我的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();
    }
Run Code Online (Sandbox Code Playgroud)