为什么要再次设置此 Animatable 属性?
Why is this Animatable property being set again?
跟进 个问题。
显然,出于某种原因,在明确设置 Parent.Child
属性(在构造函数内部或明确在构造函数外部)后,当我设置 Child.Trigger
属性 的 Parent.Child
对象,再次设置 Parent.Child
对象。这可以通过中断静态构造函数中定义的 _OnChildChanged
方法来观察。在调用它的第二个实例中,您可以看到 e.OldValue
不为空,并且它与 e.NewValue
.
相同
为什么在设置 Trigger
属性 时再次设置 Parent
的 Child
属性?
符合最低要求,Complete and Verifiable Example:
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( );
}
}
重现:
- 创建 WPF 项目。目标 .Net 4.7.2.
- Select
App.xaml
- 在
Properties
下,将 Build Action
更改为 Page
- 将代码粘贴到
App.xaml.cs
。覆盖一切。
我查看了 Animatable class 的实现。它继承自 freezable class 继承自 DependencyObject class.
在 freezable 中,它覆盖了 DependencyObject 中的 OnPropertyChanged 事件并调用所有处理程序是对 Freezable 类型的变化依赖性 属性 的响应。
这意味着当 Child class 中的任何依赖值发生变化时,将调用 Freezable class 中的 OnPropertyChanged 事件。 FireChanged() 也调用了。在 FireChange() 方法中,它会调用 GetChangeHandlersAndInvalidateSubProperties 来检查子 class 的所有更改,然后 _OnChildChanged 将在其任何依赖项 属性 发生更改时被调用。
可以参考Freezable的源码class here
此行为也记录在 Freezable Objects Overview 部分 创建您自己的 Freezable Class
(强调我的):
A class that derives from Freezable gains the following features.
Special states: a read-only (frozen) and a writable state.
Thread safety: a frozen Freezable can be shared across threads.
Detailed change notification: Unlike other DependencyObjects, Freezable objects provide change notifications when sub-property values change.
Easy cloning: the Freezable class has already implemented several methods that produce deep clones.
跟进
显然,出于某种原因,在明确设置 Parent.Child
属性(在构造函数内部或明确在构造函数外部)后,当我设置 Child.Trigger
属性 的 Parent.Child
对象,再次设置 Parent.Child
对象。这可以通过中断静态构造函数中定义的 _OnChildChanged
方法来观察。在调用它的第二个实例中,您可以看到 e.OldValue
不为空,并且它与 e.NewValue
.
为什么在设置 Trigger
属性 时再次设置 Parent
的 Child
属性?
符合最低要求,Complete and Verifiable Example:
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( );
}
}
重现:
- 创建 WPF 项目。目标 .Net 4.7.2.
- Select
App.xaml
- 在
Properties
下,将Build Action
更改为Page
- 将代码粘贴到
App.xaml.cs
。覆盖一切。
我查看了 Animatable class 的实现。它继承自 freezable class 继承自 DependencyObject class.
在 freezable 中,它覆盖了 DependencyObject 中的 OnPropertyChanged 事件并调用所有处理程序是对 Freezable 类型的变化依赖性 属性 的响应。
这意味着当 Child class 中的任何依赖值发生变化时,将调用 Freezable class 中的 OnPropertyChanged 事件。 FireChanged() 也调用了。在 FireChange() 方法中,它会调用 GetChangeHandlersAndInvalidateSubProperties 来检查子 class 的所有更改,然后 _OnChildChanged 将在其任何依赖项 属性 发生更改时被调用。
可以参考Freezable的源码class here
此行为也记录在 Freezable Objects Overview 部分 创建您自己的 Freezable Class (强调我的):
A class that derives from Freezable gains the following features.
Special states: a read-only (frozen) and a writable state.
Thread safety: a frozen Freezable can be shared across threads.
Detailed change notification: Unlike other DependencyObjects, Freezable objects provide change notifications when sub-property values change.
Easy cloning: the Freezable class has already implemented several methods that produce deep clones.