Одним из наиболее интересных способов использования XAML является разбор его на лету с помощью XamlReader. Например, предположим, что вы начинаете со следующего содержимого в файле Window1.xaml:
<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Name="Btn1" Margin="40">Кликни меня!</Button>
</DockPanel>
Во время выполнения можно загрузить это содержимое в активное окно. Ниже показан код, который делает это:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.IO;
namespace WpfApplication1
{
public class Class1 : Window
{
private Button Btn1;
public Class1() { }
public Class1(string xamlFile)
{
InitializedComponent(xamlFile);
}
private void InitializedComponent(string xamlFile)
{
// Новая форма
this.Width = this.Height = 300;
this.Left = this.Top = 100;
this.Title = "Моя кнопка";
// Получаем содержимое XAML из внешнего файла
DependencyObject rootElement;
using (FileStream fs = new FileStream(xamlFile, FileMode.Open))
{
rootElement = (DependencyObject)XamlReader.Load(fs);
}
this.Content = rootElement;
Btn1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "Btn1");
// Обработчик события клика
Btn1.Click += btn1_Click;
}
private void btn1_Click(object sender, RoutedEventArgs e)
{
Btn1.Content = "Спасибо!";
}
}
}
Здесь конструктор получает имя файла XAML в качестве аргумента (в данном случае - Window1.xaml). Он открывает FileStream и использует метод Load() класса XamlReader для преобразования содержимого этого файла в DependencyObject, являющийся базой, от которой наследуются элементы управления WPF. Этот объект DependencyObject может быть помещен внутрь контейнера любого типа (например, Panel), но в данном примере он используется как содержимое для всего окна.
В этом примере из файла XAML загружается элемент - объект DockPanel. В качестве альтернативы можно было бы загрузить все окно XAML. В данном случае объект, возвращенный XamlReader.Load(), потребуется привести к типу Window и затем вызвать его метод Show() или ShowDialog() для того, чтобы отобразить его.
Чтобы манипулировать элементом, например, кнопкой в файле Window1.xaml, необходимо найти соответствующий объект - элемент управления в динамически загруженном содержимом. Для этих целей предназначен элемент LogicalTreeHelper. Он позволяет производить поиск по всему дереву объектов - элементов управления, погружаясь на столько уровней, на сколько необходимо, пока не будет найден объект с указанным именем. Обработчик затем присоединяется к событию Button.Click.
Очевидно, что динамическая загрузка XAML не будет столь же эффективной, как компиляция XAML в BAML с последующей загрузкой BAML во время выполнения, особенно в случае сложного пользовательского интерфейса. Тем не менее, она открывает ряд возможностей для построения динамических пользовательских интерфейсов.
Например, можно было бы создать оператор общего назначения для опроса, который читает файл формы из веб-службы и затем отображает соответствующие элементы управления (метки, текстовые поля, флажки и т.п.). Файл формы может быть обычным документом XAML с дескрипторами WPF, который загружается в существующую форму с помощью XamlReader. Чтобы собрать результаты, как только форма опроса заполнена, понадобится просто перечислить все элементы управления вводом и собрать их содержимое. Другое преимущество подхода с несвязанными файлами XAML в готовом проекте состоит в том, что они позволяют использовать улучшения в стандарте XAML 2009.
Это метод, применяемый Visual Studio, который обладает рядом преимуществ, уже затронутых ранее:
Часть работы автоматизирована. Нет необходимости выполнять поиск идентификатора с помощью LogicalTreeHelper или привязывать в коде обработчики событий.
Чтение BAML-кода во время выполнения происходит быстрее, чем чтение XAML-кода.
Упрощается развертывание. Поскольку BAML-код встраивается в сборку как один или более ресурсов, его невозможно потерять.
Файлы XAML могут редактироваться в других программах, таких как инструменты графического дизайна. Это открывает возможность лучшей кооперации между программистами и дизайнерами. (Вы также получаете это преимущество, когда используете не компилированный XAML).
В Visual Studio используется двухэтапный процесс компиляции приложений WPF. Первый этап - компиляция XAML-файлов в BAML. Например, если проект включает файл по имени Window1.xaml, то компилятор создаст временный файл Window1.baml и поместит его в подпапку obj\Debug (в папке проекта). В то же время для окна создается частичный класс с использованием выбранного языка. Например, если применяется C#, то компилятор создаст файл по имени Window1.g.cs в папке obj\Debug. Здесь g означает generated (сгенерированный).
Частичный класс включает следующие вещи:
Поля для всех элементов управления в окне.
Код, загружающий BAML из сборки и тем самым создающий дерево объектов. Это случается, когда конструктор вызывает InitializeComponent().
Код, который назначает соответствующий объект элемента управления каждому полю и подключает все обработчики событий. Это происходит в методе по имени Connect(), который вызывается анализатором BAML при каждом нахождении именованного объекта.
Частичный класс не включает кода для создания экземпляра и инициализации элементов управления, потому что эта задача выполняется механизмом WPF, когда BAML-код обрабатывается методом Application.LoadComponent().
В процессе компиляции компилятор XAML должен создать частичный класс. Это возможно, только если используемый вами язык поддерживает модель .NET Code DOM. Языки C# и VB поддерживают Code DOM, но если используется язык от независимого поставщика, следует убедиться, что эта поддержка доступна, прежде чем создавать приложения со скомпилированным XAML.
Когда завершается этап компиляции XAML в BAML, Visual Studio использует компилятор соответствующего языка, чтобы скомпилировать код и сгенерированные файлы частичного класса. В случае приложения C# эту задачу решает компилятор csc.exe. Скомпилированный код становится единой сборкой (*.exe), и BAML для каждого окна встраивается как отдельный ресурс.
Ранее было показано, как использовать XAML из приложения на основе кода. Разработчики для .NET будут заниматься этим большую часть времени. Однако также возможно использовать файл XAML без создания кода. Это называется несвязанный XAML-файл. Несвязанные файлы XAML могут открываться непосредственно в Internet Explorer. (Предполагается, что платформа .NET Framework установлена.)
Если файл XAML использует код, он не может быть открыт в браузере Internet Explorer. Однако можно построить браузерное приложение под названием ХВАР, в котором это ограничение преодолено.
К этому моменту создание несвязанного XAML-файла может показаться относительно бесполезным. В конце концов, какой смысл в пользовательском интерфейсе без управляющего им кода? Однако, изучая XAML, вы откроете несколько средств, которые полностью декларативны. К ним относится анимация, триггеры, привязка данных и ссылки (которые могут указывать на другие несвязанные файлы XAML). Используя эти средства, можно строить очень простые XAML-файлы без кода. Они не будут выглядеть как завершенные приложения, но позволят делать несколько больше, чем статические страницы HTML.
Чтобы попробовать несвязанную страницу XAML, внесите в файл .xaml следующие изменения:
Удалите атрибут Class из корневого элемента.
Удалите любые атрибуты, которые присоединяют обработчики событий (такие как атрибут Button.Click).
Измените имя открывающего и закрывающего дескриптора с Window на Page. Браузер Internet Explorer может отображать только страницы, а не отдельные окна.