共用方式為


XAML 節點數據流結構和概念

在 .NET XAML 服務中實作的 XAML 讀取器和 XAML 寫入器是以 XAML 節點數據流的設計概念為基礎。 XAML 節點數據流是一組 XAML 節點的概念化。 在此概念化中,XAML 處理器會逐一逐步解說 XAML 中節點關聯性的結構。 在任何時候,只有一個目前記錄或目前位置存在於開啟的 XAML 節點數據流中,而 API 的許多層面只會報告該位置中可用的資訊。 XAML 節點數據流中的目前節點可以描述為對象、成員或值。 藉由將 XAML 視為 XAML 節點數據流,XAML 讀取器可以與 XAML 寫入器通訊,並讓程式在載入路徑或涉及 XAML 的儲存路徑作業期間,檢視、互動或改變 XAML 節點數據流的內容。 XAML 讀取器和寫入器 API 設計和 XAML 節點數據流概念類似於先前的相關讀取器和寫入器設計和概念,例如 XML 檔物件模型(DOM)和 XmlReaderXmlWriter 類別。 本主題討論 XAML 節點數據流概念,並說明如何撰寫與 XAML 節點層級 XAML 表示法互動的例程。

將 XAML 載入 XAML 讀取器

基底 XamlReader 類別不會宣告將初始 XAML 載入 XAML 讀取器的特定技術。 相反地,衍生類別會宣告並實作載入技術,包括 XAML 輸入來源的一般特性和條件約束。 例如,XamlObjectReader 會從代表根或基底之單一對象的輸入來源開始讀取物件圖形。 XamlObjectReader 然後從物件圖形產生 XAML 節點數據流。

最突出的 .NET XAML 服務定義 XamlReader 子類別是 XamlXmlReaderXamlXmlReader 載入初始 XAML,方法是直接透過資料流或檔案路徑載入文字檔,或間接透過相關的讀取器類別,例如 TextReaderXamlReader 可以視為在載入 XAML 輸入來源之後包含整個 XAML 輸入來源。 不過,XamlReader 基底 API 的設計目的是讓讀取器與 XAML 的單一節點互動。 第一次載入時,您遇到的第一個單一節點是 XAML 的根節點及其起始物件。

XAML 節點數據流概念

如果您更熟悉 DOM、樹狀結構隱喻或以查詢為基礎的方法來存取 XML 型技術,則概念化 XAML 節點數據流的實用方式如下。 假設載入的 XAML 是 DOM 或樹狀結構,其中每個可能的節點都會一路展開,然後以線性方式呈現。 當您前進到節點時,您可能會周遊與 DOM 相關的層級「in」或「out」,但 XAML 節點數據流不會明確追蹤,因為這些層級概念與節點數據流無關。 節點數據流具有「目前」位置,但除非您已自行將數據流的其他部分儲存為參考,否則目前節點位置以外的節點數據流的每個層面都會超出檢視。

XAML 節點數據流概念具有顯著優勢,如果您經歷整個節點數據流,您確信您已處理整個 XAML 表示法;您不需要擔心查詢、DOM 作業或其他處理資訊的非線性方法遺漏了完整 XAML 表示法的一部分。 基於這個理由,XAML 節點數據流表示法適用於連接 XAML 讀取器和 XAML 寫入器,並提供可在 XAML 處理作業的讀取和寫入階段之間執行自己的進程。 在許多情況下,XAML 節點數據流中的節點順序會經過 XAML 讀取器刻意優化或重新排序,以及來源文字、二進位或物件圖形中顯示的順序。 此行為旨在強制執行 XAML 處理架構,其中 XAML 寫入器絕不會在節點數據流中「返回」的位置。 在理想情況下,所有 XAML 寫入作業都應該能夠根據架構內容以及節點數據流的目前位置採取行動。

基本讀取節點迴圈

用來檢查 XAML 節點數據流的基本讀取節點迴圈包含下列概念。 基於本主題中所討論的節點迴圈目的,假設您使用 XamlXmlReader來讀取以文字為基礎的人類可讀取 XAML 檔案。 本節中的連結是指由 XamlXmlReader實作的特定 XAML 節點迴圈 API。

  • 請確定您不在 XAML 節點資料流結尾(請檢查 IsEof,或使用 Read 傳回值)。 如果您位於數據流結尾,則沒有目前的節點,您應該結束。

  • 藉由呼叫 NodeType,檢查 XAML 節點數據流目前公開的節點類型。

  • 如果您有直接連接的相關聯 XAML 物件寫入器,您通常會在此點呼叫 WriteNode

  • 根據報告為目前節點或目前記錄的 XamlNodeType,呼叫下列其中一項以取得節點內容的相關信息:

    • 如需 StartMemberEndMemberNodeType,請呼叫 Member 以取得成員的 XamlMember 資訊。 成員可能是 XamlDirective,因此不一定是上述對象的傳統型別定義成員。 例如,套用至 物件的 x:Name 會顯示為 XAML 成員,其中 IsDirective 為 true,而成員的 NameName,而其他屬性則表示這個指示詞位於 XAML 語言 XAML 命名空間底下。

    • 如需 StartObjectEndObjectNodeType,請呼叫 Type 以取得物件的 XamlType 資訊。

    • 針對 ValueNodeType,呼叫 Value。 只有當節點是成員之值的最簡單表達式或物件的初始化文字時,節點才會是值(不過,您應該注意本主題即將上一節所記載的類型轉換行為)。

    • 針對 NamespaceDeclarationNodeType,呼叫 Namespace 以取得命名空間節點的命名空間資訊。

  • 呼叫 Read,將 XAML 讀取器移至 XAML 節點數據流中的下一個節點,然後再次重複步驟。

.NET XAML 服務 XAML 讀取器所提供的 XAML 節點數據流一律會提供所有可能節點的完整深度周遊。 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。 在某些 NodeType 案例中,只呼叫 WriteNode

  • 在特定節點類型的邏輯中,分析該節點的詳細數據,並對其採取行動。 例如,您只能寫入來自特定 XAML 命名空間的物件,然後卸除或延遲來自該 XAML 命名空間的任何物件。 或者,您可以卸除或重新處理 XAML 系統不支援做為成員處理一部分的任何 XAML 指示詞。

  • 定義覆寫 Write* 方法的自定義 XamlObjectWriter,可能執行略過 XAML 架構內容的型別對應。

  • 建構 XamlXmlReader 以使用非預設的 XAML 架構內容,讓讀取器和寫入器同時使用 XAML 行為的自定義差異。

存取節點迴圈概念以外的 XAML

除了做為 XAML 節點迴圈之外,還有其他方法可以使用 XAML 表示法。 例如,可能有 XAML 讀取器可以讀取索引節點,或透過 x:Namex:Uid或其他標識碼直接存取節點。 .NET XAML 服務不提供完整的實作,但透過服務和支援類型提供建議的模式。 如需詳細資訊,請參閱 IXamlIndexingReaderXamlNodeList

使用目前的節點

大部分使用 XAML 節點迴圈的案例不只會讀取節點。 大部分案例會處理目前的節點,並一次將每個節點一個傳遞至 XamlWriter的實作。

在典型的載入路徑案例中,XamlXmlReader 會產生 XAML 節點數據流;XAML 節點會根據您的邏輯和 XAML 架構內容進行處理;與節點會傳遞至 XamlObjectWriter。 接著,您會將產生的物件圖形整合到您的應用程式或架構中。

在典型的儲存路徑案例中,XamlObjectReader 會讀取物件圖形、處理個別的 XAML 節點,以及 XamlXmlWriter 輸出串行化的結果做為 XAML 文字檔。 關鍵是路徑和案例都牽涉到一次只處理一個 XAML 節點,而 XAML 節點可以使用 XAML 類型系統所定義的標準化方式來處理,並 the.NET XAML 服務 API。

框架和範圍

XAML 節點循環會以線性方式逐步解說 XAML 節點數據流。 節點數據流會周遊至 物件、包含其他對象的成員等等。 實作框架和堆疊概念來追蹤 XAML 節點數據流中的範圍通常很有用。 如果您正在主動調整節點串流,當您在節點中時,這尤其如此。 您在節點循環邏輯中實作的框架和堆疊支援,可以在從 DOM 的觀點考慮結構時,計算 StartObject(或 GetObject)和 EndObject 範圍。

周遊和輸入物件節點

XAML 讀取器開啟節點數據流中的第一個節點是根物件的 start-object 節點。 根據定義,這個物件一律是單一對象節點,而且沒有對等。 在任何真實世界的 XAML 範例中,根物件會定義為具有一或多個保存更多對象的屬性,而且這些屬性具有成員節點。 成員節點接著會有一或多個對象節點,或也可能改為終止於值節點中。 根物件通常會定義 XAML 名稱範圍,這些範圍是以語法方式指派為 XAML 文字標記中的屬性,但會對應至 XAML 節點數據流表示法中的 Namescope 節點類型。

請考慮下列 XAML 範例(這是任意 XAML,不受 .NET 中現有類型支援)。 假設在這個物件模型中,FavorCollectionFavorList<T>BalloonNoiseMaker 可以指派給 FavorBalloon.Color 屬性是由 Color 物件所支援,類似於 WPF 如何定義色彩為已知色彩名稱,Color 支援屬性語法的類型轉換器。

XAML 標記 產生的 XAML 節點數據流
<Party PartyNamespace 節點
xmlns="PartyXamlNamespace"> PartyStartObject 節點
<Party.Favors> Party.FavorsStartMember 節點
隱含 FavorCollectionStartObject 節點
隱含 FavorCollection items 屬性 StartMember 節點。
<Balloon BalloonStartObject 節點
Color="Red" ColorStartMember 節點

屬性值字串的 Value 節點 "Red"

ColorEndMember
HasHelium="True" HasHeliumStartMember 節點

屬性值字串的 Value 節點 "True"

HasHeliumEndMember
> BalloonEndObject
<NoiseMaker>Loudest</NoiseMaker> NoiseMakerStartObject 節點

_InitializationStartMember 節點

初始化值字串的 Value 節點 "Loudest"

_InitializationEndMember 節點

NoiseMakerEndObject
隱含 FavorCollection items 屬性 EndMember 節點。
隱含 FavorCollectionEndObject 節點
</Party.Favors> FavorsEndMember
</Party> PartyEndObject

在 XAML 節點資料流中,您可以依賴下列行為:

  • 如果 Namespace 節點存在,則會在宣告具有 xmlns的 XAML 命名空間 StartObject 之前,立即將它新增至數據流。 再次查看上一個數據表,其中包含 XAML 和範例節點數據流。 請注意 StartObjectNamespace 節點在文字標記中似乎如何轉置與其宣告位置。 這是命名空間節點一律出現在節點數據流中套用至之節點之前的行為。 此設計的目的是命名空間資訊對於物件寫入器至關重要,而且必須在物件寫入器嘗試執行類型對應或處理物件之前知道。 將 XAML 命名空間資訊放在資料流中的應用程式範圍之前,可讓您更輕鬆地依呈現的順序處理節點數據流。

  • 基於上述考慮,當您從頭周遊節點時,在大部分真實世界標記案例中先讀取的一或多個 Namespace 節點,而不是根目錄的 StartObject

  • StartObject 節點後面可以接著 StartMemberValue或立即 EndObject。 它永遠不會緊接著另一個 StartObject

  • StartMember 後面可以接著 StartObjectValue或立即 EndMember。 對於值應該來自父對象的現有值,而不是具現化新值 StartObject 的成員,它可以接著 GetObject。 它也可以接著 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 節點資料流的文字表示法可以如下表示:

代表 GameBoardXamlTypeStartObject

代表 BoardSizeXamlMemberStartMember

Value 節點,具有文字字串 “8x8

EndMember 符合 BoardSize

EndObject 符合 GameBoard

請注意,此節點數據流中沒有類型轉換器實例。 但是,您可以在 BoardSizeXamlMember 上呼叫 XamlMember.TypeConverter,以取得類型轉換器資訊。 如果您有有效的 XAML 架構內容,您也可以從 ConverterInstance取得實例來叫用轉換器方法。

XAML 節點數據流中的標記延伸

標記延伸使用方式會在 XAML 節點數據流中回報為成員內的物件節點,其中 物件代表標記延伸實例。 因此,在節點數據流表示中,標記延伸使用方式比類型轉換器使用方式更明確,而且會攜帶更多資訊。 XamlMember 資訊無法告訴您有關標記延伸的任何資訊,因為使用方式是情況的,而且在每個可能的標記案例中各有不同:它不是專用且隱含的每個類型或成員,就像類型轉換器的情況一樣。

標記延伸作為對象節點的節點數據流表示,即使標記延伸使用方式是以 XAML 文字標記中的屬性形式進行(通常是這種情況)。 使用明確物件專案表單的標記延伸用法會以相同方式處理。

在標記延伸對象節點內,可能會有該標記延伸的成員。 XAML 節點數據流表示法會保留該標記延伸的使用方式,無論是位置參數使用方式,還是使用明確具名參數的用法。

對於位置參數使用方式,XAML 節點數據流會包含記錄使用方式的 XAML 語言定義屬性 _PositionalParameters。 此屬性是具有 Object 條件約束的泛型 List<T>。 條件約束是物件,而不是字串,因為可想而知位置參數使用方式可能包含其內的巢狀標記延伸用法。 若要從使用方式存取位置參數,您可以逐一查看清單,並使用索引器進行個別清單值。

針對具名參數使用方式,每個具名參數都會以節點數據流中該名稱的成員節點表示。 成員值不一定是字串,因為可能有巢狀標記延伸用法。

尚未從標記延伸叫用 ProvideValue。 不過,如果您連接 XAML 讀取器和 XAML 寫入器,以便在節點數據流中檢查 WriteEndObject 時,會在標記延伸節點上叫用它。 基於這個理由,您通常需要與 使用相同的 XAML 架構內容,才能在載入路徑上形成物件圖形。 否則,來自任何標記延伸的 ProvideValue 可能會在這裡擲回例外狀況,因為它沒有預期的服務可供使用。

XAML 和 XML Language-Defined XAML 節點數據流中的成員

某些成員會因為 XAML 讀取器的解譯和慣例而引入 XAML 節點數據流,而不是透過明確的 XamlMember 查閱或建構。 這些成員通常是 XAML 指示詞。 在某些情況下,它是讀取將 指示詞引入 XAML 節點數據流的動作。 換句話說,原始輸入 XAML 文字並未明確指定成員指示詞,但 XAML 讀取器會插入 指示詞,以滿足結構化 XAML 慣例,並在該資訊遺失之前,在 XAML 節點數據流中報告資訊。

下列清單會指出 XAML 讀取器預期引進指示詞 XAML 成員節點,以及如何在 .NET XAML 服務實作中識別該成員節點的所有案例。

  • 對象節點的初始化文字: 這個成員節點的名稱是 _Initialization,它代表 XAML 指示詞,而且定義在 XAML 語言 XAML 命名空間中。 您可以從 Initialization取得靜態實體。

  • 標記延伸的位置參數: 這個成員節點的名稱是 _PositionalParameters,而且定義在 XAML 語言 XAML 命名空間中。 它一律包含物件的泛型清單,每個物件都是預先分隔的位置參數,方法是在輸入 XAML 中提供的 , 分隔符上分割。 您可以從 PositionalParameters取得位置參數指示詞的靜態實體。

  • 未知的內容: 此成員節點的名稱 _UnknownContent。 嚴格來說,它是 XamlDirective,而且是在 XAML 語言 XAML 命名空間中定義。 這個指示詞會當做 sentinel,用於 XAML 物件專案包含來源 XAML 中的內容,但目前可用的 XAML 架構內容下無法判斷任何內容屬性。 您可以藉由檢查名為 _UnknownContent的成員,在 XAML 節點數據流中偵測此案例。 如果載入路徑 XAML 節點資料流中沒有採取其他動作,則當任何物件上遇到 _UnknownContent 成員時,預設 XamlObjectWriter 會在嘗試 WriteEndObject 擲回。 默認 XamlXmlWriter 不會擲回,並將成員視為隱含。 您可以從 UnknownContent取得 _UnknownContent 的靜態實體。

  • 集合的 Collection 屬性: 雖然用於 XAML 的集合類別支援 CLR 類型通常具有保存集合專案的專用具名屬性,但在支援型別解析之前,XAML 類型系統不知道該屬性。 相反地,XAML 節點數據流會引進 Items 佔位元元做為集合 XAML 類型的成員。 在 .NET XAML 服務實作中,節點數據流中這個指示詞或成員的名稱 _Items。 這個指示詞的常數可以從 Items取得。

    請注意,XAML 節點數據流可能包含 Items 屬性,其中的專案會根據支援類型解析和 XAML 架構內容而無法剖析。 例如

  • XML 定義的成員: .NET XAML Services 實作中,XML 定義 xml:basexml:langxml:space 成員會回報為名為 baselangspace 的 XAML 指示詞。 這些命名空間是 XML 命名空間 http://www.w3.org/XML/1998/namespace。 每個常數都可以從 XamlLanguage取得。

節點順序

在某些情況下,XamlXmlReader 變更 XAML 節點數據流中 XAML 節點的順序,與在標記中檢視或當成 XML 處理時,節點出現的順序。 這樣做是為了排序節點,讓 XamlObjectWriter 能夠以正向方式處理節點數據流。 在 .NET XAML 服務中,XAML 讀取器會重新排序節點,而不是將此工作留給 XAML 寫入器,做為節點數據流之 XAML 物件寫入器取用者的效能優化。

特定指示詞是專門用來提供從物件專案建立對象的詳細資訊。 這些指示詞包括:InitializationPositionalParametersTypeArgumentsFactoryMethodArguments。 .NET XAML 服務 XAML 讀取器嘗試將這些指示詞放在節點數據流中的第一個成員之後,物件 StartObject,原因如下一節所述。

XamlObjectWriter 行為和節點順序

StartObjectXamlObjectWriter 不一定是 XAML 物件寫入器的訊號,可立即建構物件實例。 XAML 包含數種語言功能,可讓您使用其他輸入初始化物件,而且不完全依賴叫用無參數建構函式來產生初始物件,然後才設定屬性。 這些功能包括:XamlDeferLoadAttribute;初始化文字;x:TypeArguments;標記延伸的位置參數;factory 方法和相關聯的 x:Arguments 節點 (XAML 2009)。 每個案例都會延遲實際的物件建構,而且因為節點數據流已重新排序,因此 XAML 物件寫入器可以依賴在遇到不是該物件類型的建構指示詞時實際建構實例的行為。

GetObject

GetObject 代表 XAML 節點,而不是建構新的物件,XAML 物件寫入器應該改為取得物件的包含屬性的值。 在 XAML 節點數據流中遇到 GetObject 節點是用於集合物件或字典物件時,當支援型別的物件模型中刻意將包含屬性設為唯讀時,一般情況。 在此案例中,集合或字典通常會由擁有類型的初始化邏輯建立和初始化(通常是空白)。

另請參閱