Главная » WPF » Свойства .NET

0

Свойства

При проектировании типов мы говорим об их свойствах, методах и событиях. Именно  этими тремя понятиями объект описывается с точки зрения разработчи ка. В предыдущей  компонентной модели,  созданной  корпорацией Microsoft,  – COM  – поддерживались только  методы. Поддержка  свойств  была рудиментар ной – в определении интерфейса были метаданные,  помечающие  методы put_  и get_,   чтобы инструменты типа VB могли предоставить  модель, включающую свойства.  События же были  реализованы напрямую  с помощью  паллиативных стоков (sinks)  для обратных вызовов.

При проектировании .NET была, в частности, поставлена  цель поддержать все три концепции на уровне самой платформы. События и свойства  были реализо ваны  как  в среде исполнения, так  и почти  во всех .NET совместимых языках. WPF от начала до конца реализована в виде управляемого кода. Это решение бы ло принято  на ранних стадиях разработки  WPF,  чтобы не отклоняться от согла шений и паттернов,  принятых  в .NET, и иметь возможность обращаться  к сред ствам среды исполнения. Тогда какое же место занимает система свойств WPF?

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

Начнем с самого начала – с CLR свойства  некоего фиктивного типа:

public class Widget { Widget _parent; Color _background;

public Color Background {

get { return _background; }

set { _background = value; }

}

public Widget Parent {

get { return _parent; }

set { _parent = value; }

}

}

Мы определили  два свойства: Background и Parent. И сразу же сталкиваемся с проблемой  – мы  хотим,  чтобы  при  изменении свойства  Background объект Widget перерисовывал себя. Разумеется, у других разработчиков может возник нуть желание  узнать о том, что цвет фона изменился, поэтому извещение  об из менении следует сделать открытым:

public class Widget { Widget _parent; Color _background;

public event EventHandler BackgroundChanged;

public Color Background {

get { return _background; }

set {

if (_background != value) {

_background = value;

OnBackgroundChanged(EventArgs.Empty);

}

}

}

public Widget Parent { get { … } set { … } }

protected virtual void OnBackgroundChanged(EventArgs e) {

if (BackgroundChanged != null) { BackgroundChanged(this, e);

} RepaintWidget();

}

}

Пока все просто. Далее, очень полезно было бы иметь возможность устанавли

вать такой же цвет фона, как у родителя:

public class Widget { Widget _parent;

bool _isBackgroundSet = false;

Color _background;

public event EventHandler BackgroundChanged;

public Color Background {

get {

if (!isBackgroundSet && Parent != null) {

return Parent.Background;

}

return _background;

}

set {

if (_background != value) {

_background = value;

_isBackgroundSet = true;

OnBackgroundChanged(EventArgs.Empty);

}

}

}

public Widget Parent { get { … } set { … } }

protected virtual void OnBackgroundChanged(EventArgs e) {

}

}

Но и это еще не конец. Мы забыли о двух важнейших  вещах. Во первых, если родитель  изменился, а цвет фона не установлен,  нужно отправить  извещение  об изменении.  Во вторых,  необходим  механизм  для восстановления цвета фона по умолчанию (унаследованного):

public class Widget { Widget _parent;

bool _isBackgroundSet = false; Color _background;

public event EventHandler BackgroundChanged;

public event EventHandler ParentChanged;

public Color Background { get { … } set { … } }

public Widget Parent {

get { … }

set {

if (_parent != value) {

_parent = value; OnParentChanged(EventArgs.Empty);

}

}

}

public void ResetBackground() {

_isBackgroundSet = false;

OnBackgroundChanged(EventArgs.Empty);

}

protected virtual void OnBackgroundChanged(EventArgs e) {

}

protected virtual void OnParentChanged(EventArgs e) {

if (ParentChanged != null) { ParentChanged(this, e);

}

if (!_isBackgroundSet) { OnBackgroundChanged(EventArgs.Empty);

}

}

}

Уже видно, что много кода дублируется. Напомним,  что ключевой частью ди намической,  управляемой данными,  декларативной системы является идея сти лизации.  Чтобы повысить  степень повторной  используемости кода, в WPF име ется возможность определить  единственный объект так, что он сможет наделять своими  свойствами   другие  объекты;  это  похоже  на  использование  стилей  в Microsoft  Word  или CSS стилей  в HTML.  Конечно, мы хотим, чтобы наш класс Widget поддерживал эту функциональность:

public class Widget { Style _style; Widget _parent;

bool _isBackgroundSet = false; Color _background;

public event EventHandler BackgroundChanged; public event EventHandler ParentChanged; public event EventHandler StyleChanged;

public Color Background {

get {

if (!isBackgroundSet) {

if (Style != null) {

return Style.Background;

}

return Parent.Background;

}

return _background;

}

set { … }

}

public Widget Parent { get { … } set { … } } public Style Style { get { … } set { … } } public void ResetBackground() { … }

protected virtual void OnBackgroundChanged(EventArgs e) {

}

protected virtual void OnParentChanged(EventArgs e) { … }

protected virtual void OnStyleChanged(EventArgs e) { … }

}

В этом примере мы добавили  стандартный код для поддержки  свойства Style и кое что интересное  внутри метода get для свойства Background. Чтобы соотве тствовать требованиям WPF,  предстоит еще добавить немало функциональности в свойства  (анимацию,  значения  по умолчанию  и т.д.). Но прежде чем заходить так далеко, посмотрим,  что же у нас получается.

Для начала отметим, что любое унаследованное свойство должно быть опре делено в базовом типе. Поскольку мы зашили в код поиск свойства в родительс ком типе, то выходит,  что на этапе компиляции базового типа мы уже должны знать обо всех свойствах, которые кто то может захотеть унаследовать.  То же са мое справедливо и для стилей. Так как определение  Style приходится  обновлять для каждого  свойства,  то стилизации поддаются  только  те свойства,  о которых мы знаем заранее. Мы дублируем значительный объем код для извещения об из менениях. Мы вынуждены зашивать в код зависимости между свойствами; конк ретно, нам пришлось  в родительском классе написать  код для извещения об из менении цвета фона. Оказалось, что необходимо заводить в классе локальное по ле для каждого свойства. Список можно продолжить.

Источник: К. Андерсон  Основы  Windows Presentation Foundation. Пер. с англ. А. Слинкина — М.: ДМК Пресс, 2008 — 432 с.: ил.

По теме:

  • Комментарии