【问题标题】:WPF: PropertyChangedCallback triggered only onceWPF:PropertyChangedCallback 仅触发一次
【发布时间】:2011-08-13 08:07:15
【问题描述】:

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

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

XAML:

<cc:MyFilterList VisibleItems="{Binding CurrentTables}"  />

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); }
}

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

更新

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

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

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

更新

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

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

因此,问题似乎源于 DependencyProperty (VisibleItems) 监视另一个 DependencyProperty (CurrentTables)。不知何故,绑定适用于第一次属性更改,但不适用于后续更改? 正如你们中的一些人建议的那样,尝试用 snoop 来检查这个问题。

【问题讨论】:

  • 您确定绑定保持不变吗?我会使用 Snoop 来验证绑定是否正确更新了 VisibleItems 属性。
  • 您暗示但不明确:您确定 VisibleItems 实际上更改为不同的值而没有调用属性更改回调吗?
  • Rick,是的,我敢肯定,我可以单步执行它并查看它是否被调用,现在我在每次调用时创建一个“新列表”以确保它不是同一个实例。而且如果你看一下代码,你会发现它每次都从数据库中获取列表。
  • 证明 CurrentTables 正在改变并不能证明 VisibleItems 正在改变。您的绑定可能已损坏或其他原因。我怀疑 VisibleItems 实际上并没有改变,这就是为什么我建议使用 Snoop 来确保绑定正常工作。
  • 克莱默,继续。我做了一个不同的测试,它确实似乎问题出在 VisibleItems 和 CurrentTables 之间……需要做一些调查,看看为什么它们在第一次尝试时起作用,但在随后却不起作用……

标签: c# .net wpf dependency-properties propertychanged


【解决方案1】:

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

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

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

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

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

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

  • 依赖属性接收“本地”值。
  • 依赖属性机制沿绑定向源属性发送值“向后”,
  • 源属性设置属性值并触发PropertyChanged
  • 依赖属性机制接收到PropertyChanged事件,检查源属性的新值,发现它没有改变,不再做任何事情。

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

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

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

【讨论】:

  • 不,不在事件中设置 VisibleItems。该事件执行翻译并在我的用户控件上设置另一个属性。
  • @Luke Woodward:一千次感谢!你的帖子至少让我免了一天的调试时间。
【解决方案2】:

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

我的 WPF 用户控件是

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();
    }

【讨论】:

    【解决方案3】:

    您可能会遇到集合的内容正在更改但实际实例没有更改的问题。在这种情况下,您需要使用 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;
            }
        }
    

    【讨论】:

    • 集合被反复重新分配。
    • 您能否在原始问题中发布更多代码来显示这一点?
    【解决方案4】:

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

    var control = new MyFilterList();
    control.VisibleItems = new List<string>();
    control.VisibleItems = new List<string>();
    

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

    【讨论】:

    • 正确。我确实直接分配了该属性,并且每次都有效..所以问题似乎源于一个 dep 属性监视另一个.. 不知何故,更改通知只发生一次...
    猜你喜欢
    • 2020-08-25
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2018-09-27
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多