1. 程式人生 > >Silverlight陷阱:XAML中不能使用自定義字典 AG_E_PARSER_BAD_PROPERTY_VALUE

Silverlight陷阱:XAML中不能使用自定義字典 AG_E_PARSER_BAD_PROPERTY_VALUE

我們知道,XAML中實際上是可以放置任何物件的,而系統將按照如下的規則管理巢狀的內容:

1. 如果物件實現了IList,那麼巢狀內容將通過IList.Add新增到父物件;

2. 如果物件實現了IDictionary,並且元素用x:Key指定了鍵值,那麼巢狀內容將通過IDictionary.Add新增到父物件;

3. 如果只有父物件用ContentPropertyAttribute聲明瞭內容屬性,那麼巢狀內容將被賦值為到該屬性。

我們在目前的專案中使用了很多XAML宣告來減少編碼量,但是在使用中我們發現,第2條對於Silverlight是不適用的,Silverlight的XAML只支援對Resources屬性用字典方式來宣告,對於自定義的字典內容,即使是ResourceDictionary也無法讀取,否則執行時就會丟擲異常。因為同樣的方法在服務端已經普遍使用,所以我們把程式碼應用到Silverlight工程中的時候,根本沒有想到這方面會出問題。從而花了很長時間、走了很多彎路去查詢自己程式中的Bug,反覆作了大量實驗後,終於確定:這個問題來源於Silverlight和WPF讀取XAML時的表現不同。

還是用程式碼來說明。首先我們看看在WPF中使用自定義字典是否可行:

publicclass BaseWindow : Window {     public BaseWindow()     {         Dict =new MyDict();     }
   
public MyDict Dict { get; set; } }
publicclass MyDict : Dictionary<string, Brush> { }

然後在窗體中新增字典資料:

<local:BaseWindow x:Class="TestWPF2.Window1"     xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     Title="Window1"     xmlns:local="clr-namespace:TestWPF2"><local:BaseWindow.Dict><SolidColorBrush x:Key="1" Color="Blue"/><SolidColorBrush x:Key="2" Color="Black"/></local:BaseWindow.Dict
></local:BaseWindow>

最後檢查一下宣告的字典是否正確設定了:

public Window1() {     InitializeComponent();     this.Background = Dict["1"]; }

執行結果完全正確(如圖) ,表明自定義字典在WPF中是可行的。

然後我們再如法炮製一個Silverlight工程,執行之,出現異常:

AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 10 Position: 42]Type: XamlParseExceptionStackTrace:   

位於 System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)  

位於 TestSL2.MainPage.InitializeComponent()  

位於 TestSL2.MainPage..ctor()  

位於 TestSL2.App.App_Startup(Object sender, StartupEventArgs e)  

位於 System.Windows.CoreInvokeHandler.InvokeEventHandler(Int32 typeIndex, Delegate handlerDelegate, Object sender, Object args)  

位於 MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, String eventName)

此實驗可以說明WPF和Silverlight 在處理XAML時候的不同行為。你可以把MyDict換成ResourceDictionary試試,結果也是一樣的。

為了繞過這個問題,我們不得不在Silverlight工程中去掉字典,把內容重新組織成列表,然後在程式中將內容重新組織成字典——等於服務端已經除錯好的方法在客戶端又重寫了一遍。非常惱人,但是沒有辦法。。

這個問題告訴我們,儘管Silverlight 源出WPF,但處理細節上還是存在微妙的差別。將WPF的經驗應用到Silverlight上,不見得會奏效。最好是先作一些小實驗,確信方法是可行的,再新增到工程裡,否則在大的專案裡除錯此類問題(尤其是XAML這類既缺乏編譯器檢查,又沒有呼叫堆疊可查的東西)真夠殺死你幾萬個腦細胞的。