【问题标题】:Why is this Animatable property being set again?为什么要再次设置这个 Animatable 属性?
【发布时间】:2019-05-23 22:59:08
【问题描述】:

跟进this问题。

显然,由于某种原因,在显式设置 Parent.Child 属性(在构造函数内部或显式构造函数外部)之后,当我设置 Parent.Child 对象的 Child.Trigger 属性时,Parent.Child 对象再次设置。这可以通过破坏静态构造函数中定义的_OnChildChanged 方法来观察。在第二次调用它时,您可以看到e.OldValue 不为空,并且与e.NewValue 相同。

为什么在设置Trigger 属性时再次设置ParentChild 属性?

符合必要的 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( );
    }
}

复制:

  1. 创建 WPF 项目。目标 .Net 4.7.2。
  2. 选择App.xaml
  3. Properties 下,将Build Action 更改为Page
  4. 将代码粘贴到App.xaml.cs。覆盖所有内容。

【问题讨论】:

  • 作为说明,在Trigger属性的注册中使用typeof(Parent)是错误的,因为注册属性的类是Base。您也不需要用于注册的静态构造函数。写public static readonly DependencyProperty TriggerProperty = DependencyProperty.Register(nameof(Trigger), typeof(object), typeof(Base));
  • @Clemens:粗鲁。复制粘贴留下的工件。更新了代码。

标签: c# wpf xaml dependency-properties dependencyobject


【解决方案1】:

我查看了 Animatable 类的实现。它继承自 freezable 类,该类继承自 DependencyObject 类。

freezable 内部,它覆盖了 DependencyObject 中的 OnPropertyChanged 事件并调用所有处理程序,这些处理程序是对 Freezable 类型的不断变化的依赖项属性的响应。

这意味着当 Child 类中的任何依赖值发生变化时,将调用 Freezable 类中的 OnPropertyChanged 事件。 FireChanged() 也 调用了。在 FireChange() 方法中,它会调用 GetChangeHandlersAndInvalidateSubProperties 来检查子类的所有更改,然后只要有任何依赖,就会调用 _OnChildChanged属性已更改。

可以参考Freezable类的源码here


这种行为也记录在Freezable Objects OverviewCreating Your Own Freezable Class 部分(重点是我的):

派生自 Freezable 的类具有以下特性。

  • 特殊状态:只读(冻结)和可写状态。

  • 线程安全:冻结的 Freezable 可以跨线程共享。

  • 详细的更改通知:与其他 DependencyObject 不同,Freezable 对象提供子属性值更改时的更改通知

  • 轻松克隆:Freezable 类已经实现了几种产生深度克隆的方法。

【讨论】:

  • 我相信我明白了。因此,对Foo.Bar 中的Bar 的任何更改都会触发为Foo 中的BarProperty 定义的DependencyPropertyChanged 回调。很高兴知道并解释了我在项目中看到的许多行为。谢谢。
  • 您好,“所以对 Foo.Bar 中的 Bar 所做的任何更改都会触发为 Foo 中的 BarProperty 定义的 DependencyPropertyChanged 回调”,只有当您更改 Bar 中的任何依赖属性时,此选项才是正确的(Bar 中的正常属性将对 Foo.Bar 无效)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-22
  • 2023-01-08
  • 2021-10-19
  • 2021-02-22
  • 1970-01-01
相关资源
最近更新 更多