Темы



Применение XSLT преобразований для конвертации XAML между различными платформами

Платформы WPF и Silverlight используют язык разметки XAML для описания элементов пользовательского интерфейса, шаблонов и стилей. Если вы разрабатываете одновременно под разные XAML платформы, то естественно, возникает желание иметь общие файлы разметки для этих платформ.Разметка в WPF и Silverlight очень схожа, но имеются досадные отличия, которые сильно осложняют ее шаринг. В нашей компании эта проблема была решена несколько лет назад в виде внутреннего инструмента, который называется WPF2SL.

WPF2SL слишком специфичен, чтобы быть полезным широкой публике, поэтому мы не планируем его публиковать. В этой статье я расскажу об особенностях XSLT преобразований применительно к разметке XAML и о некоторых сложностях и особенностях, с которыми мы столкнулись.Проект WPF2SL стартовал 4 года назад, когда мы решили создать линейки компонентов для платформ WPF и Silverlight. WPF контролы у нас были готовы раньше, поэтому возникла идея сделать шаринг разметки между платформами. В то время разрыв между разметкой WPF и Silverlight был больше, чем сейчас, потому что в Silverlight 3 не было implicit styles, markup extensions и были сильно ограничены байндинги.

Кстати, некоторые наши конкуренты пошли по другому пути. У них были сначала готовы Silverlight контролы и их линейка WPF контролов получена из априори урезанной платформы, поэтому они до сих пор в полной мере не используют всех возможностей WPF платформы.

Начнем с создания System.Xml.Xsl.XslCompiledTransform. Тут всё, как написано в MSDN. Однако следует помнить, что загрузка XSLT файла методом XslCompiledTransform.Load занимает много времени, потому что в этот момент в памяти будет создана временная сборка для конечного автомата, который описан в XSLT файле.

В одной из ранних версий WPF2SL на каждый входной XAML файл производилась полная инициализация с вызовом XslCompiledTransform.Load. Это сильно замедляло работу утилиты. В XslCompiledTransform загружается XSLT файл, содержащий описания преобразований для узлов и атрибутов исходного дерева. Преобразования в XSLT файле упорядочены по возрастанию приоритета. Правило с самым низким приоритетом — первое. Это копирующее правило.

Если для узла или атрибута не найдется более приоритетного правила, от будет скопирован.Отсутствие DynamicResource в Silverlight Если просто заменить DynamicResource на StaticResource, полученная разметка будет содержать много ошибок, связанных с неверным следованием ресурсов, потому что StaticResource требует, чтобы ресурс был объявлен до его использования. Решением стало ручное упорядочивание ресурсов внутри файла. XSLT шаблон для замены DynamicResource на StaticResource выглядит так. … Проблема усложняется, когда имеются ссылки на ресурсы, объявленные в другом файле. Эту часть проблемы не удалось решить XSLT преобразованиями. Для этого у нас есть отдельный этап пост-обработки, про который надо писать отдельную статью.

Вырезание узлов и атрибутов которые отсутствуют в Silverlight Так как WPF разметка значительно богаче Silverlight разметки, нам придется вырезать узлы и атрибуты из XAML дерева. Это очень просто делается в XSLT.Пример вырезания атрибута: Пример вырезания поддерева:

Особенности преобразования ключей ресурсов И в WPF, и в Silvelight в разметке XAML можно задать ResourceDictionary, в котором будут храниться ресурсы. Ресурсы доступны по ключу. В WPF ключом может быть любой объект, а в SL ключ должен быть обязательно строковый.Для унификации, конечно можно ввести в WPF ограничение, чтобы ключом была только строка, но нам нравится строгая типизация, которая достижима именно на объектных ключах. В WPF возможно написать вот так

Где FloatingContainerThemeKey — это специальный дженерик объект, унаследованный от System.Windows.ResourceKey. Дженерик принимает параметром тип Enum, который описывает возможные названия ключей. public class FloatingContainerThemeKeyExtension: ThemeKeyExtensionBase { } public enum FloatingContainerThemeKey { FloatingContainerAdornerTemplate, FloatingContainerPopupTemplate, FloatingContainerWindowTemplate, } c79927c4309674a0fcef3e137e9cf400.pngЗа счет этого в WPF сложнее ошибиться в названии ключа в объявлении ресурса или в ссылке на ресурс.

Вернемся к преобразованию XAML. В Silverlight объектных ключей нет, поэтому

преобразуется в строчку XML namespaces Многие схожие элементы в WPF и Silverlight находятся в разных xml неймспейсах. Это различие породило вот такие шаблоны.

Когда мы поняли, что таких шаблонов придется сделать очень много, мы создали своего наследника стандартного класса XmlTextWriter, у которого перегружен метод WriteString.

public override void WriteString (string text) { if (NamespacesReplacementTable.ContainsKey (text)) base.WriteString (NamespacesReplacementTable[text]); else base.WriteString (text);

… }

Этого наследника можно отдать в метод XslCompiledTransform.Transfrom (reader, writer) в качестве второго параметра. Перегруженый WriteString в соответствии с таблицей замен подменяет неймспейс при записи.Интеграция в процесс компиляции WPF2SL — это консольное приложение. В наших SL проектах на Pre-build event прописан вызов WPF2SL с соответствующими параметрами.Но тут не все просто как кажется. Практически у всех сейчас машины с многоядерными процессорами, на которых msbuild делает одновременную сборку сразу для нескольких проектов. WPF2SL в процессе работы создавала временные файлы в Temp. Поскольку их названия совпадали, возникал конфликт доступа. Проблема была решена добавлением ID процесса к названию файла.

Диагностика проблем в XSLT преобразовании К сожалению, удобного средства диагностики XSLT преобразований нет (по крайней мере, автору о них не известно). Когда какие-то из XSLT преобразований работают не так, как ожидается, самый действенный способ — итеративная модификация XSLT с анализом результатов. Если результат преобразования сильно отличается от ожидаемого, смело помещайте в коментарий половину XSLT файла; если всё еще не понятно, еще половину и так далее. Этот способ получил у нас название: «метод половинного комента».Описанный способ является универсальным для всех декларативных языков, в том числе и для XAML. Если не понятно, какой из шаблонов сформировал неверную строчку в выходном файле, можно временно в шаблон вписать строчку, которая позволит однозначно его идентифицировать.

Выводы XSLT преобразования хорошо работают в задаче конвертации XAML разметки между различными платформами, а .NET реализация XSLT преобразований XslCompiledTransform достаточно гибкая, производительная и расширяемая.Литература Сэл Мангано. XSLT. Сборник рецептов

© Habrahabr.ru