XAML 命名範圍

XAML 命名範圍儲存物件的 XAML 定義名稱與其執行個體等效項之間的關係。 這個概念類似於其他程式設計語言和技術中術語命名範圍的更廣泛意義。

如何定義 XAML 命名範圍

XAML 命名範圍中的名稱可讓使用者程式碼參考最初在 XAML 中宣告的物件。 剖析 XAML 的內部結果是執行階段會建立一組物件,以保留這些物件在 XAML 宣告中具有的部分或所有關聯性。 這些關聯性會維持為所建立物件的特定物件屬性,或會公開給程式設計模型 API 中的公用程式方法。

XAML 命名範圍中名稱最典型的用途是做為物件執行個體的直接參考,這是由標記編譯傳遞做為專案建置作業啟用的,並與部分類別範本中產生的 InitializeComponent 方法結合。

您也可以在執行階段使用公用程式方法 FindName,將參考傳回 XAML 標記中以名稱定義的物件。

深入瞭解建置動作和 XAML

從技術上說,XAML 本身會經歷標記編譯程式傳遞,同時 XAML 和針對程式碼後置所定義的部分類別會一起編譯。 標記中定義的 Namex:Name 屬性的每個物件元素都會產生內部欄位,且名稱符合 XAML 名稱。 此欄位一開始是空的。 然後,類別會產生 InitializeComponent 方法,只有在載入所有 XAML 之後才會呼叫。 在 InitializeComponent 邏輯中,每個內部欄位接著會填入對等名稱字串的 FindName 傳回值。 您可以在編譯後,查看針對 Windows 執行階段應用程式專案 /obj 子資料夾中每個 XAML 頁面所建立的 ".g" (generated) 檔案,來自行觀察此基礎結構。 如果您反映這些欄位,或檢查其介面語言內容,您也可以將欄位和 InitializeComponent 方法視為產生的組件成員。

注意:特別是對於 Visual C++ 元件延伸模組 (C++/CX) 應用程式,不會為 XAML 檔案的根元素建立 x:Name 參考的支援欄位。 如果您需要從 C++/CX 程式碼後置參考根物件,請使用其他 API 或樹狀結構周遊。 例如,您可以針對已知的具名下層元素呼叫 FindName,然後呼叫 Parent

使用 XamlReader.Load 在執行階段建立物件

XAML 也可以做為 XamlReader.Load 方法的字串輸入,其作用類似於初始 XAML 來源剖析作業。 XamlReader.Load 會在執行階段建立物件的新中斷連線的樹狀結構。 然後,中斷連線的樹狀結構可以附加至主要物件樹狀結構上的某個點。 您必須明確地連接已建立的物件樹狀結構,方法是將它新增至 Content 屬性集合,例如 Children,或設定一些接受物件值的其他屬性 (例如,為 Fill 屬性值載入新的 ImageBrush)。

XamlReader.Load 的 XAML 命名範圍含意

XamlReader.Load 所建立之新物件樹狀結構所定義的初步 XAML 命名範圍會評估所提供 XAML 中的任何定義名稱,以取得唯一性。 如果目前提供的 XAML 中的名稱在內部不是唯一的,XamlReader.Load 會擲回例外狀況。 如果已中斷連線的物件樹狀結構連接到主要應用程式物件樹狀結構,則不會嘗試將其 XAML 命名範圍與主要應用程式 XAML 命名範圍合併。 連接樹狀結構之後,您的應用程式具有統一的物件樹狀結構,但該樹狀結構中有離散的 XAML 命名範圍。 這些分割會在物件之間的連接點發生,而您將某些屬性設定為從 XamlReader.Load 呼叫傳回的值。

具有離散且中斷連線的 XAML 命名範圍的複雜性在於呼叫 FindName 方法,以及直接管理的物件參考不再針對統一的 XAML 命名範圍運作。 相反地,呼叫 FindName 的特定物件隱含了範圍,該範圍是呼叫物件所在的 XAML 命名範圍。 在直接管理物件參考的情況下,範圍由程式碼所在的類別隱含。 一般而言,應用程式內容的「頁面」執行階段互動程式碼後置存在於支援根目錄「page」的部分類別中,因此 XAML 命名範圍是根 XAML 命名範圍。

如果呼叫 FindName 來取得根 XAML 命名範圍中的具名物件,則該方法將不會從 XamlReader.Load 建立的離散 XAML 命名範圍中尋找物件。 相反地,如果您從從離散 XAML 命名範圍中取得的物件呼叫 FindName,此方法就不會在根 XAML 命名範圍中尋找具名物件。

這個離散的 XAML 命名範圍問題只會在使用 FindName 呼叫時,依 XAML 命名範圍中的名稱影響尋找物件。

若要取得不同 XAML 命名範圍中定義之物件的參考,您可以使用數種技術:

  • 使用已知存在於物件樹狀結構中的上層和/或集合屬性,以離散步驟逐步執行整個樹狀結構 (例如 Panel.Children 所傳回的集合)。
  • 如果您是從離散 XAML 命名範圍呼叫,而且想要根 XAML 命名範圍,則一律很容易取得目前顯示主視窗的參考。 您可以從具有呼叫 Window.Current.Content 的一列程式碼中,從目前應用程式視窗取得視覺化根目錄 (根 XAML 元素,也稱為內容來源)。 然後,您可以轉換成 FrameworkElement,並從這個範圍呼叫 FindName
  • 如果您要從根 XAML 命名範圍呼叫,而且想要在離散的 XAML 命名範圍內物件,最好的做法是在程式碼中預先規劃,並保留 XamlReader.Load 所傳回物件的參考,然後新增至主要物件樹狀結構。 此物件現在是在離散 XAML 命名範圍中呼叫 FindName 的有效物件。 您可以保留此物件做為全域變數,或使用方法參數傳遞它。
  • 您可以檢查視覺化樹狀結構,完全避免名稱和 XAML 命名範圍考慮。 VisualTreeHelper API 可讓您根據位置與索引,在上層物件和下層集合方面周遊視覺化樹狀結構。

範本中的 XAML 命名範圍

XAML 中的範本可讓您以直接的方式重複使用和重新套用內容,但範本也可能包含具有範本層級所定義名稱的元素。 可能會在頁面中多次使用這個相同的範本。 因此,範本定義自己的 XAML 命名範圍,與應用樣式或範本的包含頁無關。 請考慮此範例:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  >
  <Page.Resources>
    <ControlTemplate x:Key="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</Page>

在此處,相同的範本會套用至兩個不同的控制項。 如果範本沒有離散的 XAML 命名範圍,則範本中使用的「MyTextBlock」名稱將導致名稱衝突。 範本的每個具現化都有其專屬的 XAML 命名範圍;因此,在此範例中,每個具現化範本的 XAML 命名範圍都只會包含一個名稱。 不過,根 XAML 命名範圍不包含任一範本的名稱。

由於不同的 XAML 命名範圍,因此從套用範本的頁面範圍中尋找具名元素需要不同的技術。 您必須先取得已套用範本的物件,然後呼叫 GetTemplateChild,而不是在物件樹狀結構中的某些物件上呼叫 FindName。 如果您是控制項作者,並且要產生慣例,其中套用的範本中的特定命名元素是控制本身定義的行為的目標,則可以使用控制項實作程式碼中的 GetTemplateChild 方法。 GetTemplateChild 方法受保護,因此只有控制項作者才能存取它。 此外,還有一些慣例,控制項作者應該遵循這些慣例來命名組件和範本組件,並報告這些組件做為套用至控制項類別的屬性值。 這項技術可讓重要組件的名稱可供探索,以控制可能想要套用不同範本的使用者,這需要取代具名組件,才能維護控制項功能。