Поделиться через


Структуры и понятия потока узлов XAML

Средства чтения XAML и записи XAML, реализованные в службах XAML .NET, основаны на концепции проектирования потока узлов XAML. Поток узлов XAML — это концепция набора узлов XAML. В этой концепции обработчик XAML проходит по структуре связей узлов в XAML одновременно. В любое время только одна текущая запись или текущая позиция существует в открытом потоке узлов XAML, и многие аспекты отчета API только сведения, доступные из этой позиции. Текущий узел в потоке узлов XAML можно описать как объект, член или значение. Рассматривая XAML как поток узлов XAML, средства чтения XAML могут взаимодействовать с средствами записи XAML и позволяют программе просматривать, взаимодействовать или изменять содержимое потока узлов XAML во время загрузки или операции сохранения пути, которая включает XAML. Проектирование API чтения и записи XAML и концепция потока узлов XAML похожи на предыдущие связанные проекты чтения и записи, такие как объектная модель XML-документа (DOM) и XmlReader и XmlWriter классов. В этом разделе рассматриваются понятия потока узлов XAML и описывается, как создавать подпрограммы, взаимодействующие с представлениями XAML на уровне узла XAML.

Загрузка XAML в средство чтения XAML

Базовый XamlReader класс не объявляет конкретный метод загрузки исходного XAML в средство чтения XAML. Вместо этого производный класс объявляет и реализует метод загрузки, включая общие характеристики и ограничения источника входных данных для XAML. Например, XamlObjectReader считывает граф объектов, начиная с входного источника одного объекта, представляющего корневой или базовый. Затем XamlObjectReader создает поток узла XAML из графа объектов.

Наиболее известные службы XAML .NET, определенные XamlReader подклассом, XamlXmlReader. XamlXmlReader загружает исходный XAML либо путем загрузки текстового файла непосредственно через поток или путь к файлу, либо косвенно через связанный класс чтения, например TextReader. XamlReader можно считать полным источником входных данных XAML после загрузки. Однако базовый API XamlReader разработан таким образом, чтобы читатель взаимодействовал с одним узлом XAML. При первой загрузке первый один узел, который вы столкнулись, является корнем XAML и его начальным объектом.

Концепция потока узлов XAML

Если вы более знакомы с DOM, метафорой дерева или подходом на основе запросов к доступу к технологиям на основе XML, полезный способ концептуального представления потока узлов XAML выглядит следующим образом. Представьте, что загруженный XAML — это DOM или дерево, где каждый возможный узел расширяется по всему пути, а затем отображается линейно. При переходе по узлам может потребоваться обход "in" или "out" уровней, которые будут иметь отношение к DOM, но поток узлов XAML явно не отслеживается, так как эти понятия уровня не относятся к потоку узлов. Поток узлов имеет "текущую" позицию, но если вы не сохранили другие части потока самостоятельно в качестве ссылок, каждый аспект потока узла, отличный от текущей позиции узла, отсутствует в представлении.

Концепция потока узлов XAML имеет заметное преимущество, что при прохождении всего потока узлов вы уверены, что вы обработали все представление XAML; Вам не нужно беспокоиться о том, что запрос, операция DOM или другой нелинейный подход к обработке информации пропустил часть полного представления XAML. По этой причине представление потока узлов XAML идеально подходит как для подключения средств чтения XAML и записи XAML, так и для предоставления системы, в которой можно вставить собственный процесс, который действует между этапами обработки XAML и операций записи. Во многих случаях порядок узлов в потоке узлов XAML намеренно оптимизирован или переупорядочен средствами чтения XAML, а также способ отображения порядка в исходном тексте, двоичном или объектном графе. Это поведение предназначено для применения архитектуры обработки XAML, в которой записи XAML никогда не находятся в положении, где они должны вернуться в поток узлов. В идеале все операции записи XAML должны иметь возможность действовать на основе контекста схемы, а также текущего положения потока узлов.

Базовый цикл чтения узла

Базовый цикл чтения узла для изучения потока узлов XAML состоит из следующих понятий. В целях циклов узлов, как описано в этом разделе, предположим, что вы читаете текстовый файл XAML, доступный для чтения с помощью XamlXmlReader. Ссылки в этом разделе относятся к конкретному API цикла узлов XAML, реализованному XamlXmlReader.

  • Убедитесь, что вы не находитесь в конце потока узлов XAML (проверьте IsEofили используйте возвращаемое значение Read). Если вы находитесь в конце потока, текущий узел отсутствует и вы должны выйти.

  • Проверьте тип узла, предоставляемый потоком узлов XAML, вызывая NodeType.

  • Если у вас есть связанный модуль записи объектов XAML, подключенный напрямую, обычно вызывается WriteNode на этом этапе.

  • В зависимости от того, XamlNodeType сообщается как текущий узел или текущая запись, вызовите один из следующих элементов, чтобы получить сведения о содержимом узла:

    • Для NodeTypeStartMember или EndMemberвызовите Member, чтобы получить XamlMember сведения о члене. Элемент может быть XamlDirective, поэтому не обязательно является обычным элементом, определенным типом предыдущего объекта. Например, x:Name, примененные к объекту, отображаются как член XAML, где IsDirective имеет значение true, и Name элемента Name, а другие свойства указывают на то, что эта директива находится в пространстве имен XAML языка XAML.

    • Для NodeTypeStartObject или EndObjectвызовите Type, чтобы получить XamlType сведения об объекте.

    • Для NodeTypeValueвызов Value. Узел является значением только в том случае, если это самое простое выражение значения элемента или текст инициализации для объекта (однако следует учитывать поведение преобразования типов, как описано в предстоящем разделе этого раздела).

    • Для NodeTypeNamespaceDeclarationвызовите Namespace, чтобы получить сведения о пространстве имен для узла пространства имен.

  • Вызовите Read для перехода средства чтения XAML к следующему узлу в потоке узлов XAML и повторите шаги еще раз.

Поток узлов XAML, предоставляемый средствами чтения XAML служб XAML .NET, всегда предоставляет полный, глубокий обход всех возможных узлов. Типичные методы управления потоками для цикла узла XAML включают определение текста в while (reader.Read())и включение NodeType в каждой точке узла в цикле узла.

Если поток узла находится в конце файла, текущий узел имеет значение NULL.

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

XamlXmlReader xxr = new XamlXmlReader(new StringReader(xamlStringToLoad));
//where xamlStringToLoad is a string of well formed XAML
XamlObjectWriter xow = new XamlObjectWriter(xxr.SchemaContext);
while (xxr.Read()) {
  xow.WriteNode(xxr);
}

Этот базовый пример цикла узла XAML пути загрузки прозрачно подключает средство чтения XAML и записи XAML, не выполняя ничего другого, чем если бы вы использовали XamlServices.Parse. Но эта базовая структура затем расширяется, чтобы применить к сценарию чтения или записи. Ниже приведены некоторые возможные сценарии.

  • Включите NodeType. Выполняйте различные действия в зависимости от типа узла, считываемого.

  • Не вызывайте WriteNode во всех случаях. Вызов WriteNode только в некоторых случаях NodeType.

  • В логике для определенного типа узла анализируйте особенности этого узла и действовать над ними. Например, можно создавать только объекты, поступающие из определенного пространства имен XAML, а затем удалять или откладывать объекты, не из этого пространства имен XAML. Или вы можете удалить или повторно обработать любые директивы XAML, которые система XAML не поддерживает в процессе обработки члена.

  • Определите пользовательский XamlObjectWriter, переопределяющий методы Write*, возможно, выполняя сопоставление типов, которое проходит контекст схемы XAML.

  • Создайте XamlXmlReader для использования контекста схемы XAML, отличного от языка XAML, чтобы настраиваемые различия в поведении XAML использовались как средством чтения, так и средства записи.

Доступ к XAML за пределами концепции цикла узлов

Возможно, существуют другие способы работы с представлением XAML, отличного от цикла узла XAML. Например, может существовать средство чтения XAML, которое может считывать индексированные узлы или, в частности, обращаться к узлам напрямую по x:Name, x:Uidили с помощью других идентификаторов. Службы XAML .NET не предоставляют полную реализацию, но предоставляют предлагаемый шаблон через службы и типы поддержки. Дополнительные сведения см. в IXamlIndexingReader и XamlNodeList.

Работа с текущим узлом

Большинство сценариев, использующих цикл узла XAML, не только считывают узлы. Большинство сценариев обрабатывают текущие узлы и передают каждый узел одновременно в реализацию XamlWriter.

В типичном сценарии пути загрузки XamlXmlReader создает поток узлов XAML; узлы XAML обрабатываются в соответствии с логикой и контекстом схемы XAML; и узлы передаются в XamlObjectWriter. Затем вы интегрируете результирующий граф объектов в приложение или платформу.

В типичном сценарии сохранения пути XamlObjectReader считывает граф объектов, обрабатываются отдельные узлы XAML, а XamlXmlWriter выводит сериализованный результат в виде текстового файла XAML. Ключ заключается в том, что оба пути и сценарии включают работу с одним узлом XAML за раз, а узлы XAML доступны для лечения стандартизированным способом, который определяется системой типов XAML и the.NET API служб XAML.

Кадры и область

Цикл узла XAML проходит по потоку узлов XAML линейно. Поток узла проходит по объектам, в элементы, содержащие другие объекты, и т. д. Часто полезно отслеживать область в потоке узлов XAML, реализуя концепцию кадра и стека. Это особенно верно, если вы активно настраиваете поток узлов во время его выполнения. Поддержка кадров и стека, реализуемая в рамках логики цикла узла, может подсчитать StartObject (или GetObject) и EndObject области при переходе к структуре узла XAML, если структура считается с точки зрения DOM.

Обход и ввод узлов объектов

Первый узел в потоке узлов при открытии средством чтения XAML — это узел начального объекта корневого объекта. По определению этот объект всегда является одним узлом объекта и не имеет одноранговых узлов. В любом примере XAML в реальном мире корневой объект определяется для одного или нескольких свойств, которые содержат больше объектов, и эти свойства имеют узлы-члены. Затем узлы-члены имеют один или несколько узлов объектов или могут завершиться в узле значений. Корневой объект обычно определяет области имен XAML, которые синтаксически назначаются как атрибуты в текстовой разметке XAML, но сопоставляются с типом узла Namescope в представлении потока узлов XAML.

Рассмотрим следующий пример XAML (это произвольный КОД XAML, не поддерживаемый существующими типами в .NET). Предположим, что в этой объектной модели FavorCollectionList<T>Favor, Balloon и NoiseMaker можно назначить Favor, свойство Balloon.Color поддерживается объектом Color аналогично тому, как WPF определяет цвета как известные имена цветов, а Color поддерживает преобразователь типов для синтаксиса атрибутов.

Разметка XAML Результирующий поток узлов XAML
<Party узел Namespace для Party
xmlns="PartyXamlNamespace"> узел StartObject для Party
<Party.Favors> узел StartMember для Party.Favors
узел StartObject для неявных FavorCollection
узел StartMember для неявного свойства элементов FavorCollection.
<Balloon узел StartObject для Balloon
Color="Red" узел StartMember для Color

узел Value для строки значения атрибута "Red"

EndMember для Color
HasHelium="True" узел StartMember для HasHelium

узел Value для строки значения атрибута "True"

EndMember для HasHelium
> EndObject для Balloon
<NoiseMaker>Loudest</NoiseMaker> узел StartObject для NoiseMaker

узел StartMember для _Initialization

узел Value для строки значения инициализации "Loudest"

узел EndMember для _Initialization

EndObject для NoiseMaker
узел EndMember для неявного свойства элементов FavorCollection.
узел EndObject для неявных FavorCollection
</Party.Favors> EndMember для Favors
</Party> EndObject для Party

В потоке узлов XAML можно использовать следующее поведение:

  • Если узел Namespace существует, он добавляется в поток непосредственно перед StartObject, который объявил пространство имен XAML с xmlns. Просмотрите предыдущую таблицу с помощью XAML и примера потока узлов еще раз. Обратите внимание, что StartObject и Namespace узлы, как представляется, транспонируются по сравнению с их позициями объявления в текстовой разметке. Это является репрезентативным поведением, когда узлы пространства имен всегда отображаются перед узлом, к который они применяются в потоке узлов. Цель этого проектирования заключается в том, что сведения о пространстве имен жизненно важны для записи объектов и должны быть известны перед попыткой записи объектов выполнить сопоставление типов или иначе обработать объект. Размещение сведений о пространстве имен XAML перед областью приложения в потоке упрощает всегда обрабатывать поток узлов в указанном порядке.

  • Из-за приведенного выше рассмотрения это один или несколько Namespace узлов, которые сначала считываются в большинстве реальных случаев разметки при обходе узлов с самого начала, а не StartObject корневого узла.

  • За узлом StartObject можно следовать StartMember, Valueили немедленно EndObject. Он никогда не следует немедленно другим StartObject.

  • За StartMember можно следовать StartObject, Valueили немедленный EndMember. За ним можно следовать GetObjectдля элементов, где значение должно поступать из существующего значения родительского объекта, а не StartObject, создающего новое значение. За ним также может следовать узел Namespace, который применяется к предстоящему StartObject. Он никогда не следует немедленно другим StartMember.

  • Узел Value представляет само значение; Нет "EndValue". За ним может следовать только EndMember.

    • Текст инициализации XAML объекта, который может использоваться конструкцией, не приводит к Object-Value структуре. Вместо этого создается выделенный узел-член для члена с именем _Initialization. и этот узел-член содержит строку значения инициализации. Если он существует, _Initialization всегда является первым StartMember. _Initialization могут быть квалифицированы в некоторых представлениях служб XAML с помощью области имен XAML языка XAML, чтобы уточнить, что _Initialization не является определенным свойством в резервных типах.

    • Сочетание Member-Value представляет параметр атрибута значения. В конечном итоге может быть преобразователь значений, участвующий в обработке этого значения, и значение является обычной строкой. Однако это не вычисляется до тех пор, пока модуль записи объектов XAML обрабатывает этот поток узлов. Модуль записи объектов XAML имеет необходимый контекст схемы XAML, сопоставление систем типов и другую поддержку, необходимую для преобразования значений.

  • За узлом EndMember может следовать узел StartMember для последующего члена или узел EndObject для владельца члена.

  • За узлом EndObject может следовать узел EndMember. За ним также может следовать узел StartObject для случаев, когда объекты являются одноранговыми элементами коллекции. Кроме того, за ним может следовать узел Namespace, который применяется к предстоящему StartObject.

    • Для уникального случая закрытия всего потока узлов EndObject корневого каталога не следует ничего; Средство чтения теперь является конечным, и Read возвращает false.

Преобразователи значений и поток узлов XAML

Преобразователь значений — это общий термин расширения разметки, преобразователь типов (включая сериализаторы значений) или другой выделенный класс, который передается в качестве преобразователя значений через систему типов XAML. В потоке узлов XAML использование преобразователя типов и расширение разметки имеют очень разные представления.

Преобразователи типов в потоке узлов XAML

Набор атрибутов, который в конечном итоге приводит к использованию преобразователя типов, сообщается в потоке узлов XAML в качестве значения элемента. Поток узлов XAML не пытается создать объект экземпляра преобразователя типов и передать в него значение. Использование реализации преобразования преобразователя типов требует вызова контекста схемы XAML и его использования для сопоставления типов. Даже определение класса преобразователя типов для обработки значения требует косвенного контекста схемы XAML. При использовании контекста схемы XAML по умолчанию эта информация доступна в системе типов XAML. Если перед подключением к записи XAML необходимо получить сведения о классе преобразователя типов на уровне потока узла XAML, его можно получить из XamlMember сведений о заданном элементе. Но в противном случае входные данные преобразователя типов должны сохраняться в потоке узлов XAML в виде обычного значения до тех пор, пока не будут выполнены остальные операции, требующие выполнения системы сопоставления типов и контекста схемы XAML, например создание объекта с помощью модуля записи объектов XAML.

Например, рассмотрим следующую структуру определения класса и использование XAML.

public class BoardSizeConverter : TypeConverter {
  //converts from string to an int[2] by splitting on an "x" char
}
public class GameBoard {
  [TypeConverter(typeof(BoardSizeConverter))]
  public int[] BoardSize; //2x2 array, initialization not shown
}
<GameBoard BoardSize="8x8"/>

Текстовое представление потока узлов XAML для этого использования может быть выражено следующим образом:

StartObject с XamlType, представляющей GameBoard

StartMember с XamlMember, представляющей BoardSize

узел Value с текстовой строкой "8x8"

EndMember совпадений BoardSize

EndObject совпадений GameBoard

Обратите внимание, что в этом потоке узлов нет экземпляра преобразователя типов. Но вы можете получить сведения о преобразователе типов, вызвав XamlMember.TypeConverter на XamlMember для BoardSize. Если у вас есть допустимый контекст схемы XAML, можно также вызвать методы преобразователя, получив экземпляр из ConverterInstance.

Расширения разметки в потоке узлов XAML

Использование расширения разметки сообщается в потоке узлов XAML в качестве узла объекта в элементе, где объект представляет экземпляр расширения разметки. Таким образом, использование расширения разметки более явно представлено в представлении потока узлов, чем использование преобразователя типов, и содержит дополнительные сведения. XamlMember информация не могла рассказать вам ничего о расширении разметки, так как использование является ситуационной и зависит в каждом возможном случае разметки; он не является выделенным и неявным для каждого типа или члена, как и в случае с преобразователями типов.

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

В узле объекта расширения разметки могут быть члены этого расширения разметки. Представление потока узлов XAML сохраняет использование этого расширения разметки, будь то позиционный параметр или использование с явными именованными параметрами.

Для использования позиционного параметра поток узла XAML содержит определяемое языком xaml свойство _PositionalParameters, записывающее использование. Это свойство является универсальным List<T> с ограничением Object. Ограничение является объектом, а не строкой, так как, возможно, использование позиционного параметра может содержать вложенные расширения разметки в нем. Чтобы получить доступ к позициальным параметрам из использования, можно выполнить итерацию по списку и использовать индексаторы для отдельных значений списка.

Для именованного использования параметров каждый именованный параметр представлен как узел-член этого имени в потоке узлов. Значения элементов не обязательно являются строками, так как может быть вложенное расширение разметки.

ProvideValue из расширения разметки еще не вызывается. Однако он вызывается при подключении средства чтения XAML и записи XAML, чтобы WriteEndObject вызывается на узле расширения разметки при его проверке в потоке узлов. По этой причине для формирования графа объектов в пути загрузки обычно требуется тот же контекст схемы XAML, что и для формирования графа объектов. В противном случае ProvideValue из любого расширения разметки могут вызывать исключения здесь, так как у него нет ожидаемых служб.

Элементы XAML и XML Language-Defined в потоке узлов XAML

Некоторые члены представлены в потоке узлов XAML из-за интерпретаций и соглашений средства чтения XAML, а не с помощью явного XamlMember подстановки или построения. Часто эти члены являются директивами XAML. В некоторых случаях это акт чтения XAML, который вводит директиву в поток узлов XAML. Другими словами, исходный входной текст XAML не явно указал директиву-член, но средство чтения XAML вставляет директиву, чтобы удовлетворить структурное соглашение XAML и сообщить сведения в потоке узлов XAML перед потерей этой информации.

В следующем списке отмечается все случаи, когда средство чтения XAML должно вводить узел-член директивы XAML и как этот узел-член определяется в реализации служб XAML .NET.

  • текст инициализации узла объекта: имя этого узла-члена _Initialization, он представляет директиву XAML и определяется в пространстве имен XAML языка XAML. Вы можете получить статическую сущность для нее из Initialization.

  • позиционные параметры расширения разметки: имя этого узла-члена _PositionalParametersи определяется в пространстве имен XAML языка XAML. Он всегда содержит универсальный список объектов, каждый из которых является позициальным параметром, предварительно разделенным разделением на символе разделителя ,, как указано в входном XAML. Вы можете получить статическую сущность для директивы позиционных параметров из PositionalParameters.

  • Неизвестное содержимое: имя этого узла-члена _UnknownContent. Строго говоря, это XamlDirective, и он определен в пространстве имен XAML языка XAML. Эта директива используется в качестве sentinel в случаях, когда элемент объекта XAML содержит содержимое в исходном XAML, но свойство содержимого не может быть определено в контексте схемы XAML в настоящее время. Этот случай можно обнаружить в потоке узлов XAML, проверив элементы с именем _UnknownContent. Если ни одно другое действие не выполняется в потоке узлов XAML пути загрузки, XamlObjectWriter по умолчанию вызывает попытку WriteEndObject при обнаружении элемента _UnknownContent на любом объекте. XamlXmlWriter по умолчанию не создает и обрабатывает элемент как неявный. Вы можете получить статическую сущность для _UnknownContent из UnknownContent.

  • свойство коллекции коллекции: Хотя резервный тип CLR класса коллекции, используемого для XAML, обычно имеет выделенное именованное свойство, которое содержит элементы коллекции, это свойство не известно системе типов XAML до разрешения типа xaml. Вместо этого поток узлов XAML представляет заполнитель Items в качестве члена типа XAML коллекции. В реализации служб XAML .NET имя этой директивы или члена в потоке узлов _Items. Константу для этой директивы можно получить из Items.

    Обратите внимание, что поток узлов XAML может содержать свойство Items с элементами, которые, как представляется, не синтаксический анализ на основе разрешения типов резервного копирования и контекста схемы XAML. Например

  • определяемые XML-элементы: определяемые XML xml:base, xml:lang и xml:space члены передаются как директивы XAML с именем base, langи space в реализации служб XAML .NET. Пространство имен для них — это пространство имен XML http://www.w3.org/XML/1998/namespace. Константы для каждого из них можно получить из XamlLanguage.

Порядок узлов

В некоторых случаях XamlXmlReader изменяет порядок узлов XAML в потоке узлов XAML, а также порядок отображения узлов при просмотре в разметке или обработке как XML. Это делается для того, чтобы упорядочить узлы таким образом, чтобы XamlObjectWriter могли обрабатывать поток узлов только вперед. В службах XAML .NET узлы средства чтения XAML переупорядочены, а не покидают эту задачу записи XAML в качестве оптимизации производительности для потребителей модуля записи объектов XAML потока узлов.

Некоторые директивы предназначены специально для предоставления дополнительных сведений о создании объекта из элемента объекта. Эти директивы: Initialization, PositionalParameters, TypeArguments, FactoryMethod, Arguments. Средства чтения XAML служб .NET XAML пытаются разместить эти директивы в качестве первых членов потока узлов после StartObjectобъекта по причинам, описанным в следующем разделе.

Поведение XamlObjectWriter и порядок узлов

StartObject XamlObjectWriter не обязательно сигнал для записи объектов XAML для немедленного создания экземпляра объекта. XAML включает несколько языковых функций, которые позволяют инициализировать объект с дополнительными входными данными, и не полагаться полностью на вызов конструктора без параметров для создания исходного объекта, а затем задавать свойства. К таким функциям относятся: XamlDeferLoadAttribute; текст инициализации; x:TypeArguments; позиционные параметры расширения разметки; методы фабрики и связанные x:Arguments узлов (XAML 2009). Каждый из этих случаев задерживает фактическое построение объекта, и поскольку поток узлов переупорядочен, модуль записи объектов XAML может полагаться на поведение фактического создания экземпляра при обнаружении элемента запуска, который не является директивой построения для этого типа объекта.

GetObject

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

См. также