Поделиться через


Загрузка кода XAML и свойства зависимостей (WPF .NET)

В реализации Windows Presentation Foundation (WPF) процессора XAML изначально учитывается свойство зависимостей. Таким образом, процессор XAML использует системные методы свойств WPF для загрузки кода XAML и обработки атрибутов свойств зависимостей и полностью обходит оболочки свойств зависимостей с помощью системных методов свойств WPF, таких как GetValue и SetValue. Итак, если добавить пользовательскую логику в оболочку своего настраиваемого свойства зависимостей, она не вызывается процессором XAML, если значение свойства задано в XAML.

Важно!

Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.

Необходимые компоненты

Для понимания статьи нужно иметь базовые знания о свойствах зависимостей и прочитать Общие сведения о свойствах зависимостей. Чтобы понимать примеры в этой статье полезно познакомиться с языком XAML и узнать, как создавать приложения WPF.

Производительность загрузчика XAML WPF

Для процессора XAML WPF вычислительно дешевле прямым вызовом метода SetValue задать значение свойству зависимостей, чем использовать оболочку свойства зависимостей.

Если процесс XAML использовал оболочку свойства, потребуется вывести всю объектную модель резервного кода только на основе типа и связей между членами, указанных в разметке. Несмотря на то, что тип можно определить из разметки с помощью сочетания xmlns и атрибутов сборки, идентификация членов и определение тех из них, которые можно задать в качестве атрибута, и разрешение поддерживаемых типов значений свойств, потребуют обширного отражения с помощью PropertyInfo.

Система свойств WPF поддерживает таблицу хранения свойств зависимостей, реализованных в заданном типе, производном из объекта DependencyObject. Процессор XAML использует такую таблицу для вывода идентификатора свойства зависимостей для свойства зависимостей. Например, в соответствии с соглашением идентификатор свойства зависимостей для свойства зависимостей с именем ABC имеет значение ABCProperty. Обработчик XAML может эффективно устанавливать значение любого свойства зависимостей, вызывая метод SetValue для содержащего его типа, используя идентификатор свойства зависимостей.

Дополнительные сведения о оболочках свойств зависимостей см. в разделе Пользовательские свойства зависимостей.

Последствия использования пользовательских свойств зависимостей

При установке значения свойства зависимостей процессор XAML WPF обходит оболочки свойств и прямые вызовы SetValue. Поэтому не следует размещать какую-либо лишнюю логику в методе доступа set настраиваемого свойства зависимости, так как эта логика не будет выполняться, если значение свойства задано в XAML. Метод доступа set должен содержать только вызов SetValue.

Аналогично, аспекты процессора XAML WPF, которые получают значения свойств, обходят оболочку свойств и напрямую вызывают GetValue. Поэтому также не следует размещать какую-либо лишнюю логику в методе доступа get настраиваемого свойства зависимости, так как эта логика не будет выполняться, если значение свойства считывается в XAML. Метод доступа get должен содержать только вызов GetValue.

Пример свойства зависимостей с оболочкой

В следующем примере показано рекомендуемое определение свойства зависимостей с оболочками свойства. Идентификатор свойства зависимостей хранится в виде поля public static readonly, а методы доступа get и set не содержат кода помимо необходимых методов системы свойств WPF, которые возвращают значение свойства зависимостей. Если у вас есть код, который должен выполняться при изменении значения свойства зависимостей, рекомендуется поместить этот код в метод PropertyChangedCallback для своего свойства зависимостей. Дополнительные сведения см. в разделе Обратные вызовы при изменении свойств.

// Register a dependency property with the specified property name,
// property type, owner type, and property metadata. Store the dependency
// property identifier as a public static readonly member of the class.
public static readonly DependencyProperty AquariumGraphicProperty =
    DependencyProperty.Register(
      name: "AquariumGraphic",
      propertyType: typeof(Uri),
      ownerType: typeof(Aquarium),
      typeMetadata: new FrameworkPropertyMetadata(
          defaultValue: new Uri("http://www.contoso.com/aquarium-graphic.jpg"),
          flags: FrameworkPropertyMetadataOptions.AffectsRender,
          propertyChangedCallback: new PropertyChangedCallback(OnUriChanged))
    );

// Property wrapper with get & set accessors.
public Uri AquariumGraphic
{
    get => (Uri)GetValue(AquariumGraphicProperty);
    set => SetValue(AquariumGraphicProperty, value);
}

// Property-changed callback.
private static void OnUriChanged(DependencyObject dependencyObject, 
    DependencyPropertyChangedEventArgs e)
{
    // Some custom logic that runs on effective property value change.
    Uri newValue = (Uri)dependencyObject.GetValue(AquariumGraphicProperty);
    Debug.WriteLine($"OnUriChanged: {newValue}");
}
' Register a dependency property with the specified property name,
' property type, owner type, and property metadata. Store the dependency
' property identifier as a public static readonly member of the class.
Public Shared ReadOnly AquariumGraphicProperty As DependencyProperty =
    DependencyProperty.Register(
        name:="AquariumGraphic",
        propertyType:=GetType(Uri),
        ownerType:=GetType(Aquarium),
        typeMetadata:=New FrameworkPropertyMetadata(
            defaultValue:=New Uri("http://www.contoso.com/aquarium-graphic.jpg"),
            flags:=FrameworkPropertyMetadataOptions.AffectsRender,
            propertyChangedCallback:=New PropertyChangedCallback(AddressOf OnUriChanged)))

' Property wrapper with get & set accessors.
Public Property AquariumGraphic As Uri
    Get
        Return CType(GetValue(AquariumGraphicProperty), Uri)
    End Get
    Set
        SetValue(AquariumGraphicProperty, Value)
    End Set
End Property

' Property-changed callback.
Private Shared Sub OnUriChanged(dependencyObject As DependencyObject,
                                e As DependencyPropertyChangedEventArgs)
    ' Some custom logic that runs on effective property value change.
    Dim newValue As Uri = CType(dependencyObject.GetValue(AquariumGraphicProperty), Uri)
    Debug.WriteLine($"OnUriChanged: {newValue}")
End Sub

См. также