首页 > 解决方案 > 为什么要再次设置这个 Animatable 属性?

问题描述

跟进这个问题。

显然,由于某种原因,在显式设置Parent.Child属性(在构造函数内部或显式构造函数外部)之后,当我设置对象的Child.Trigger属性时Parent.ChildParent.Child对象又被设置了。这可以通过中断_OnChildChanged静态构造函数中定义的方法来观察。在它被调用的第二个实例中,您可以看到它e.OldValue不为空,并且它与e.NewValue.

为什么设置Child属性Parent时又设置了Trigger属性?

符合必要的最小、完整和可验证示例

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Animation;

namespace MCVE {
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class Program {
        [STAThread]
        public static int Main( ) {
            Parent p = new Parent( );
            p.Child.Trigger = new object( );
            return 0;
        }
    }

    public abstract class Base : Animatable {
        public static readonly DependencyProperty TriggerProperty;
        static Base( ) =>
            TriggerProperty = DependencyProperty.Register(
                "Trigger", typeof( object ), typeof( Base) );
        public object Trigger {
            get => this.GetValue( TriggerProperty );
            set => this.SetValue( TriggerProperty, value );
        }
    }
    public class Parent : Base {
        public static readonly DependencyProperty ChildProperty;

        static Parent( ) {
            ChildProperty = DependencyProperty.Register(
                "Child", typeof( Child ), typeof( Parent ),
                new PropertyMetadata( null as Child, _OnChildChanged ) );

            void _OnChildChanged(
                DependencyObject sender,
                DependencyPropertyChangedEventArgs e ) =>
                Console.WriteLine( "Child Changed!" );
        }

        public Parent( ) : base( ) =>
            this.Child = new Child( );


        public Child Child {
            get => this.GetValue( ChildProperty ) as Child;
            set => this.SetValue( ChildProperty, value );
        }

        protected override Freezable CreateInstanceCore( ) => new Parent( );
    }
    public class Child : Base {
        public Child( ) : base( ) { }
        protected override Freezable CreateInstanceCore( ) => new Child( );
    }
}

重现:

  1. 创建 WPF 项目。目标.Net 4.7.2。
  2. 选择App.xaml
  3. 在 下Properties,更改Build ActionPage
  4. 将代码粘贴到App.xaml.cs. 覆盖一切。

标签: c#wpfxamldependency-propertiesdependencyobject

解决方案


我看了一下Animatable类的实现。它继承自可冻结类,该类继承自DependencyObject类。

freezable内部,它覆盖了DependencyObject中的OnPropertyChanged事件并调用所有处理程序,这些处理程序响应于 Freezable 类型的不断变化的依赖项属性。

这意味着当 Child 类中的任何依赖值发生更改时,将调用 Freezable 类中的OnPropertyChanged事件。FireChanged () 也调用了。在FireChange()方法中,它将调用GetChangeHandlersAndInvalidateSubProperties来检查子类的所有更改,然后每当其依赖项属性发生更改时都会调用_OnChildChanged 。

您可以在此处参考 Freezable 类的源代码


此行为也记录在Freezable Objects Overview,部分创建您自己的 Freezable 类 (强调我的):

派生自 Freezable 的类具有以下特性。

  • 特殊状态:只读(冻结)和可写状态。

  • 线程安全:冻结的 Freezable 可以跨线程共享。

  • 详细的更改通知:与其他 DependencyObjects 不同,Freezable 对象在子属性值更改时提供更改通知

  • 轻松克隆:Freezable 类已经实现了几种产生深度克隆的方法。


推荐阅读