Свойство зависимости WPF - PullRequest
18 голосов
/ 25 июня 2010

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

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

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

Ответы [ 4 ]

32 голосов
/ 25 июня 2010

Моя ментальная модель работы свойств зависимости:

Любой класс 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, оно запускает событие изменения свойства, которое слышит все, что зависит от свойства. (Вот почему они называются свойствами зависимости, помните.) И если вещь, зависящая от свойства, сама является свойством зависимости, она запускает свои собственные события изменения свойства. Таким образом, изменение FontFamily на Window изменяет шрифт для каждого элемента управления в окне, а также побуждает WPF повторно визуализировать измененные элементы управления.

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

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

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

6 голосов
/ 25 июня 2010

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

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

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

2 голосов
/ 25 июня 2010

просто посмотрите это сообщение от joshsmith, в нем есть дополнительная информация

http://joshsmithonwpf.wordpress.com/2007/06/22/overview-of-dependency-properties-in-wpf/

1 голос
/ 31 мая 2015

Ниже вы можете увидеть очень простой пример 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) Добавьте пространство имен для customуправляющее текстовое поле, как показано ниже:

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

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

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

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

...