0

In defining a base class inheriting an the Animatable, class, I have discovered what I am finding to be some odd behavior.

When I create a Child DependencyProperty within a 'Parent' class, then define an instance of that 'Parent' class, then alter a property on the parents child, the PropertyChangedCallback I defined for the Parents Child property fires.

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:

  1. Create WPF Project. Target .Net 4.7.2.
  2. Select App.xaml
  3. Under Properties, change Build Action to Page
  4. Paste code into App.xaml.cs. Overwrite EVERYTHING.

Running this code, you should see the message print twice within the console.

Why is this happening? Is there a way to STOP it from happening?

Follow Up Here:

Will
  • 3,413
  • 7
  • 50
  • 107
  • Hi, I tried to run your code, and it threw System.TypeInitializationException exception as this line of code: "ChildProperty = DependencyProperty.Register( "Child", typeof( Child ), typeof( Parent ), new PropertyMetadata( null as Child, _OnChildChanged ) );" – Nhan Phan Dec 25 '18 at 02:57
  • @NhanPhan Did you create the project as a console application, or as a WPF application. It needs to be a WPF application. – Will Dec 25 '18 at 03:19
  • As a note, writing `null as Child` is redundant for two reasons. First, `null` is assignable to every reference type, second the type of the default value argument of the PropertyMetadata constructor is `object`. – Clemens Dec 25 '18 at 08:22
  • @Clemens That's fair. It's just my own personal preference as I have some utility methods for registering dependency properties, one expecting a `default` value and one expecting a `PropertyMetaData`, so it tends to get upset if I don't explicitly provide the property type by `null as Foo`. – Will Dec 25 '18 at 16:08

1 Answers1

1

The message print 2 times in console because Child property was set 2 times:

  • 1st time in Parent constructor( this.Child = new Child( );)
  • 2nd time when you call p.Child.Trigger = new object( );

I think in your case, you can compare new value and old value of Child property in _OnChildChanged() to prevent the problem:

            void _OnChildChanged(
                DependencyObject sender,
                DependencyPropertyChangedEventArgs e)
            {
                if (e.NewValue != e.OldValue)
                {
                    Console.WriteLine("Child Changed!");
                }
            }

Regards!

Nhan Phan
  • 1,262
  • 1
  • 14
  • 32
  • wonderful. so why is the property being set the second time without my having explicitly done so? Guess that will need to go into a new question... – Will Dec 25 '18 at 03:36