Архитектура WPF

В этом разделе предлагается ознакомительная демонстрация иерархии классов Windows Presentation Foundation (WPF). Она охватывает большую часть подсистем WPF и описывает их взаимодействие. Здесь также подробно рассматриваются некоторые архитектурные решения WPF.

В этом разделе содержатся следующие подразделы.

  • System.Object
  • System.Threading.DispatcherObject
  • System.Windows.DependencyObject
  • System.Windows.Media.Visual
  • System.Windows.UIElement
  • System.Windows.FrameworkElement
  • System.Windows.Controls.Control
  • Сводка
  • Связанные разделы

System.Object

Основная модель программирования WPF доступна через управляемый код. Ранее, на этапе проектирования WPF, несколько раз обсуждалось, где следует провести черту между управляемыми и неуправляемыми компонентами системы. CLR предоставляет ряд возможностей, которые делают разработку более продуктивной и надежной (включая управление памятью, обработку ошибок, систему общих типов и т. д.), но требуют некоторых затрат.

Основные компоненты WPF показаны на расположенном ниже рисунке. Красные разделы диаграммы (PresentationFramework, PresentationCore и milcore) представляют собой основные части кода WPF. Только один из этих компонентов является неуправляемым — milcore. Компонент milcore написан в неуправляемом коде, чтобы обеспечить тесную интеграцию с DirectX. Все отображения в WPF выполнены с помощью подсистемы DirectX, позволяющей эффективно использовать оборудование и программное обеспечение для визуализации. Для WPF также необходим тонкий контроль над памятью и выполнением. Механизм композиции в milcore крайне чувствителен к производительности и требует отказа от многих преимуществ CLR в пользу производительности.

Размещение WPF в платформе .NET Framework.

Связь между управляемыми и неуправляемыми частями WPF обсуждается далее в этом разделе. Остальная часть управляемой модели программирования описана ниже.

System.Threading.DispatcherObject

Большинство объектов в WPF произошли от DispatcherObject, который предоставляет базовые конструкции для работы с параллелизмом и потоковостью. WPF базируется на системе обмена сообщениями, реализуемой диспетчером. Его работа очень похожа на обычную загрузку сообщений в Win32; в действительности диспетчер WPF использует сообщения User32 для выполнения перекрестных вызовов потоков.

Существует два основных понятия при обсуждении параллельной работы в WPF — диспетчер и сходство потоков.

На этапе проектирования WPF основной целью был переход к одному потоку выполнения при непотоковой модели "со сходством". Сходство потоков случается, когда компонент использует идентификацию выполняемого потока для сохранения некоторых типов состояния. Наиболее распространенной формой этого является использование локальной памяти потока (TLS) для сохранения состояния. Сходство потоков требует, чтобы каждый логический поток выполнения принадлежал только одному физическому потоку в операционной системе, что создает большую нагрузку на память. В итоге сохранилась синхронизация потоковой модели WPF с существующей потоковой моделью User32 однопотокового выполнения со сходством потоков. Основной причиной этого явилась функциональная совместимость — для всех систем, таких как OLE 2.0, буфер обмена и Internet Explorer, требуется выполнение в одном сходном потоке (STA).

Пусть имеются объекты с организацией потоковой обработки STA и необходим способ связи между потоками и проверка нахождения в правильном потоке. В этом заключается роль диспетчера. Диспетчер — это основная диспетчерская система управления сообщениями, включающая несколько очередей с назначенными приоритетами. Примеры сообщений включают необработанные входящие уведомления (перемещение мыши), функции Framework (макет) или пользовательские команды (выполнение этого метода). Благодаря наследованию из DispatcherObject создается объект CLR, который обладает функциями STA и получает во время создания указатель на диспетчер.

System.Windows.DependencyObject

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

Чтобы иметь больше свойств управления системой, требовалась более полная система свойств, чем предоставляемая CLR. Простым примером такой полноты являются уведомления об изменении. Для включения двусторонней привязки необходимо, чтобы обе стороны привязки поддерживали уведомления об изменениях. Чтобы поведение зависело от значений свойств, необходимо получать уведомление в случае изменения значения свойства. В Microsoft .NET Framework существует интерфейс INotifyPropertyChange, который позволяет объекту публиковать уведомления об изменениях (однако это необязательно).

WPF предоставляет обширную систему свойств, полученную из DependencyObject. Система свойств действительно является системой свойств "зависимостей" в том смысле, что она отслеживает зависимости между выражениями свойств и автоматически проверяет значения свойства при изменении зависимости. Например, если имеется наследующее свойство (например FontSize), то система автоматически обновляется при изменении свойства в родительском объекте элемента, наследующего значение.

Основой системы свойств WPF является понятие "выражение свойства". В этом первом выпуске WPF система выражений свойств закрыта, и все выражения предоставлены как часть Framework. Выражения объясняют, почему система свойств не поддерживает привязку к данным, создание стилей или жестко запрограммированное наследование, но вместо этого представлена макетами более поздних версий в Framework.

Система свойств также предоставляет способ разреженного хранения значений свойств. Поскольку объекты могут иметь десятки (если не сотни) свойств, и большинство значений находятся в состоянии по умолчанию (унаследованы, задаются стилем и т. д.), не каждый экземпляр объекта должен иметь все определенные в нем свойства в полном объеме.

Наконец последняя новая особенность системы свойств — это понятие вложенных свойств. Элементы WPF базируются на принципе повторного использования композиции и компонентов. Часто бывает так, что некоторому содержащему элементу (например элементу макета Grid) требуются дополнительные данные о дочерних элементах для управления их поведением (например сведения о строках и столбцах). Вместо того чтобы сопоставлять все эти свойства с каждым элементом, любой объект может предоставить определения свойств для любого другого объекта. Это похоже на возможности "expando" JavaScript.

System.Windows.Media.Visual

После определения системы следующим шагом является рисование точек на экране. Класс Visual предоставляет средства для построения дерева визуальных объектов, которые дополнительно включают инструкции по рисованию и метаданные о способе визуализации этих инструкций (обрезка, преобразование и другие). Класс Visual разработан максимально облегченным и гибким, так что большинство компонентов скрыто от общего доступа API и сильно зависит от защищенных функций обратного вызова.

Класс Visual является реальной точкой входа в систему композиции WPF. Класс Visual является точкой соединения между двумя подсистемами, управляемым API и неуправляемым компонентом milcore.

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

При программировании WPF создаются элементы Visual и производные типы, которые осуществляют внутреннее взаимодействие с деревом композиции через этот протокол обмена сообщениями. Каждый элемент Visual в WPF может создать один, ни одного или несколько узлов композиции.

Windows Presentation Foundation в визуальном дереве.

Здесь имеется один очень важный архитектурный момент — все дерево визуальных объектов и инструкций по рисованию кэшируется. С графической точки зрения WPF использует систему сохраненной визуализации. Это позволяет системе осуществлять перерисовку с высокой частотой без блокирования системы композиции при обратных вызовах, обращенных к коду пользователя. Это помогает предотвратить признаки неотвечающего приложения.

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

В User32 и GDI система работает в немедленном режиме системы обрезки. Когда требуется визуализация компонента, система устанавливает границы обрезки, вне которых компонент не может изменять точки, а затем компонент запрашивает рисование точек в этой области. Эта система работает очень хорошо в системах с ограниченной памятью, поскольку в случае каких-либо изменений приходится иметь дело только с измененным компонентом — два компонента никогда не воздействуют на цвет одной точки.

WPF использует "алгоритм художника" для модели рисования. Это означает, что вместо обрезки каждого компонента каждый компонент запрашивается для отрисовки, начиная с заднего плана и до переднего плана отображения. Это позволяет рисовать каждый компонент поверх отображения предыдущего компонента. Преимуществом этой модели является то, что можно создавать сложные, полупрозрачные фигуры. В сочетании с современным графическим оборудованием эта модель является относительно быстрой (чего нельзя было сказать о создании User32/ GDI).

Как упоминалось ранее, основным принципом WPF является переход к более декларативной, "сфокусированной на свойствах" модели программирования. В визуальной системе это проявляется в паре любопытных моментов.

Во-первых, если говорить о сохраненном режиме графической системы, он действительно отражает переход от обязательной модели DrawLine/DrawLine к модели, ориентированной на данные — new Line ()/new Line(). Этот переход к управляемой данными визуализации позволяет выполнять сложные операции в инструкциях по рисованию, выражаемых с помощью свойств. Типы, получаемые из Drawing, являются эффективной объектной моделью для визуализации.

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

System.Windows.UIElement

UIElement определяет основные подсистемы, включая "Макет", "Ввод данных" и "События".

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

Measure позволяет компоненту определить требуемый размер. Этот этап является отдельным от Arrange, поскольку существует множество ситуаций, когда родительский элемент запрашивает несколько раз измерение дочернего элемента для определения его оптимального положения и размера. Тот факт, что родительские элементы запрашивают измерение дочерних, демонстрирует еще один ключевой принцип WPF — размер по содержимому. Все элементы управления в WPF поддерживает возможность изменения размера по размеру их содержимого. Это значительно упрощает локализацию и позволяет осуществлять динамическую разметку элементов в соответствии с изменением размеров. На этапе Arrange родительский элемент может расположить каждый дочерний элемент и определить его конечный размер.

Много времени часто затрачивается на обсуждение внешней стороны WPF — Visual и связанных объектов. Однако существует также множество новшеств со стороны ввода данных. Вероятно, наиболее фундаментальным изменением в модели ввода для WPF является согласованная модель, согласно которой события ввода направляются через систему.

Ввод возникает как сигнал драйверу устройства режима ядра и направляется в нужный процесс и поток через сложный процесс, в котором участвуют ядро Windows и User32. После того как сообщение User32, соответствующее входным данным, направлено в WPF, оно преобразуется в сообщение базового ввода для WPF и отправляется к диспетчеру. WPF позволяет преобразовать события базового ввода в несколько фактических событий, запуская реализацию таких компонентов, как "MouseEnter", на низких уровнях системы с гарантированной доставкой.

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

Это разделение между нисходящим и всплывающим этапами делает возможным согласованную реализацию таких возможностей, как сочетания клавиш. В User32 сочетания клавиш реализуются посредством одной глобальной таблицы, содержащей все необходимые сочетания клавиш (Ctrl + N сопоставляется с командой "Создать"). В диспетчере приложения вызывается метод TranslateAccelerator, который будет анализировать входящие сообщения в User32 и определять их соответствие зарегистрированному сочетанию клавиш. В WPF это не работает, поскольку система полностью "компонуема" — любой элемент может обрабатывать и использовать любые сочетания клавиш. Наличие этой двухэтапной модели для ввода позволяет компонентам реализовать собственные методы TranslateAccelerator.

Забегая на шаг вперед, в UIElement вводится также понятие CommandBindings. Система команд WPF позволяет разработчикам определять функциональность в терминах конечной точки команды — нечто, что реализует ICommand. Команды привязки включают элемент для определения соответствия между действием ввода (CTRL + N) и командой ("Создать"). И действия ввода, и определения команд являются расширяемыми и могут быть связаны вместе во время использования. Это делается, например, для того чтобы разрешить конечному пользователю настраивать привязки клавиш, которые ему необходимо использовать в приложении.

До этого момента в разделе рассматривались "основные" возможности WPF — возможности, реализованные в сборке PresentationCore. При построении WPF предполагалось четкое разделение между фундаментальными частями (такими как соглашение для макета с использованием Measure и Arrange) и частями Framework (такими как реализация определенного макета, например Grid). Целью являлось предоставление точки расширения внизу стека, что позволило бы внешним разработчикам при необходимости создавать свои собственные структуры.

System.Windows.FrameworkElement

Элемент FrameworkElement можно рассматривать с двух разных сторон. Он представляет набор политик и настроек подсистем, введенных на нижнем уровне WPF. В нем также вводится набор новых подсистем.

Основная политика, представленная классом FrameworkElement, затрагивает макет приложения. Класс FrameworkElement строится на основе базового контракта макета, представленного UIElement, и добавляет понятие "ячейки" макета, что упрощает для авторов макета создание согласованного набора управляемых свойствами семантик макета. Свойства, такие как HorizontalAlignment, VerticalAlignment, MinWidth и Margin (для нескольких именований), обеспечивают всем компонентам, производным от FrameworkElement, согласованное поведение внутри контейнеров макета.

FrameworkElement упрощает также доступ к API для множества функций на уровне ядра WPF. Например, класс FrameworkElement предоставляет прямой доступ к анимации с помощью метода BeginStoryboard. Объект Storyboard предоставляет способ создания скриптов нескольких анимаций вместо набора свойств.

Две наиболее важные вещи, представленные в FrameworkElement — это привязка данных и стили.

Подсистема привязки данных в WPF должна быть относительно знакома каждому, кто использовал Windows Forms или ASP.NET, чтобы создать user interface (UI) приложения. В каждой из этих систем есть простой способ выразить, что для данного элемента необходимо привязать одно или более свойств к части данных. WPF обладает полным набором возможностей для привязки свойств, преобразования и привязки списка.

Одной из наиболее интересных возможностей привязки данных в WPF является введение шаблонов данных. Шаблоны данных позволяют декларативно указать способ визуализации фрагмента данных. Вместо создания настраиваемого пользовательского интерфейса, который может быть привязан к данным, можно обойти проблему и позволить данным определять отображение, которое будет создано.

Создание стилей — это облегченная форма привязки данных. Посредством создания стилей можно привязать набор свойств из общего определения к одному или нескольким экземплярам элемента. Стили применяются к элементу посредством либо явной ссылки (путем задания свойства Style), либо неявного связывания стиля с типом CLR элемента.

System.Windows.Controls.Control

Наиболее значимая возможность для элемента управления — это использование шаблонов. Если представлять себе систему композиции WPF как систему визуализации сохраненного режима, то шаблоны позволяют элементу управления описывать свою визуализацию в параметризированной, декларативной форме. ControlTemplate в действительности не более чем скрипт для создания набора дочерних элементов с привязками к свойствам, предлагаемым элементом управления.

Control предоставляет набор стандартных свойств Foreground, Background, Padding и т. д., которые авторы шаблона могут затем использовать для настройки отображения элемента управления. Реализация элемента управления обеспечивает модель данных и модель взаимодействия. Модель взаимодействия определяет набор команд (таких как "Закрыть" для окна) и привязки к действиям ввода (таким как нажатие красного символа X в верхнем углу окна). Модель данных предоставляет набор свойств либо для настройки модели взаимодействия, либо для настройки отображения (определяется шаблоном).

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

Типичным аспектом модели данных элементов управления является модель содержимого. Если представить себе элемент управления, например Button, то можно увидеть, что он имеет свойство с именем "Content" типа Object. В Windows Forms и ASP.NET это свойство было бы строкой, но это ограничивает тип содержимого, который можно поместить на кнопку. Содержимое для кнопки может представлять собой простую строку, сложный объект данных или все дерево элементов. В случае объекта данных используется шаблон данных для создания отображения.

Сводка

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

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

В начале разработки приложений на основе WPF все должно быть знакомо. Можно задавать свойства, использовать объекты и привязывать данные практически так же, как с помощью Windows Forms или ASP.NET. При углублении в архитектуру WPF вы обнаружите, что можно создавать значительно более сложные приложения, которые фундаментально обрабатывают данные, как драйвер ядра приложения.

См. также

Ссылки

Visual

UIElement

ICommand

FrameworkElement

DispatcherObject

CommandBinding

Control

Основные понятия

Общие сведения о связывании данных

Система макета

Общие сведения об эффектах анимации