Подтвердить что ты не робот

Свойство зависимостей WPF

Я читал о свойствах зависимостей в нескольких книгах, но у всех есть одна общая черта, они просто рассказывают нам, как они реализованы (используя static readonly DependencyProperty и т.д.), но не говорят точно, как они работают изнутри.

Я имею в виду, что они реализованы как статические, но все же применяются ко всем объектам.
Вторая точка смущения - это свойства.

Есть ли какой-либо учебник, который может легко объяснить все эти понятия?

4b9b3361

Ответ 1

Моя ментальная модель того, как работают свойства зависимостей:

Любой класс DependencyObject реализует два специальных свойства. Одно, статическое свойство класса, является словарем объектов DependencyProperty. Каждый экземпляр класса может заглянуть внутрь этого словаря, чтобы найти метаинформацию о каждом DependencyProperty - имени свойства, его типе, любых обратных вызовах, которые нужно вызывать при их получении и установке, о том, как он участвует в наследовании свойств и т.д. Когда вы регистрируете свойство зависимостей, вы добавляете запись в этот словарь.

Другим свойством является свойство экземпляра: это словарь с ключом DependencyProperty, который содержит локальное значение каждого DependencyProperty, если он был установлен.

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

Магия свойств зависимостей заключается в том, что делают GetValue и SetValue.

GetValue просматривает значение для свойства в словаре значений объектов. Если он не находит его, он вызывает GetValue в родительском элементе, чтобы получить то, что родительский элемент считает значением. Например, при создании TextBox в Window все, что смотрит на TextBox FontFamily, на самом деле вызывает GetValue. Если вы явно не задали шрифт, в его словаре для этого свойства нет записи. Поэтому GetValue запрашивает родительский элемент для значения. Родительский элемент может иметь или не иметь FontFamily set; если нет, то его вызов GetValue возвращает значение из его родителя. И так далее, пока не будет достигнут объект Window, и будет найдено фактическое значение FontFamily.

Если вы установите FontFamily на TextBox, SetValue сохранит значение в словаре значений. В следующий раз что-нибудь нужно получить значение FontFamily для этого TextBox, GetValue находит значение в словаре и возвращает его, поэтому ему не нужно запрашивать родительский элемент.

Если вы устанавливаете FontFamily в Window, SetValue не только обновляет значение в словарях Window value, он запускает событие изменения свойства, которое все зависит от свойства. (Вот почему они называются свойствами зависимостей, помните.) И если предмет, зависящий от свойства, сам по себе является свойством зависимости, он запускает свои собственные события изменения свойств. Именно так, изменение FontFamily на Window изменяет шрифт для каждого элемента управления в окне, а также предлагает WPF повторно отобразить измененные элементы управления.

Прикрепленные свойства работают с использованием такого же подхода. Любой объект, который может иметь прикрепленные свойства, имеет словарь, в котором хранятся значения прикрепленных свойств. Когда вы устанавливаете Grid.Column на CheckBox в XAML, вы просто добавляете запись в этот словарь CheckBox. Когда Grid должен знать, в каком столбце находится CheckBox, он отображает значение из этого словаря. Когда вы устанавливаете Grid.IsSharedSizeScope на True на объект, этот словарь объектов будет содержать новое свойство - словарь, который содержит ширины/высоты для каждого SharedSizeKey.

Я должен подчеркнуть, что это моя ментальная модель. Я не сел с Reflector и посмотрел на фактическую реализацию Register, GetValue и SetValue, чтобы выяснить, как они на самом деле работают. Возможно, я ошибаюсь в деталях. Но это модель, которая точно предсказывает, как ведет себя этот материал, поэтому он достаточно хорош.

Концепция хранения значений свойств в словарях довольно странна для программистов на С#. Тем не менее, это старая шляпа для программистов на Python. В Python все свойства класса - все объекты, по сути, хранятся в словарях, и поэтому вы можете получить их значение либо через аксессуры свойств, либо просто просмотрев их. Свойства зависимостей и приложенные свойства - это еще один способ, которым .NET, украв все, что было у Java, которое стоило воровать, теперь грабит Python. (Или из того места, где Python грабил их.) Изучение Python сделало меня намного лучшим программистом на С#; Я рекомендую его любому разработчику С#, который еще не сделал этого.

Ответ 2

Вот учебник по свойствам зависимостей http://www.wpftutorial.net/DependencyProperties.html, который немного объясняет, как они работают.

Краткое объяснение того, почему объект DependencyProperty находится в статическом поле, заключается в том, что он представляет описание свойства, а не значение свойства. Каждый объект DependencyObject имеет сопоставление объектов DependencyProperty с их значениями.

Так же работают приложенные свойства. Поскольку каждый DependencyObject сохраняет сопоставление от любого DependencyProperty к значению, любой тип может создать новый DependencyProperty и установить его на любом существующем DependencyObject.

Ответ 4

Ниже показан очень простой пример dependency property, который создает custom control text box, в котором пространство будет недоступно, означает, что пользователь не может ввести пробел в текстовое поле.

1) Создайте класс с именем ValidatedTextBox и напишите следующий код в этом файле:

public class ValidatedTextBox : TextBox  
{  
    public ValidatedTextBox()  
    {
    }

    public static readonly DependencyProperty IsSpaceAllowedProperty =  
        DependencyProperty.Register("IsSpaceAllowed", typeof(bool), typeof(ValidatedTextBox));  

    public bool IsSpaceAllowed
    {
        get { return (bool)base.GetValue(IsSpaceAllowedProperty); }
        set { base.SetValue(IsSpaceAllowedProperty, value); }
    }

    protected override void OnPreviewKeyDown(KeyEventArgs e)
    {
        base.OnPreviewKeyDown(e);
        if (!IsSpaceAllowed && (e.Key == Key.Space))
        {
            e.Handled = true;
        }
    }
}

2) Теперь используйте вышеуказанный элемент управления в файле .XAML

a) Добавьте пространство имен текстового поля настраиваемого элемента управления, как показано ниже:

xmlns:CustomControls="clr-namespace: ValidatedTextBox;assembly= ValidatedTextBox "  

b) Теперь используйте текстовое поле пользовательского элемента управления, как показано ниже:

<CustomControls:ValidatedTextBox IsSpaceAllowed="False" x:Name="MyTextBox" /> 

Он создаст настраиваемое текстовое поле управления, которое не позволит использовать пространство. Таким образом, свойство Basically Dependency позволяет добавить функцию, расширить функцию любого элемента управления.