63815

Why is this Animatable property being set again?

Question:

Follow up to <a href="https://stackoverflow.com/questions/53918882/child-object-property-change-fires-parents-dependencypropertychanged-callback" rel="nofollow">this</a> 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, <a href="https://stackoverflow.com/help/mcve" rel="nofollow">Complete and Verifiable Example</a>:

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:

<ol><li>Create WPF Project. Target .Net 4.7.2.</li> <li>Select App.xaml</li> <li>Under Properties, change Build Action to Page</li> <li>Paste code into App.xaml.cs. Overwrite EVERYTHING.</li> </ol>

Answer1:

I took a look on implementation of the <strong>Animatable</strong> class. It is inherited from <strong>freezable</strong> class which is inherited from <strong>DependencyObject</strong> class.

Inside <strong>freezable</strong>, it overwrote <strong>OnPropertyChanged</strong> event from the <strong>DependencyObject</strong> 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, <strong>OnPropertyChanged</strong> event in Freezable class will be called. And the <strong>FireChanged() has</strong> called also. Inside <strong>FireChange()</strong> method, it will call <strong>GetChangeHandlersAndInvalidateSubProperties</strong> to check all changed from sub class, and then the <strong>_OnChildChanged</strong> will be invoked whenever any its dependency property has changed.

You can refer source code of Freezable class <a href="http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/wpf/src/Base/System/Windows/Freezable@cs/1305600/Freezable@cs" rel="nofollow">here</a>

<hr />

This behavior is also documented in <a href="https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/freezable-objects-overview" rel="nofollow">Freezable Objects Overview</a>, section <a href="https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/freezable-objects-overview#creating-your-own-freezable-class" rel="nofollow">Creating Your Own Freezable Class </a> (emphasis mine):

<blockquote>

A class that derives from Freezable gains the following features.

<ul><li>

Special states: a read-only (frozen) and a writable state.

</li> <li>

Thread safety: a frozen Freezable can be shared across threads.

</li> <li>

Detailed change notification: Unlike other DependencyObjects, Freezable objects provide <strong>change notifications when sub-property values change</strong>.

</li> <li>

Easy cloning: the Freezable class has already implemented several methods that produce deep clones.

</li> </ul></blockquote>

Recommend

  • Typical ormconfig.json file for Google Cloud SQL?
  • Show Path on GoogleMaps in Swift4
  • Classic ASP do while loop showing error
  • Make Tkinter Notebook be Draggable to Another View
  • Force localize image or image assets
  • cannot execute task : the task is already running
  • Install a personal firefox web extension permanently
  • How to get OEM USB driver for Samsung Galaxy SIII with model number GT-I9300
  • Single thread apartment issue
  • Android GoogleAPIClient - NoClassDefFoundError: com/google/android/gms/internal/zzsa
  • gradle processResources with different replacement and renaming rules
  • Running gauge tests from jar file
  • Python: what is the fastest way to map or compress calls and ignore errors?
  • Jsoncpp FastWriter right trim
  • ORA-02253: constraint specification not allowed here
  • PhoneGap FileReader/readAsDataURL Not Triggering Callbacks
  • Problems installing Common Lisp Sketch into Windows 10
  • Keyboard Extension Crash on Device
  • AppEngine Paypal integration giving SSLCertificateError on localhost, using Python
  • Transfer file trough SSH tunnel with Java
  • how to transform dataframe that contains list in every row of each column
  • Unicode File IO in Codename One
  • Windows 8 Flexboxes - Nesting Flexboxes with overflow enabled
  • apache zeppelin is started but there is connection error in localhost:8080
  • device tree overlay phandle
  • How do you run a synchronous timer in C#?
  • Protractor Page objects - TypeError: Object # has no method 'methodName'
  • Windows biometric framework sample umdf driver: This device cannot start. (Code 10)
  • Multiplying polynomials/simplifying like terms
  • reshape/remould data frame to create normalized bar chart and pie chart
  • Google App Engine Datastore: Dealing with eventual consistency
  • Codeigniniter insert data through models and controller
  • Sign a Pdf using custom digital signature in Java
  • Accessing Arguments, Workflow Variables from custom activities
  • How do I use TagLib-Sharp to write custom (PRIV) ID3 frames?
  • CAS 4 - Not able to retrieve the LDAP groups after successful authentication
  • ReferenceError: TextEncoder is not defined
  • JavaScript RegExp Replace