В ваших файлах .xaml.cs
вы можете заметить, что классы, поддерживающие ваши скомпилированные файлы XAML, помечены как классы partial
. Задача сборки XAML создает второй файл .cs
с другим сектором частичного класса, содержащим реализацию метода IComponentConnector.InitializeComponent()
, который вызывается конструктором по умолчанию в коде позади. Этот метод в основном проходит через XAML (который фактически находится в форме BAML в этой точке) и использует его для «исправления» вновь созданного объекта, в отличие от создания нового объекта из источника XAML, что и произойдет, если вы должны были использовать XamlReader
для загрузки или анализа объекта.
Итак, когда вы создаете экземпляр нового скомпилированного объекта XAML (например, UserControl
), любой код предшествует вызову InitializeComponent()
в конструкторе. Затем все свойства и обработчики событий, установленные в файле XAML, будут обработаны во время вызова до InitializeComponent()
, после чего конструктор возобновится. Это может быть полезно знать, поскольку вы можете захотеть убедиться, что определенные свойства будут установлены до или после обработки файла XAML.
Относительно того, как анализируется XAML, он по существу считывается как поток узлов XAML, представляющих присвоения свойств, объявления объектов и т. Д., Которые выполняются по порядку услугами в System.Xaml
. Этот поток узлов основан на общей объектной модели, которая, возможно, была построена из потока BAML, XML-документа (например, файла .xaml
), другого экземпляра объекта и т. Д. BAML, будучи более компактным, чем формат на основе XML , обычно быстрее разбирается.
Добавление: В примере вы добавили, вы спросите, как анализатор видит, что Button
объект должен быть создан и Margin
набор. Короткий ответ: это зависит. В частности, это зависит от контекста схемы, используемого для чтения потока XAML.
Часть XAML анализатор использует свою собственную систему типа, из которых по крайней мере две реализации:
- стандартной системы типа CLR, основанные на отражении и
System.ComponentModel
;
- Система типа WPF, которая расширяет # 1 путем включения специальной поддержки свойств зависимостей и маршрутизируемых событий.
Это, основанное на моем воспоминании о XAML языка Spec, примерно то, что происходит:
- Часть XAML парсер встречает
StartObject
узел типа Button
, который (для стандартных отображений пространств имен WPF) разрешается до System.Windows.Controls.Button
. Это говорит парсеру, что ему нужно создать экземпляр объекта Button
, который он делает, вызывая его конструктор по умолчанию через отражение.
- Следующий узел в потоке - это узел
StartMember
с именем участника Margin
. Модель типа для контекста схемы WPF разрешит это для свойства зависимостей Margin
.
- A
Value
узел приходит далее, что говорит синтаксическому анализатору установить значение "10"
(строка). Парсер видит, что тип свойства Thickness
несовместим со строковым значением. Он консультирует свою систему типов, чтобы узнать, существует ли атрибут [ValueSerializer]
в свойстве Margin
, который может быть использован для преобразования строки; такой атрибут не существует. Он проверяет свойство для атрибута [TypeConverter]
; опять же, он не находит ничего. Он ищет атрибут [TypeConverter]
самого типа Thickness
и находит тот, который инструктирует его использовать ThicknessConverter
для преобразования строкового значения в Thickness
. Он делает это. Поскольку Margin
является свойством зависимости, он использует API SetValue()
для установки значения свойства; если бы это было свойство CLR, оно использовало бы отражение или PropertyDescriptor
.
- Узел
EndMember
указывает синтаксическому анализатору завершить назначение свойства.
- Парсер встречает
Value
узел с контентом "OK"
. Парсер знает, что он создает сложный объект, поэтому контент не может представлять весь объект. Он ищет атрибут [ContentProperty]
на Button
и его супертипах; он находит один на ContentControl
, что указывает, что значение должно использоваться для установки свойства Content
(которое получает разрешение на соответствующее свойство зависимостей). Content
- object
, поэтому он непосредственно присваивает значение string
(опять же, используя SetValue()
).
- Следующий узел:
EndObject
, который сообщает парсеру, что он закончил обработку объекта Button
.
Обратите внимание, что я использовал термин «парсер» для упрощения. Честно говоря, ничто из этого не происходит на этапе синтаксического анализа (если даже существует «синтаксический» этап). То, что вы можете себе представить как этап «синтаксического анализа», - это просто построение потока узлов XAML. Создание и/или совокупность объявленного объекта (ов) фактически происходит путем подачи этого потока в XamlObjectWriter
, который представляет собой просто реализацию XamlWriter
, которая записывает узлы XAML в объект (в отличие от XML-документа или потока BAML). На высоком уровне происходит только две вещи:
XamlReader
превращает что-то в поток узлов XAML.
XamlWriter
превращает поток узлов XAML во что-то.
В случае compled XAML ресурса, время компиляции трубы сборки задачи выхода из XamlXmlReader
в BamlWriter
, чтобы «компилирование» XAML.Во время выполнения вход BamlReader
отправляется в XamlObjectWriter
для создания или «исправления» корневого объекта.
Как только вы все это осознаете, вы можете начать распознавать XAML как мощный формат сериализации и персистентности, а не просто язык для создания пользовательских интерфейсов.
Майк, спасибо за ваш хороший ответ. Ваше добавление - это то, что мне нужно. Таким образом, построение дерева объектов с помощью механизма WPF предполагает интенсивное использование отражения в нашем микроприложении - создание объекта Button, определение того, какой TypeConverter использовать, построение TypeConverter, выяснение, что Margin является свойством зависимостей и т. Д. Право? – WpfNewbie
Да, в системе типа XAML много отражений. –
Отличный ответ !! – nawfal