Каковы различия между PropertyMetaData, UIPropertyMetadata и FrameworkMetaData в WPF

Я знаю основную разницу между этими классами, которую PropertyMetadata используется при резервном свойстве, UIPropertyMetadata, когда мы хотим иметь поддержку для анимации, и свойства FrameworkMetadata for Framework, которые будут использоваться в пользовательских элементах управления.

Но я понимаю только теоретическую часть. Это будет отличная идея, если вы объясните простейший пример, который использует все эти 3 класса в 3 различных свойствах зависимости, четко различая их.

Спасибо заранее.

Ответ 1

Источник: PropertyMetadata и FrameworkPropertyMetadata

Когда вы реализуете настраиваемое свойство зависимостей, и вы регистрируете свойство, вызывая DependencyProperty.Register, вы указываете некоторые метаданные для свойства, передавая его экземпляр PropertyMetadata. Это может быть экземпляр класса PropertyMetadata или экземпляр одного из его подклассов. Различия показаны ниже.

PropertyMetadata - Основные метаданные, относящиеся к свойствам зависимостей

  • CoerceValueCallback - принуждение значения при установке
  • DefaultValue - значение по умолчанию для свойства
  • PropertyChangedCallback - ответьте на новое эффективное значение для свойства

UIPropertyMetadata - происходит от PropertyMetadata и добавляет:

  • IsAnimationProhibited - отключить анимацию для этого свойства?

FrameworkPropertyMetadata - происходит от UIPropertyMetadata и добавляет:

  • AffectsArrange, AffectsMeasure, AffectsParentArrange, AffectsParentMeasure, AffectsRender - Должны ли повторные запуска вычислений компоновки после изменения значения свойства?
  • BindsTwoWayByDefault, DefaultUpdateSourceTrigger, IsDataBindingAllowed, IsNotDataBindable - Диктует, как свойство участвует в привязке данных
  • Inherits, OverridesInheritanceBehavior - Работает ли наследование для этого свойства?
  • Journal - сохранить это значение при ведении журнала?
  • SubPropertiesDoNotAffectRender - Проверьте свойства этого объекта при изменении макета?

Ответ 2

Важным практическим различием между PropertyMetadata и FrameworkPropertyMetadata является то, что последний позволяет указать набор FrameworkPropertyMetadataOptions.

Например, указание FrameworkPropertyMetadataOptions.AffectsRender заботится о том, чтобы инициировать повторный рендеринг элемента UIE, для которого изменилось свойство. Без этого флага вам придется делать это вручную в PropertyChangedCallback.

Ответ 3

Все поведения, предоставляемые FrameworkPropertyMetadata и UIPropertyMetadata, управляются битами флага, которые записываются в одном поле enum (32-bit uint), называемом _flags которое объявлено в базовом классе PropertyMetadata, даже при том, что ни один из флагов на самом деле публично не предоставляется из там. Вот объявление этого enum:

internal enum MetadataFlags : uint
{
 DefaultValueModifiedID                 /**/= 0b_00000000_00000000_00000000_00000001, //0x00000001
 SealedID                               /**/= 0b_00000000_00000000_00000000_00000010, //0x00000002
 Inherited                              /**/= 0b_00000000_00000000_00000000_00010000, //0x00000010
 UI_IsAnimationProhibitedID             /**/= 0b_00000000_00000000_00000000_00100000, //0x00000020
 FW_AffectsMeasureID                    /**/= 0b_00000000_00000000_00000000_01000000, //0x00000040
 FW_AffectsArrangeID                    /**/= 0b_00000000_00000000_00000000_10000000, //0x00000080
 FW_AffectsParentMeasureID              /**/= 0b_00000000_00000000_00000001_00000000, //0x00000100
 FW_AffectsParentArrangeID              /**/= 0b_00000000_00000000_00000010_00000000, //0x00000200
 FW_AffectsRenderID                     /**/= 0b_00000000_00000000_00000100_00000000, //0x00000400
 FW_OverridesInheritanceBehaviorID      /**/= 0b_00000000_00000000_00001000_00000000, //0x00000800
 FW_IsNotDataBindableID                 /**/= 0b_00000000_00000000_00010000_00000000, //0x00001000
 FW_BindsTwoWayByDefaultID              /**/= 0b_00000000_00000000_00100000_00000000, //0x00002000
 FW_ShouldBeJournaledID                 /**/= 0b_00000000_00000000_01000000_00000000, //0x00004000
 FW_SubPropertiesDoNotAffectRenderID    /**/= 0b_00000000_00000000_10000000_00000000, //0x00008000
 FW_SubPropertiesDoNotAffectRenderModifiedID= 0b_00000000_00000001_00000000_00000000, //0x00010000
 FW_InheritsModifiedID                  /**/= 0b_00000000_00010000_00000000_00000000, //0x00100000
 FW_OverridesInheritanceBehaviorModifiedID  = 0b_00000000_00100000_00000000_00000000, //0x00200000
 FW_ShouldBeJournaledModifiedID         /**/= 0b_00000001_00000000_00000000_00000000, //0x01000000
 FW_UpdatesSourceOnLostFocusByDefaultID /**/= 0b_00000010_00000000_00000000_00000000, //0x02000000
 FW_DefaultUpdateSourceTriggerModifiedID/**/= 0b_00000100_00000000_00000000_00000000, //0x04000000
 FW_ReadOnlyID                          /**/= 0b_00001000_00000000_00000000_00000000, //0x08000000
 FW_DefaultUpdateSourceTriggerEnumBit1  /**/= 0b_01000000_00000000_00000000_00000000, //0x40000000
 FW_DefaultUpdateSourceTriggerEnumBit2  /**/= 0b_10000000_00000000_00000000_00000000, //0x80000000
};

Обратите также внимание на то, как взаимодействуют следующие три свойства, все объявленные FrameworkPropertyMetadata. То есть IsDataBindingAllowed - это не то же самое, что !IsNotDataBindable; первый добавляет дополнительное ограничение, исключающее использование неправильного направления привязки для свойств "только для чтения".

private bool ReadOnly => (_flags & FW_ReadOnlyID) != 0;

public bool IsDataBindingAllowed =>
                     (_flags & FW_IsNotDataBindableID) == 0 && !this.ReadOnly;

public bool IsNotDataBindable => (_flags & FW_IsNotDataBindableID) != 0;



[edit:] ALERT/WARNING: По какой-то неизвестной причине флаги, показанные выше, не разделяют значения, указанные в соответствующем флаге FrameworkPropertyMetadataOptions !
[Flags]
public enum FrameworkPropertyMetadataOptions
{                                  //         FPMO            MetadataFlags
                                   //     ----------           ----------
                                   //                          0x00000010 ←┐
    None                           /**/ = 0x00000000, //                   │
    AffectsMeasure                 /**/ = 0x00000001, //  << 6 0x00000040  │
    AffectsArrange                 /**/ = 0x00000002, //  << 6 0x00000080  │
    AffectsParentMeasure           /**/ = 0x00000004, //  << 6 0x00000100  │
    AffectsParentArrange           /**/ = 0x00000008, //  << 6 0x00000200  │
    AffectsRender                  /**/ = 0x00000010, //  << 6 0x00000400  │
    Inherits                       /**/ = 0x00000020, //  >> 1   →  →  ────┘
    OverridesInheritanceBehavior   /**/ = 0x00000040, //  << 5 0x00000800
    NotDataBindable                /**/ = 0x00000080, //  << 5 0x00001000
    BindsTwoWayByDefault           /**/ = 0x00000100, //  << 5 0x00002000
    Journal                        /**/ = 0x00000400, //  << 4 0x00004000
    SubPropertiesDoNotAffectRender /**/ = 0x00000800, //  << 4 0x00008000
};