Silverlight陷阱:XAML中不能使用自定義字典 AG_E_PARSER_BAD_PROPERTY_VALUE
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最後檢查一下宣告的字典是否正確設定了:
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這類既缺乏編譯器檢查,又沒有呼叫堆疊可查的東西)真夠殺死你幾萬個腦細胞的。