Почему это свойство Animatable устанавливается снова?

Продолжайте до этого вопроса.

Видимо, по какой-то причине после того, как Parent.Child свойство (либо внутри конструктора, либо явно вне конструктора), когда я устанавливаю Child.Trigger собственность Parent.Child объект, Parent.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. Target .Net 4.7.2.
  2. Выбрать App.xaml
  3. Под Properties, менять Build Action в Page
  4. Вставьте код в App.xaml.cs, Перезаписать ВСЕ.

1 ответ

Решение

Я посмотрел на реализацию класса Animatable. Он наследуется от класса freezable, который наследуется от класса DependencyObject.

Внутри freezable он перезаписывает событие OnPropertyChanged из DependencyObject и вызывает все обработчики, которые являются ответом на изменяющееся свойство зависимости типа Freezable.

Это означает, что при изменении любого значения зависимости в дочернем классе будет вызвано событие OnPropertyChanged в классе Freezable. И вызвал также FireChanged(). Внутри метода FireChange() он будет вызывать GetChangeHandlersAndInvalidateSubProperties, чтобы проверить все изменения из подкласса, а затем _OnChildChanged будет вызываться всякий раз, когда изменяется любое его свойство зависимости.

Вы можете ссылаться на исходный код класса Freezable здесь


Это поведение также задокументировано в обзоре Freezable Objects, раздел Создание собственного класса Freezable (выделено мной):

Класс, производный от Freezable, получает следующие возможности.

  • Особые состояния: доступное только для чтения (замороженное) и доступное для записи состояние.

  • Безопасность потоков: замороженный Freezable может быть разделен между потоками.

  • Подробное уведомление об изменении: в отличие от других объектов DependencyObject, объекты Freezable предоставляют уведомления об изменениях при изменении значений под-свойств.

  • Простое клонирование: класс Freezable уже реализовал несколько методов, которые производят глубокие клоны.

Другие вопросы по тегам