本文章是由機器翻譯。

惡意的程式碼

3 For Silverlight 開發的重要秘訣

Jeff Prosise

可從 MSDN 程式庫 的程式碼下載
瀏覽線上的程式碼

內容

隨選的組件載入
Just-In-Time 呈現
避免地區的相依性
開啟新網頁

我會撰寫這,Silverlight 2 是關閉按下的作用中,並開發人員會取得第一個看許多相信表示 Web 程式設計的未來。不論您是在 Silverlight proponent,或是在競爭的技術,例如 Adobe Flex 中找到更多的 allure 很有趣,查看 HTML,JavaScript 的替代方案和 AJAX 出現取得 mindshare 建立 Web 應用程式。Microsoft 已經硬碟在工作的 Silverlight 3,未來有永遠不會似乎更亮,

如也是任何平台的成為 Silverlight 開發人員在外出並不是沒有幾個 potholes。您知道,例如,在其他國家 / 地區的電腦上,將會失敗許多呼叫 XamlReader.load 正常測試,在美國地區的 PC 上嗎?您是否知道 Silverlight 的轉譯引擎至 UI 執行緒會 intimately 繫結,而且此事實可以 profoundly 影響您的程式碼的結構?您知道您可以藉由以動態方式載入組件,減少 XAP 檔案的大小,但是皆這樣做,而不會遺失的強型別優點需要的知識庫 」 的 CLR 內部嗎?如果這會 intrigues 您,閱讀。我有一些秘訣,並將共用的技巧將進行使用 Silverlight 的存留一點比較 bumpy — 讓您一個好和太多個通知 Silverlight 程式設計人員。

如需有關封裝的更快的傳遞的 Silverlight 內容的詳細資訊,請參閱1 月 2009 的期,Cutting Edge 的.

隨選的組件載入

其中一個設計良好的 Silverlight 應用程式的 hallmarks 會是小的 XAP 檔案正式稱為應用程式封裝。XAP 檔案所有太常 swell 以無法管理的大小,為內嵌的資源 (尤其是影像) 和組件參考的結果。越大,XAP 檔案]、 [在長花費下載,並且如果變得太大而,Silverlight 可能無法載入它。

即使大型應用程式可以如果你小心因數資源和應用程式使用的組件封裝小 XAP 檔案中,並保留的可延遲載入或在 Web 伺服器上的要求下載。您可以使用 Silverlight 的網路堆疊 WebClient 或其他類別来下載其他的資源] 和 [組件,一旦您已下載的應用程式套件。是通常比取得應用程式的 UI 設定及執行快速然後並其他資產的啟動非同步網路要求您必須以張貼在 100 MB XAP 比檔案強制使用者花五分鐘,等候一個進度指示器到達 100%。

Silverlight 中載入的點播資源會簡單且直接了當。在 [圖 1 ,程式碼片段會例如,下載 JPEG,在原來網站的部署,並顯示交給一個名為 MyImage 的 XAML 影像的已下載的位元。

[圖 1 從原始的網站] 下載影像

WebClient wc = new WebClient();
wc.OpenReadCompleted +=
    new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri("JetCat.jpg", UriKind.Relative));
  ...
void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        BitmapImage bi = new BitmapImage();
        bi.SetSource(e.Result);
        MyImage.Source = bi;
    }
}

不過是更難會視需要下載的組件載入。乍看之下似乎簡單: 使用 WebClient 下載組件和 AssemblyPart.load 將它載入,AppDomain。問題是,Silverlight 的 JIT 編譯器可以取得方式導致許多開發人員認為的無法下載上要求的組件中,並享受的強型別,太好處。在實際上,但是,您可以執行兩者。但您需要知道哪些您正在執行,需要載入的組件如何在 CLR 中,基本瞭解。

為了示範,請考慮下列程式碼:

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    Calendar cal = new Calendar();
    cal.Width = 300.0;
    cal.Height = 200.0;
    cal.SelectedDatesChanged += new
        EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
    LayoutRoot.Children.RemoveAt(0);
    LayoutRoot.Children.Add(cal);
}

fig02.gif

[圖 2] 會 保持在 XAP 的組件

其處理常式,以動態方式的建立行事曆控制項,並將它,以加入 XAML 畫面按一下按鈕。(它也會刪除之按鈕的引發事件會假設為 LayoutRoot 的子集合中的 0th 項目)。 因為行事曆在 System.Windows.controls.dll 不是嵌入外掛程式的 silver­light 但而屬於 「 延伸的 BCL 的實作此程式碼運作就正常,只要您將 System.Windows.controls.dll 的參考加入至專案。參考會導致 System.Windows.controls.dll XAP 檔案中,並自動載入,AppDomain。

現在假設您要是聰明和只載入 System.Windows.controls.dll 必要的也就是,如果使用者按一下 [] 按鈕。因此您將 System.Windows.controls.dll 的參考加入專案以符合編譯器 (否則編譯器不會編譯行事曆的參考,因為編譯器有不知道哪個行事曆型別),並,在 [Visual Studio 的屬性] 視窗中,您將 System.Windows.controls.dll 的複製到本機屬性設定為 False,以防止它 (像我在 [圖 2 ) 的內嵌於 XAP) 檔案。

接下來,您部署 System.Windows.controls.dll 的複本與伺服器上應用程式的 ClientBin 資料夾中的 XAP 檔案。請重組最後,您程式碼的結構,如 [圖 3 ] 所示。在按鈕按一下 [從 Web 伺服器的處理常式現在下載 System.Windows.controls.dll 它載入與 assembly­part.load,在 AppDomain,具現化 Calendar 控制項。

載入,[圖 3 On-Demand 組件沒有作用

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    wc.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
    wc.OpenReadAsync(new Uri("System.Windows.Controls.dll",
        UriKind.Relative));
}

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null) 
    {
        // Load the downloaded assembly
        AssemblyPart part = new AssemblyPart();
        part.Load(e.Result);

        // Create a Calendar control
        Calendar cal = new Calendar();
        cal.Width = 300.0;
        cal.Height = 200.0;
        cal.SelectedDatesChanged += new
           EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
        LayoutRoot.Children.RemoveAt(0);
        LayoutRoot.Children.Add(cal);
    }
}        

它看起來合理和程式碼會編譯就很好,但執行階段按一下 [處理常式會產生一個如 [圖 4 ] 中的例外狀況。 編譯的程式碼是良好的。 程式碼,會擲回例外狀況不。 因此所提供? 錯誤訊息,似乎表示 CLR 嘗試載入 System.Windows.controls.dll,但是它不需要因為您以程式設計方式載入。

這是一個很好的範例的情況下,CLR 知識庫 」 的內部可以讓您更好的 Silverlight 程式設計人員。 此處的問題是 JIT 編譯器會編譯您的 wc_OpenReadCompleted 方法時, 它掃描方法、 看到它會參考名為 [行事曆,型別而嘗試載入 System.Windows.controls.dll,以便可以解析參考。

fig04.gif

[圖 4 噢 !

這方法即使執行之前, 發生的不幸的是,因此您沒有機會來呼叫 AssemblyPart.load。 這是傳統的雞蛋問題。 您需要呼叫以載入組件,AssemblyPart.load 但 JIT 編譯器可以呼叫它之前,intervenes,並嘗試為您將它載入。 嘗試失敗,因為 System.Windows.controls.dll 不是應用程式套件中。

這是的點,其手上的許多程式設計人員擲回並一結束視組件載入不在 Silverlight 中運作,或依靠反映來具現化行事曆的型別:

AssemblyPart part = new AssemblyPart();
Assembly a = part.Load(e.Result);
Object cal = (Object)a.CreateInstance("Calendar");

這個方法運作,但是 clumsy。 您無法對於傳回 assembly.create­instance 行事曆因為如此做會造成嘗試執行方法之前將組件載入 JIT 編譯器將參考進行轉換為。 如果您無法轉換至行事曆,然後控制項的方法 」、 「 屬性和 「 事件必須透過存取反映 (Reflection),太。 程式碼快速成長不易很容易只要中,以增加 XAP 大小,在應用程式套件 」 和 「 Live 中嵌入 System.Windows.controls.dll。

好消息是您可以結合動態載入的組件,和強型別)。 只要重組您碼,以及 [圖 5 行的結構。 觀察 wc_OpenReadCompleted 不再參考行事曆型別 ; 所有的參考都已經移至名為 CreateCalendar 不同的方法。 此外,這種方式 JIT 編譯器將不會嘗試內嵌方法是賦予屬性 CreateCalendar。 (如果是內嵌是發生想就權限重新啟動因為 wc_OpenReadCompleted 會包含行事曆型別的隱含參考) 現在,JIT 編譯器不會檢查如果稱為 CreateCalendar,並將該時間,您已已經載入它在 AppDomain 之前,載入 System.Windows.controls.dll。

載入的運作方式 [圖 5 On-Demand 組件

private void CreateCalendarButton_Click(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    wc.OpenReadCompleted +=
        new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
    wc.OpenReadAsync(new Uri("System.Windows.Controls.dll",
        UriKind.Relative));
}

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        // Load the downloaded assembly
        AssemblyPart part = new AssemblyPart();
        part.Load(e.Result);

        // Create a Calendar control
        CreateCalendar();
    }
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void CreateCalendar()
{
    Calendar cal = new Calendar();
    cal.Width = 300.0;
    cal.Height = 200.0;
    cal.SelectedDatesChanged += new
        EventHandler<SelectionChangedEventArgs>(cal_SelectedDatesChanged);
    LayoutRoot.Children.RemoveAt(0);
    LayoutRoot.Children.Add(cal);
}

了解: 呈現和 UI 執行緒

請注意 Jeff 說不取得大量的注意力的一個層面是 Silverlight 的 Silverlight 中所有的呈現是 Silverlight 的在應用程式的 UI 執行緒上,,如果您 hog UI 執行緒,您防止發生的任何呈現。由於 WPF 提供的轉譯執行緒,它是 Silverlight 不可能驚人的。您可能想知道為什麼。

決定來到向下折衷系統負荷和 decoupling framerates 之間。使用 Silverlight,我們去使用一個淺權數執行緒上方法,並沒有不會隔離您的應用程式程式碼,從呈現的系統。這表示您可以執行更多您的動畫 (像有版面配置為基礎的動畫] 或 [自訂執行的程式碼) 中,而且沒有最小的延遲和轉譯系統所取得的額外負荷。向下的側邊,是如果您做太多,您可能會干擾作業,例如視訊的播放。

換句話說,Silverlight 呈現系統會利用多重核心處理,並以加快其呈現方式使用許多的執行緒。因此,呈現很少執行緒,「 」 上但它與您應用程式,以避免同步處理,以及資料的副本同步處理。

—Ashraf Michail 主要設計師 Silverlight

順便一提,如果這是 Windows Presentation Foundation (WPF),而不使用 Silverlight,您無法解決問題以更簡潔的方式註冊 AppDomain.AssemblyResolve 事件處理常式,並在有載入 System.Windows.controls.dll。在 Silverlight 的 AppDomain.AssemblyResolve 存在,但它是屬性化表示使用者程式碼無法註冊處理常式,為它的 SecurityCritical]。

[圖 5 會假設您包含在您的專案參考 System.Windows.Controls.dll,但您將 [複製到本機設為 false (請參閱 [圖 2 ) 和部署 ClientBin 資料夾中的組件。證明它運作,下載本專欄所附的 OnDemandAssemblyDemo 應用程式,再按按鈕標記為 [建立行事曆控制項。行事曆控制項會出現在按鈕的位置中。大幅,OnDemandAssemblyDemo.xap 不包含其中您可以輕鬆地檢查開啟 XAP 檔案使用 WinZip 的 System.Windows.controls.dll 的複本。魔力 !這將是您的下一個 Silverlight 合作對象的絕佳冰-分隔。

Just-In-Time 呈現

Silverlight 不會取得大量的按下的一部分是 Silverlight 中所有的呈現是在應用程式的 UI 執行緒,並您 hog UI 執行緒,如果您會防止從所進行的任何呈現。這表示您要避免長時間執行迴圈,在 UI 執行緒上的,如果您正在修改 XAML 景象,在該迴圈或任何動畫的進行在同一時間。

聽起來簡單 — 避免長時間執行迴圈,在 UI 執行緒上的 — 但實務,它可以有深遠的影響您所撰寫的程式碼。請考慮應用程式呼叫 OpenFileDialogDemo, [圖 6 ] 所示。它會示範如何使用 Silverlight 的 OpenFileDialog 類別允許使用者瀏覽他或她硬碟的影像檔案,並將再將影像載入 XAML 影像物件。執行應用程式,按一下 [開啟] 按鈕在頁面頂端,然後選取數個影像的檔案 (大好),並按一下 OpenFileDialog 的 [開啟] 按鈕]。

[圖 6 OpenFileDialogDemo 的動作

您會看到一個接著一個影像您選取到畫面使用 XamlReader.load 動態建立的物件的快顯而假設在頁面上的隨機位置。後影像顯示,您可以按一下,讓它們是來前端,並將甚至使用 [滑鼠] 來拖曳頁面周圍。

儘管其明顯的簡單 OpenFileDialogDemo 可提供實務課程中要考慮與 UI 執行緒。當我最初撰寫程式碼顯示 OpenFileDialog,並在載入影像檔案時,我結構它起來像 [圖 7 ] 中的程式碼片段。一旦使用者已關閉對話方塊,簡單的 foreach 迴圈會逐一查看選取的檔案,並載入它們的一個。

[圖 7 載入影像檔案的簡單方法

OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
    "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
ofd.FilterIndex = 1;
ofd.Multiselect = true;

if ((bool)ofd.ShowDialog())
{
    foreach (FileInfo fi in ofd.Files)
    {
        using (Stream stream = fi.OpenRead())
        {
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            GetNextImage().Source = bi;
        }
    }
}

不幸的是,沒有任何影像出現在螢幕上直到所有已載入。延遲不是什麼大不了,如果使用者選取一或兩個的影像檔案,但它是 intolerable,如果已選取的 40 個 (或) 50 個檔案。簡,應用程式沒有符合我設定為其,最低需求,因為我希望 「 顯示 」 在螢幕上載入和影像。取得它?顯示!顯示!顯示!

的問題會的不用說是 UI 執行緒上, 執行的 foreach 迴圈,並在已加入場景 Silverlight 迴圈執行時無法呈現影像 — 這意味著它是時間步驟後,需要一個的氣息並重建程式碼以使其能夠及時呈現影像。

[圖 8 ] 顯示有一個解決這個問題。修改過的 foreach 迴圈不執行任何動作超過 FileInfo 的物件加入一個 System.Collections.generic.queue。這可讓執行 [快速] 和 [手動控制項回 Silverlight 它可以取得下轉譯的迴圈。也許最有趣的 restructured 的程式碼外觀也是如何它 dequeues 處理 FileInfo) 物件,以回應 CompositionTarget.rendering 事件。

[圖 8 A 好方法,載入的影像檔案

private Queue<FileInfo> _files = new Queue<FileInfo>();
 ...
public Page()
{
    InitializeComponent();

    // Register a handler for Rendering events
    CompositionTarget.Rendering +=
        new EventHandler(CompositionTarget_Rendering);
}
 ...
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
    "PNG Files (*.png)|*.png|All Files (*.*)|*.*";
ofd.FilterIndex = 1;
ofd.Multiselect = true;

if ((bool)ofd.ShowDialog())
{
    // Reset the queue
    _files.Clear();

    // Place each FileInfo in a queue
    foreach (FileInfo fi in ofd.Files)
    {
        _files.Enqueue(fi);
    }
}
 ...
private void CompositionTarget_Rendering(Object sender, EventArgs e)
{
    if (_files.Count != 0)
    {
        FileInfo fi = _files.Dequeue();
        using (Stream stream = fi.OpenRead())
        {
            BitmapImage bi = new BitmapImage();
            bi.SetSource(stream);
            GetNextImage().Source = bi;
        }
    }
}

CompositionTarget.rendering 會為回每個框架轉譯呼,傳統上用於實作遊戲的迴圈。 它從 WPF 借用,晚在 Silverlight 2 開發週期中,顯示設定。 每次要 re-render 畫面 Silverlight 引發事件。

OpenFileDialogDemo 註冊 composition­target.rendering 事件 (CompositionTarget_Rendering) 在處理常式,並 dequeues 一個的 FileInfo 物件並將它轉換成一個 XAML 影像,的每次呼叫處理常式中。 結果呢? 影像會顯示在螢幕上,它們是載入,因為 Silverlight 現在有機會更新下列每個新的影像的畫面。 這會是如何結構在最終版本的 OpenFileDialogDemo foreach 迴圈,而且很時為什麼您在執行,您會看到一個接著一個而非一次螢幕上出現的影像。

您要小心不要過度使用 CompositionTarget.rendering。 如果 OpenFileDialogDemo 有動畫以執行它加入影像的場景可能 stutter 動畫,會因為的載入影像位元,並將它們指派給影像物件所需的時間會延遲每個框架。 但若您需要,您需要它錯誤,],而 OpenFileDialogDemo 則是一個很好的 CompositionTarget.rendering—indeed 的可接受使用範例,以下目標會是難否則完成。

避免地區的相依性

最後的提示,將使用 XamlReader.load 以動態方式建立 XAML 物件。 您可以找出到底有什麼問題這段程式碼嗎?

Rectangle rect = (Rectangle)XamlReader.Load(
    String.Format(
        "<Rectangle xmlns=\"http://schemas.microsoft.com/client/2007\" " +
        "Width=\"{0}\" Height=\"{1}\" Stroke=\"Black\" Fill=\"Yellow\" />",
        100.5, 100.0
    )
); 

如果您辨識這個程式碼適用在美國大部分的電腦上,但上大部分的電腦在歐洲和全世界的其他部分將會失敗,提供您自我為 pat 在背面。若要示範,請先設定您的作業系統顯示號碼 」、 「 貨幣 」、 「 日期和 「 時間,在美國如果它沒有已設定這種方式格式化。(在 Vista,移至 [地區及語言選項] 對話方塊可透過 [控制台] 的 [格式] 索引標籤)。 執行 XamlReader.load 呼叫,並確認呼叫順利執行中。現在變更地區格式為法文,並再次執行呼叫。這一次 XamlReader.load 擲回例外狀況: 「 無效的屬性值 100,5 屬性寬度的 」 ( 見 [圖 9 )。問題是,十進位數字,例如 100.5 寫入 100,5 (注意逗號小數點的位置) 在許多國家 / 地區。而且後 String.Format 接受的地區設定主機電腦上,十進位的 100.5 成為 100,5 」。不幸的是,XamlReader.load 不知道要讓 「 100,5 」 的因此它會擲回例外狀況 (Exception)。

[圖 9 XamlReader.Load 所擲回的例外狀況

下列程式碼將顯示您,在正確的方式呼叫 XamlReader.load,以便執行 Silverlight 的任何電腦上運作:

Rectangle rect = (Rectangle)XamlReader.Load(
    String.Format(
        CultureInfo.InvariantCulture,
        "<Rectangle xmlns=\"http://schemas.microsoft.com/client/2007\" " +
        "Width=\"{0}\" Height=\"{1}\" Stroke=\"Black\" Fill=\"Yellow\" />",
        100.5, 100.0
    )
);

第一個參數傳遞至 String.Format 將是您,CultureInfo 物件參考不因文化特性而異。 XamlReader.load 預期不因文化特性而異的字串,以便使用 CultureInfo.InvariantCulture 確保的 String.Format 產生正確格式化的十進位值 (以及格式正確的日期和時間) (如果您使用的。 不恰巧前, 一節中的 OpenFileDialogDemo 應用程式會使用這項技術,來確定 XamlReader.load 自己呼叫不論地區設定而運作。

如果您傳遞給 XamlReader.load 字串,包含由 String.Format 所產生的十進位值,永遠使用 CultureInfo.InvariantCulture 取得適當格式化。 一旦您的應用程式執行在留心的責任會執行各種不同的地區設定。

開啟新網頁

如果您要開始使用 Silverlight,而且您的經驗.NET 開發,您已經知道您需要知道的 90%。 但是 Silverlight 讓 nuances,讓您瞭解.NET。

說到頁面,許多讀者要求我想要更新, Silverlight 1.0 頁面開啟 FrameworkSilverlight 2] 所示可能 2008。 也,移植已完成。 您可以檢視使用更新的架構,在應用程式範例 wintellect.com/silverlight/pageturndemo/您可以下載的原始程式碼 wintellect.com/Downloads/PageTurnDemo2.zip.

開啟頁面架構會存在於 PageTurn.cs,和 page.xaml.cs 中的程式碼說明一切的運作方式。 API 與 Silverlight 1.0 版本 (在 C# 而不是 JavaScript),並且我做的變更能夠開啟更好的頁面。 我會包含一個 PageTurned 事件的引發由 Framework 每次在頁面開啟已完成,因此您可以更新 UI — 說,顯示目前的 T: System.Web.UI.MobileControls.Adapters.XhtmlAdapters.頁碼。

您提出問題或意見,請以 Jeff 寄 wicked@Microsoft.com.

Jeff ProsiseMSDN Magazine 在特約編輯器包括程式設計的 Microsoft.NET (2002,Microsoft Press) 幾本書籍的作者。 他也是 Wintellect (創辦人。 wintellect.com),使用軟體諮詢顧問和教育公司的專長於 Microsoft.NET。 連絡人在 Jeff wicked@Microsoft.com.