對象樹和 FindName
在 JavaScript API 中,初始對象樹始終通過分析 XAML 來構造。除了一些能夠分析字串的類型轉換用法,載入 XAML 是在 JavaScript API 中“構造”對象的唯一方式。構造了這樣的樹之後,您通常會希望與該對象樹進行互動。為此,需要指令碼對象引用樹中的一個對象。
建立這樣的引用的最佳方法是將 Name 或 x:Name 屬性分配給計劃在運行期間引用對象所在的 XAML 中的任何對象元素。在 JavaScript API 中,名稱在概念上只是一個字串,而非對象。但可以隨後通過在任何運行時代碼中調用 FindName,使用名稱字串檢索實際對象。FindName 以這種方式存在於 API 中:您可以在 JavaScript API 中的幾乎所有對象(包括表示 Silverlight 外掛程式的對象)上調用 FindName。您通常會發現為用於 Silverlight 的 JavaScript API 編寫的使用者函數的第一行或前幾行使 FindName 調用擷取對象引用,然後函數的其餘部分對那些對象進行操作。
即使沒有命名物件,您仍然可以在運行時通過 Silverlight 外掛程式上的 Root 屬性進入對象樹。這將返回作為載入的 XAML 的根的對象。
此外,任何附加到 Silverlight 對象的事件處理常式都有權從事件訪問 sender 值。該 sender 和相關事件處理常式通常以這樣的方式進行附加:該 sender 是您希望與其互動的對象。如果不是這樣,sender 仍提供可以從中調用 FindName 的方便對象。
對象樹和 CreateFromXaml
網頁上的 Silverlight 內容按樹狀結構中的對象階層進行排列。結構中最頂層的那一個對象是根對象,並且對應於作為 Silverlight 外掛程式的 Source 載入的 XAML 檔案的根。根對象通常是一個 Canvas 對象,因為 Canvas 可以包含其他對象,如 TextBlock 或 MediaElement。(Silverlight 1.0 僅支援 Canvas 作為根;如果將 JavaScript API 用於 Silverlight 2 用戶端,則可以使用其他 UIElement 對象作為根。)
若要在初始載入 source XAML 後將 XAML 內容動態添加到樹狀結構中,請先使用 CreateFromXaml 方法建立 XAML 片段。此時,建立的 XAML 片段與 Silverlight 對象樹是中斷連線的。這意味著尚未呈現該片段。不過,即使在將其添加到對象樹之前,仍可以修改該 XAML 片段中對象的屬性。不能在串連到樹之前調用對象方法。
在建立與 XAML 片段中斷連線的對象集後,您通常會希望將該對象集添加到活動的 Silverlight 對象樹中。可以將該片段作為子物件添加到某個父物件中,也可以將該片段設定為某個對象的屬性值。當該片段成為 Silverlight 對象樹的一部分後,將呈現該 XAML 片段中的對象。根據作為 CreateFromXaml 輸入提供的 XAML 範圍,可以建立一個 Silverlight 對象(如 TextBlock)或複雜的 Silverlight 對象樹。
對於將 XAML 內容動態添加到 Silverlight 對象樹有以下幾個要求:
必須有與 Silverlight 外掛程式關聯的現有 XAML 內容。不能使用 CreateFromXaml 替換整個內容樹;必須至少保留原始根項目。如果希望替換整個樹,則可以設定 Source,但請注意,Source 需要 URI 處存在實際的檔案,並且不能僅用字串來進行初始化。但是,可以通過下面的方法來解決此問題:使用內聯 XAML 作為 Source,使用 HTML DOM 來 document.write 包含預期源 XAML 的 SCRIPT 塊的內容。
CreateFromXaml 方法只能由 Silverlight 外掛程式調用。不過,在 JavaScript API 中很容易擷取對該外掛程式的對象引用:只需在任何 Silverlight 對象上調用 GetHost。
只能將使用 CreateFromXaml 方法建立的 XAML 內容分配給一個對象。如果希望將從同一 XAML 建立的對象添加到應用程式的不同地區,則必須多次調用 CreateFromXaml,並對傳回值使用不同變數。(如果執行此操作,請小心名稱衝突;請參見本主題後面的使用 CreateFromXAML 建立名稱範圍。)
將要包含新的 XAML 內容的 Silverlight 對象必須能夠封裝對象,無論是作為子項目還是作為屬性值。
為 CreateFromXaml 的 xamlContent 指定的字串必須指定語式正確的 XML(具體地說,它必須具有單個根項目)。如果提供的 XML 字串的格式不正確,CreateFromXaml 將失敗並導致錯誤。
根項目隱式使用 Silverlight XML 命名空間。可以顯式設定 Silverlight 命名空間,但不要將預設 XML 命名空間設定為 Silverlight 命名空間以外的任何其他值。
使用 CreateFromXAML 建立名稱範圍
用於 CreateFromXaml 的 XAML 可以具有為 Name 或 x:Name 定義的值。在 CreateFromXaml 調用過程中,將基於所提供的 XAML 的根建立初步的名稱範圍。該初步名稱範圍計算所提供的 XAML 中的所有已定義名稱是否唯一。如果所提供的 XAML 中的名稱此時在內部不唯一,CreateFromXaml 將引發錯誤。但是,如果所提供的 XAML 中的名稱與主對象樹名稱範圍中已經存在的名稱衝突,則不會立即發生任何錯誤。這是因為調用 CreateFromXaml 方法時,返回的所建立的對象樹是中斷連線的。必須通過以下方法顯式串連所建立的對象樹:將它添加到內容屬性集合(如 Canvas.Children)中,或者設定另外一個採用對象值的屬性(例如,為 Fill 屬性值指定一個新的 ImageBrush)。只有在嘗試將中斷連線的對象樹串連到應用程式的主對象樹之後,才能看到導致錯誤的名稱衝突。用於所嘗試的串連的方法或屬性將是錯誤的源,並且所嘗試的串連將失敗(中斷連線的樹將保持中斷連線狀態)。
CreateFromXaml 具有一個選擇性參數 createNameScope,該參數通常預設為 false。如果在用於建立中斷連線的對象樹的 CreateFromXaml 調用中將該值顯式設定為 true,仍然會建立初步的名稱範圍來計算所提供的 XAML 中的所有已定義名稱是否唯一。如果所提供的 XAML 中的名稱此時在內部不唯一,CreateFromXaml 將引發錯誤。行為上的差異為,中斷連線的對象樹將隨即被標記為,當它與主應用程式物件樹串連後,將不嘗試將其名稱範圍與主應用程式名稱範圍合并在一起。實際上,在串連樹之後,應用程式將具有一個統一的對象樹,而不是具有獨立的名稱範圍。在 CreateFromXaml 根或以前中斷連線的樹中的任何對象上定義的名稱都不與主對象樹名稱範圍關聯;它具有自己的獨立名稱範圍。
具有獨立名稱範圍會帶來以下麻煩:對 FindName 方法的調用將不再對統一的名稱範圍執行操作。相反,調用 FindName 的特定對象將表示一個範圍,而該範圍就是調用對象所在的名稱範圍。因此,如果嘗試調用 FindName 來擷取主對象樹名稱範圍中的命名物件,將找不到由 CreateFromXaml 建立的獨立名稱範圍中的對象。相反,從獨立名稱範圍調用 FindName 將找不到主應用程式名稱範圍中的命名物件。對 Silverlight 外掛程式對象定義的 FindName 方法不會完全解決此問題;其名稱範圍始終為主對象樹名稱範圍。
此獨立名稱範圍問題僅影響名稱範圍和 FindName 調用。在某些情況下仍然可以通過調用 GetParent 方法向上執行分離的步驟,或者通過調用相關集合屬性或屬性(如 Canvas.Children 返回的集合)向下執行分離的步驟。
若要擷取在不同名稱範圍中定義的對象,則可以使用以下多種技術:
使用 GetParent 和/或集合屬性在分離的步驟中執行整個樹。
如果從獨立名稱範圍進行調用並希望得到主對象樹名稱範圍,則始終最簡單的做法是擷取對 Silverlight 外掛程式的引用,然後對其調用 FindName。若要在單行中執行此操作,則串連的指令碼調用如下所示:
returnedObject = 對象.GetHost().content.FindName("nameToFind");
其中,對象 是獨立名稱範圍中的調用對象。此文法中的 .content 是通往定義 FindName 方法的特定 Silverlight 外掛程式子物件的步驟。
如果從主對象樹名稱範圍進行調用並希望得到獨立名稱範圍內的對象,則最佳做法是在代碼中提前進行計劃並保留對由 CreateFromXaml 返回的對象的引用,然後添加到樹中。現在,此對象對於在所建立的獨立名稱範圍內調用 FindName 是有效對象。可以將此對象保持為全域變數,否則使用方法參數傳遞它。