WPF XAML 名稱範圍
XAML 名稱範圍是識別 XAML 中所定義物件的概念。 XAML 名稱範圍中的名稱可以用來建立物件的 XAML 定義名稱與其在物件樹狀結構中的執行個體對等項目之間的關聯性。 一般而言,載入 XAML 應用程式的個別 XAML 頁面根時,會建立 WPF Managed 程式碼中的 XAML 名稱範圍。 XAML 名稱範圍作為程式設計物件是由 INameScope 介面所定義,而且也會由實際類別 NameScope 實作。
所載入 XAML 應用程式中的名稱範圍
在更廣泛的程式設計或電腦科學內容中,程式設計概念通常包括可用來存取物件之唯一識別碼或名稱的原則。 針對使用識別碼或名稱的系統,在要求該名稱的物件時,名稱範圍會定義程序或技術將在其內搜尋的界限,或是在其中強制執行識別名稱唯一性的界限。 這些一般原則適用於 XAML 名稱範圍。 在 WPF 中,載入 XAML 頁面時,會在頁面的根項目上建立 XAML 名稱範圍。 在頁面根開始之 XAML 頁面內所指定的每個名稱都會新增至適當的 XAML 名稱範圍。
在 WPF XAML 中,通用根元素的元素 (例如 Page 和 Window) 一律會控制 XAML 名稱範圍。 如果 FrameworkElement 或 FrameworkContentElement 之類的元素是標記中頁面的根元素,XAML 處理器會隱含新增 Page 根項目,讓 Page 可以提供有效的 XAML 名稱範圍。
注意
WPF 建置動作會針對 XAML 生產來建立 XAML 名稱範圍,即使未在 XAML 標記的任何項目上定義 Name
或 x:Name
屬性也是一樣。
如果您嘗試在任何 XAML 名稱範圍中使用相同的名稱兩次,則會引發例外狀況。 針對具有程式碼後置且為已編譯應用程式一部分的 WPF XAML,在初始標記編譯期間建立頁面的已產生類別時,WPF 建置動作會在建置期間引發例外狀況。 針對未透過任何建置動作進行標記編譯的 XAML,在載入 XAML 時,可能會引發 XAML 名稱範圍問題的相關例外狀況。 XAML 設計工具也可能預期會在設計階段發生 XAML 名稱範圍問題。
將物件新增至執行階段物件樹狀結構
剖析 XAML 的時間點代表建立和定義 WPF XAML 名稱範圍的時間點。 如果您在剖析已產生該樹狀結構的 XAML 之後的某個時間點,將物件新增至物件樹狀結構,則新物件上的 Name
或 x:Name
值不會自動更新 XAML 名稱範圍中的資訊。 若要在載入 XAML 之後,將物件的名稱新增至 WPF XAML 名稱範圍,您必須在定義 XAML 名稱範圍的物件上呼叫適當的 RegisterName 實作,這通常是 XAML 頁面根目錄。 如果未註冊名稱,則無法透過 FindName 等方法依名稱來參考新增的物件,而且您無法使用該名稱作為動畫目標。
應用程式開發人員最常見的案例是,您將使用 RegisterName ,將名稱註冊到頁面目前根目錄的 XAML 名稱範圍。 RegisterName 是腳本以動畫物件為目標的重要案例的一部分。 如需詳細資訊,請參閱分鏡腳本概觀。
如果您在定義 XAML 名稱範圍之物件以外的物件上呼叫 RegisterName ,該名稱仍會註冊至呼叫物件所保存的 XAML 名稱範圍,就像您在 XAML 名稱範圍定義物件上呼叫 RegisterName 一樣。
程式碼中的 XAML 名稱範圍
您可以在程式碼中建立 XAML 名稱範圍後來加以使用。 甚至針對純程式碼用法,與 XAML 名稱範圍建立有關的 API 和概念也會相同,因為 WPF 的 XAML 處理器在處理 XAML 本身時會使用這些 API 和概念。 概念和 API 的存在目的主要是可以依名稱在物件樹狀結構內找到物件,而物件樹狀結構一般是在 XAML 中部分或完整定義。
對於以程式設計方式建立,而不是從載入的 XAML 建立的應用程式,定義 XAML 名稱範圍的物件必須實作 INameScope,或必須是 FrameworkElement 或 FrameworkContentElement 衍生類別,以支援在其實例上建立 XAML 名稱範圍。
此外,針對 XAML 處理器未載入和處理的任何項目,預設都不會建立或初始化物件的 XAML 名稱範圍。 您必須針對任何您要註冊名稱的物件,明確建立新的 XAML 名稱範圍。 若要建立 XAML 命名稱圍,您可以呼叫靜態 SetNameScope 方法。 指定將擁有它作為 dependencyObject
參數的物件,並將新的 NameScope 建構函式呼叫指定為 value
參數。
如果為 SetNameScope 提供做為 dependencyObject
的物件不是 INameScope 實作、FrameworkElement 或 FrameworkContentElement,則在任何子系元素上呼叫 RegisterName 將不會有任何作用。 如果您無法明確建立新的 XAML 名稱範圍,則對 RegisterName 的呼叫將會引發例外狀況。
如需在程式碼中使用 XAML 名稱範圍 API 的範例,請參閱定義名稱範圍。
樣式和範本中的 XAML 名稱範圍
WPF 中的樣式和範本可以用更簡單的方式重複使用和重新套用內容。 不過,樣式和範本可能也包括具有範本層級所定義之 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="MyButtonTemplate" TargetType="{x:Type Button}">
<Border BorderBrush="Red" Name="TheBorder" BorderThickness="2">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Page.Resources>
<StackPanel>
<Button Template="{StaticResource MyButtonTemplate}">My first button</Button>
<Button Template="{StaticResource MyButtonTemplate}">My second button</Button>
</StackPanel>
</Page>
在這裡,相同的範本會套用至兩個不同的按鈕。 如果範本沒有離散 XAML 名稱範圍,則範本中所使用的 TheBorder
名稱會導致 XAML 名稱範圍中的名稱衝突。 範本的每個具現化都有其專屬的 XAML 命名範圍;因此,在此範例中,每個具現化範本的 XAML 命名範圍都只會包含一個名稱。
樣式也會定義其專屬的 XAML 名稱範圍;因此,分鏡腳本的各部分一般可以獲指派特定名稱。 即使已在控制項自訂時重新定義範本,這些名稱還是可以啟用將目標設為該名稱之項目的控制項特定行為。
因為不同的 XAML 名稱範圍,所以在範本中尋找具名項目會比在頁面中尋找非範本具名項目更具挑戰。 您必須先取得套用範本之控制項的 Template 屬性值,以判斷該套用範本。 然後,您會呼叫 FindName 的範本版本,傳遞已套用範本作為第二個參數的控制項。
如果您是控制項作者,而且您正在產生慣例,其中套用範本中的特定命名元素是控制項本身所定義之行為的目標,您可以使用控制項實作程式碼中的 GetTemplateChild 方法。 GetTemplateChild 方法受到保護,因此只有控制項作者可以存取它。
如果您要從範本內工作,而且需要取得套用範本的 XAML 名稱範圍,請取得 TemplatedParent 值,然後在該處呼叫 FindName。 在範本內運作的範例就是您要撰寫事件處理常式實作,其中,將從已套用範本中的項目引發事件。
XAML 名稱範圍和名稱相關 API
FrameworkElement 具有 FindName、RegisterName 和 UnregisterName 方法。 如果您在其上呼叫這些方法的物件擁有 XAML 名稱範圍,則方法會呼叫相關 XAML 名稱範圍的方法。 否則,會檢查父項目,確認它是否擁有 XAML 名稱範圍,而且此程序會遞迴地執行,直到找到 XAML 名稱範圍 (因為 XAML 處理器行為,所以根一定會有 XAML 名稱範圍)。 FrameworkContentElement 具有類似的行為,但 FrameworkContentElement 永遠不會擁有 XAML 名稱範圍。 方法存在於 FrameworkContentElement 上,讓呼叫最終可以轉送至 FrameworkElement 父代元素。
SetNameScope 可用來將新的 XAML 命稱範圍對應至現有的物件。 您可以多次呼叫 SetNameScope,以重設或清除 XAML 名稱範圍,但這不是常見的用法。 此外,通常不會從程式碼使用 GetNameScope。
XAML 名稱範圍實作
下列類別會直接實作 INameScope:
ResourceDictionary 不使用 XAML 名稱或名稱範圍;它會改用索引鍵,因為它是字典實作。 ResourceDictionary 實作 INameScope 的唯一原因是,它可以對使用者程式碼引發例外狀況,以協助釐清真正 XAML 名稱範圍與 ResourceDictionary 如何處理索引鍵之間的差異,同時確保 XAML 名稱範圍不會套用至父代元素的 ResourceDictionary。
FrameworkTemplate 和 Style 透過明確介面定義實作 INameScope。 透過 INameScope 介面存取這些 XAML 名稱範圍時,明確的實作可讓這些 XAML 名稱範圍如常運作,即如何透過 WPF 內部程序與 XAML 名稱範圍進行通訊。 但是明確的介面定義不是 FrameworkTemplate 和 Style 傳統 API 介面的一部分,因為您不需要直接在 FrameworkTemplate 和 Style 上呼叫 INameScope 方法,而是會改用其他 API,例如 GetTemplateChild。
下列類別會使用 System.Windows.NameScope 協助程式類別,並透過 NameScope.NameScope 附加屬性連線到其 XAML 名稱範圍實作,以定義自己的 XAML 名稱範圍: