Follow up to this question.
Apparently, for some reason after having explicitly set the Parent.Child property ( either inside the constructor or explicitly outside of the constructor ), when I set the Child.Trigger property of the Parent.Child object, the Parent.Child object is being set yet again. This can be observed by breaking on the _OnChildChanged method defined within the static constructor. On the second instance of it being called, you can see that e.OldValue is not null, and that it is the same as e.NewValue.
WHY is the Child property of Parent being set yet again when setting the Trigger property?
In compliance with the requisite Minimal, 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( );
}
}
To reproduce:
- Create WPF Project. Target .Net 4.7.2.
- Select
App.xaml - Under
Properties, changeBuild ActiontoPage - Paste code into
App.xaml.cs. Overwrite EVERYTHING.
I took a look on implementation of the Animatable class. It is inherited from freezable class which is inherited from DependencyObject class.
Inside freezable, it overwrote OnPropertyChanged event from the DependencyObject and invoke all handlers which is response to a changing dependency property of type Freezable.
That mean when any dependency value in Child class has changed, OnPropertyChanged event in Freezable class will be called. And the FireChanged() has called also. Inside FireChange() method, it will call GetChangeHandlersAndInvalidateSubProperties to check all changed from sub class, and then the _OnChildChanged will be invoked whenever any its dependency property has changed.
You can refer source code of Freezable class here
This behavior is also documented in Freezable Objects Overview, section Creating Your Own Freezable Class (emphasis mine):