本主題幫助你理解來自Prism的多目標和它的優缺點。多目標的代碼針對兩個不一樣的平臺有大體相同的代碼庫。這容許同時保持代碼儘量多同樣可以產生針對兩種不一樣技術的二進制文件。在這種狀況下,本節介紹的技術是WPF和Silverlight。本主題包含了一些你在使用這些技術開發多目標應用程序的時候的注意事項。
目標和有點
當在編寫具備類似功能和能力的WPF和Silverlight應用程序的時候,努力使用一個代碼庫頗有意義。儘管WPF和Silverlight平臺很是類似,但他們只有有限的二進制兼容性。僅Silverlight4中引用的一組特定的內核,可移植的框架程序集能夠被加載到.NET Framework4.0的運行時中。
由於Prism爲WPF和Silverlight提供了大量的類似的功能。許多的代碼能夠針對這兩種技術進行生成。支持多目標應用主要是有關實如今兩個環境之間共享代碼和組件的最大化的可能性的模式和結構,以及用於容許一個應用程序集成環境特定的功能,使之可以充分利用臺式機或瀏覽器的的特定的功能。經過建立使用Prism的多目標組合應用程序,您能夠重用跨越WPF和Silverlight應用程序的源代碼。
超出範圍web
本章不打算來形容這種狀況下,相反,它描述了經過共享源代碼構建多目標應用所面臨的挑戰和解決方案。
多目標場景shell
主要狀況是對於同時提供了功能豐富的桌面體驗和寬範圍的互聯網應用體驗的應用程序。在這種狀況下,您可能但願開發對WPF和Silverlight或一個提供不一樣的功能和工做流程相同的功能和工做流程的應用程序。如下一些多目標的應用程序:
- 你能夠爲用戶提供一個在辦公室的具備全功能的以及在旅行時可縮減功能的基於瀏覽器版本的的應用程序。
- 你能夠爲內部用戶提供一個桌面班的應用程序併爲外部人員或參與者一個瀏覽器應用程序。
例如,一個企業可能想同時擁有一個那些用戶想要經過電話來下訂單電話呼叫中心的應用程序和一個那些想要在網上下訂單的客戶可以在網上下訂單的應用程序。然而,形式是徹底不同的。電話呼叫中心桌面訂單的形式相比在網上下訂單的形式提供了更多信息和擴展功能。然而,由於他們完成相似的東西將有訂單和業務邏輯能夠跨兩個場景中重複使用的某些部分。
面向服務的應用程序更容易實現多目標,由於Silverlight先天就是面向服務的。Silverlight因爲它的目標功能設置和安全機制使得它不支持本地存儲或者訪問數據庫。另外,鏈接的應用程序也更容易多目標,由於Silverlight的關聯性質。
多目標的思考數據庫
經過解決方案的多目標性,你也應該考慮如下幾點:
- Silverlight提供的獨立存儲在本地客戶端計算機上存儲空間有限。
- 你可能會在你的多目標的解決方案中的代碼中失簡潔性和易讀性。由於一些WPF的功能在Silverlight中並無實現。你將須要解決這些問題,而且這將會致使你的代碼再也不優雅或者易讀。
- 默認狀況下,Silverlight應用程序在一個安全的沙箱中運行,因此有幾件事情,你不能在沙箱環境外進行的。這些應用具備受限訪問本地計算機和被限制,以幫助防止惡意行爲。這些限制防止對設備的訪問,並與其餘正在運行的程序進行交互。
注意:
- Silverlight只支持異步通訊,因此你不能在多目標應用程序中使用同步通訊。
多目標元素編程
一般狀況下有許多的代碼與實際展現結束是毫無關係的。因爲Silverlight和.NET Framework運行時本質上很是相近。大部分的代碼能夠兩種技術之間共享。這也鼓勵大量使用展現隔離的模式,以從實際可視化展現和展示邏輯之間分開。爲了最大化的分離UI和非UI代碼。你可使得如下多目標源代碼元素:
- 展示模式。模式例如MVVM,MVP和MVC,若是在平臺之間邏輯大部分相同時均可以使用這些模式。
- 服務。幫助展示的服務能夠常常用於多目標。
- 單元測試。許多單元測試在工具中能夠是多目標的,例如Silverlight 單元測試,框架可使用例如MSTest的相同的屬性結構。
- 簡單的視圖。若是XAML被用於支持Silverlight和WPF。若是一個視圖僅由基本控件和簡單的數據綁定組成,它有可能也能夠在WPF和Silverlight之間共享。
Silverlight 的API大部分是.NET Framework的API的子集,因此它使用這種更小的API來開發應用程序來減小使用在多個平臺之間不能同時使用的功能的機會常常頗有意義。因爲Siliverlight和WPF的XAML之間的不一樣,如下元素比較難以複用:
- Complex Views(XAML)
- Controls
- Styling
- Animation
- Expression Blend behaviors 和triggers
一個多目標的解決方案:多連接的項目windows
Silverlight和WPF只有有限的二進制兼容,因此一些代碼還組合必須在每一個目標環境中被從新編譯。Prism採用的機制用於在應用程序的結構和將模塊代碼引用到多個項目的提供指導。每一個工程都管理者全部的引用,資源,和WPF和Silverlight目標環境指定的代碼。在兩個項目之間鏈接的代碼的共享使得它們自動的編譯到各自的目標中,單元測試能夠相似的被兩個環境間共享,以使得它們能夠被一次編寫在任何一個目標環境中運行目標代碼。Prism 團隊建立了一個名爲項目鏈接器的工具來幫助建立和維護這些鏈接。項目鏈接器工具用於在Prism類庫,快速啓動,和實現引用中鏈接項目。
注意:
你能夠從Visual Studio Gallery 中下載項目鏈接器或者打開Visual Studio,在工具菜單中點擊擴展管理,在擴展工具管理窗口的聯機庫中查找項目鏈接器。
非UI代碼和組件可能更容易被共享,因此堅持分離UI模式是確保UI和非UI部分的應用程序或模塊被清晰地劃分開相當重要的。
核心應用程序瀏覽器
這個模式的總體是基於應用程序內核代碼共享的而且而後使用實現了WPF和Silverlight特定功能的擴展項來擴充的。應用程序的內核定義了應用程序的總體結構而且包含了在兩個環境中通用的代碼和組件。Silverlight是WPF的主要的一個子集,因此在Silverlight中開發應用程序內核減小了依賴一個在WPF 中支持但在Silverlihgt中不支持的API的風險。
注意:
在Prism源碼樹中許多的項目實際上在WPF項目中擁有內核源文件,將Silverlight項目鏈接到WPF項目的文件。這主要是因爲Prism的第一個版本實在Silverlight發佈以前構建的歷史緣由形成的。
下面的插圖展現了Multi-Targeted QuickStart的資源管理器。QuickStart的大多數的WPF版本是鏈接的文件由於應用程序的內核實在Silverlight中開發的。在QuickStart中找到的共享的文件包括圖片,模塊,服務,接口和資源。共享(鏈接)的文件被突出顯示。
建立多目標應用程序緩存
本節描述三方面來考慮開發多目標的應用程序:
- 設計和編碼方針。這一節描述了在Silverlight和WPF之間共享代碼的考慮內容。
- 程序的指導方針。這一節描述了Silverlight和WPF之間共享代碼的實現方式。
- 團隊建設指導方針。這一節描述了當構建多目標程序是特定的軟件團結構建的問題。
- Silverlight和WPF之間的對比。這一節描述了Silverlight和WPF之間的區別。
設計和編碼方針安全
設計和編碼方針包含如下內容:
- 使用表現分離模式來最大化共享代碼的數量。
- 當可能時,編輯能夠在兩個平臺下編譯的代碼。若是不行,按如下操做:
- 使用#if標籤若是你有簡單或者單行構造方法。
- 在類的大部分類似可是一些方法具備特定平臺的實現時使用部分類
- 只有當你須要在一個平臺調用外部方法可是在另外一個平臺不須要調用外部方法時使用部分方法
- 使用單一職責構建指定平臺的類
- 爲Silverlight和WPF建立各自的解決方案文件夾
- 在重構代碼時間長Silverlight和WPF的引用。
下面的章節將更詳細的單獨描述這些方針。
使用表現分離最大化共享代碼數量服務器
在平臺間共享視圖-代碼會比較困難。若是你講邏輯代碼從UI中分離共享展示和業務邏輯將會更容易一些。另外,這使得代碼更容被理解和維護。
編寫在兩個平臺中編譯的代碼網絡
當可能時,編輯能夠在兩個平臺下編譯的代碼來進行復用。當這種方式變得太複雜時,你必須權衡使用兩份代碼比一份基類代碼跟優雅。例如,在Silverlight中,你能夠在ItemsControl的Items屬性上執行如下LINQ表達式。
ItemsControl someItemsControl = new ItemsControl();
someItemsControl.Items.Add(new TextBox());
bool hasDependencyObjects = someItemsControl.Items.Any(item => item is DependencyObject); |
然而,因爲在WPF中 Items屬性不是
IEnumerable<T>的,這種實現方式將不能正常工做。而不是在WPF和Silverlight中建立一個不一樣版本,選擇一個較低的首選,可是單一來源的解決方案,以下面的代碼。
ItemsControl someItemsControl = new ItemsControl();
someItemsControl.Items.Add(new TextBox());
bool hasDependencyObjects = false;
foreach (var item in someItemsControl.Items)
{
if (item is DependencyObject)
{
hasDependencyObjects = true;
break;
}
} |
這種簡單的方式在Silverlight中編寫,由於在.NET Framework中它是被迫使用的。
若是你有簡單或者單行構造 使用#if 聲明
有時候你不能有建立一個單一的代碼基礎由於WPF和Silverlight之間的不兼容性。在這種狀況下,你可使用#if SILVERLIGHT標籤來建立又掉進的編譯部分。下面的代碼示例展現了使用#if SILVERLIGHT聲明。
#if SILVERLIGHT
System.Windows.Application.Current.RootVisual = shell;
#else
Application.Current.MainWindow = shell;
shell.Show();
#endif |
然而,#if有一些很差:
- 代碼缺乏易讀性和易維護性。若是代碼中散列這#if聲明,它會變得很難閱讀和維護。
- 調試變得困難。若是在構造方法中有一個編譯錯誤。當你嘗試打開文件時,Visual Studio 選擇了包含物理文件的解決方案而不是包含錯誤的解決方案,這意味這你必須手動的關閉這個文件而後打開正確的解決方案或者在沒有隻能提示的狀況下編輯代碼。
在類的大部分類似可是一些方法具備特定平臺的實現時使用部分類
當Silverlight和WPF之間的變化變得更加複雜時,你能夠建立部分類。標記共享的類做爲部分類而且而後爲特定的平臺建立一個部分類。這樣一樣應用到單元測試中。如下是一些額外的建議:
- 儘可能將特定平臺的方法保持爲私有的。在這種方式中,單元測試將不須要爲特定的平臺而包含特定的邏輯。
- 確保類有單獨的,清晰的職責,任何爲特定平臺編寫的部分類應該指改變詳細的實現內容。
注意:
若是在兩個平臺之間的區別變得很是普遍,而且兩種平臺的類變得很是不一樣,考慮建立指定平臺的類來替換掉部分類。
如下示例代碼展現了一個部分類,RealEstateService,它在Multi Targeting QuickStart項目中在Silverlight和WPF項目中共享。
namespace RealEstateListingViewer.Services
{
public partial class RealEstateService: IRealEstateService
{
public RealEstate GetRealEstate ()
{
...
return property;
}
} |
如下代碼實例展現了在Multi-Targeting QuickStart.中的RealEstateService類中指定Silverlight的部分類中的檢索圖片,
namespace RealEstateListingViewer.Services
{
public partial class RealEstateService
{
/// <summary>
/// Return the images. In Silverlight, you want to download the image
/// from a web server.
/// You can either store the images on the server or build an
/// HTTP handler to retrieve the images.
/// </summary>
private static BitmapImage GetImage()
{
Uri imageUri;
Uri source = App.Current.Host.Source;
if (source.ToString().StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
imageUri = new Uri("../Images/House.jpg", UriKind.Relative);
}
else
{
source = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}/", source.Scheme, source.Host, source.Port));
imageUri = new Uri(source, "Images/house.jpg");
}
return new BitmapImage(imageUri);
}
}
} |
如下代碼展現了在在Multi-Targeting QuickStart.中的RealEstateService類中指定WPF的部分類中的檢索圖片,
namespace RealEstateListingViewer.Services
{
public partial class RealEstateService
{
/// <summary>
/// Return the images. In a windows application, normally you
/// retrieve the image from a database.
/// But for simplicity, it is just being retrieved from the file system.
/// </summary>
private static BitmapImage GetImage()
{
return new BitmapImage(new Uri("../Images/house.jpg", UriKind.Relative));
}
}
} |
僅當你須要在一個平臺而不是其餘平臺上調用外部方法時使用部分方法
若是一些工做須要僅在Silverlight或者僅在WPF中執行。你也可使用部分方法。這意味着你能夠將一個方法的接口放到父類中而且將這個接口的實現僅放到指定平臺的類中。對於另外一個平臺,編譯器將會移除方法調用。這裏有一些部分方法的限制:
- 部分方法的聲明必須以partial關鍵字開始而且返回類型必須爲void。
- 部分方法能夠擁有ref參數可是不能有out參數
- 部分方法是隱性的private的,所以,不能爲virtual。
- 部分方法不能是extern的,引入呈現的主題決定了它們是否被定義或者被實現。
- 部分方法能夠擁有static和unsafe修飾符
- 分部方法能夠傳統的。在部分方法的聲明中定義了約束,並能夠選擇性地在實現中重複定義這些規則。參數和類型參數名稱在實現中沒必要與定義的名稱如出一轍。
- 你不能夠將一個delegate賦值給一個部分方法。
使用單一職責構建指定平臺的類
一般狀況下,將影響全部指定平臺的代碼放到一個單獨的類中(例如,服務或者服務代理)更有意義。若是大部分的邏輯在兩個平臺中狗不一樣的話那將會發生。在這種方式下,你能夠爲服務建立一個指定平臺的實現,例如一個獲取數據緩存,或者受權。這種方式在僅展示某一個平臺能夠提供的功能的狀況也能起做用。如下是一些額外的建議:
- 在不一樣的平臺中使用通用的接口來共享代碼。例如,在Prism類庫中,有幾個用於加載模塊類型的特定平臺的類,好比用於Silverlight的XapModuleTypeLoader和用於桌面程序的FileModuleTypeLoader。它們頭實現了IModuleTypeLoader接口。
- 當在不一樣的平臺中共享一些功能時,有利於組成總體繼承(例如,使用策略模式)。換句話講,肯定在一個共享類中分解出具備特定功能共享的代碼是否有益於。在某些狀況下,使用繼承是有意義的。
爲Silverlight和WPF建立各自的解決方案文件夾
使用解決方案文件夾保持你的解決方案的有組織性。一般,你能夠經過使用兩個文件夾來作這些,啓用一個用於Silverlight代碼,另外一個用於WPF代碼。例如如何組織你的應用程序的結構,查看Multi-Targeting QuickStart.
重構代碼時檢查Silverlight和WPF的引用
有時候,一個Silverlight引用可能陷入一個WPF項目,反之亦然。這是經過使用重構工具所引發的。若是你獲得關於未在WPF項目中引用的Silverlight組件意想不到的編譯器錯誤,請檢查引用。
過程指導
過程指導包含如下內容:
- 在Silverlight中開發內核。
- 在源項目與目標項目之間鏈接共享代碼。
- 爲Silverlight和WPF項目使用相同的命名空間。
接下來的幾節將會詳細的描述這些指導。
在Silverlight中開發應用程序內核
因爲Silverlight是WPF的一個主要的子集,相同的代碼在WPF中不須要大的改動就能夠運行,因此你應該在Silverlight中開發應用程序內核。
在源項目與目標項目之間鏈接共享代碼
對於那些Silverlight和WPF通用的文件可是須要不一樣的引用的狀況,你應該使用鏈接文件。一種方法就是使用項目鏈接器工具在源項目與目標項目之間鏈接共享代碼。
在Silverlight和WPF項目中使用相同的命名空間
因爲共享代碼要求相同的命名空間,因此保持項目之間的命名空間相同。
團隊構建指導
這一章節將會描述團隊構建指導。
在構建地配置團隊構建
若是你使用團隊構建來構建一個解決方案來承載WPF和Silverlight項目,你可能會遇到文件衝突的問題。默認狀況下,團隊構建將會把輸出從工程拷貝到一個單獨的輸出文件夾。因爲WPF和Silverlight工程應該擁有相同的輸出名稱,將會有文件衝突問題組織你編譯工程或者運行單元測試。
經過設置CustomizableOutDir屬性爲true,你能夠高數團隊構建構建到一個位置而不是將輸出拷貝到一個單獨的位置,這將會阻止名稱衝突的爲難題。
另外,若是你設置了這個屬性而且在你的構建中想要運行單元測試,你也須要指定TestContainers的位置。
<PropertyGroup>
<!--Build in place-->
<CustomizableOutDir>true</CustomizableOutDir>
</PropertyGroup>
<!—Override the BeforeTestConfiguration target to specify where the testcontainers live. -->
<Target Name="BeforeTestConfiguration">
<!--
Change the outdir, because the testtoolstask needs this to execute all the tests
-->
<PropertyGroup>
<OutDir>$(SolutionRoot)\Source\</OutDir>
</PropertyGroup>
<!--Create a list of all tests dll's to run (test only the desktop versions) -->
<CreateItem Include="$(SolutionRoot)\**\Desktop\**\Bin\Debug\%2a.Tests.dll">
<Output ItemName="TestContainer" TaskParameter="Include" />
</CreateItem>
</Target> |
注意:
這個例子假定你的桌面項目放置在一個名爲Desktop的文件夾中。MSTest.exe只有在目標桌面版本的是.NET Framework的時候才能運行單元測試。因此Silverlight測試程序集被執行。
Silverlight和WPF 對比
Silverlight和WPF都容許你基於XAML和.NET Framework開發豐富的用戶體驗的應用程序。然而,兩種平臺之間有一些不一樣,以及這些不一樣須要在Silverlight和WPF程序之間過分時或者當構建一個目標包括WPF和Silverlight應用程序時須要仔細的考慮。
注意:
這個主題描述了Silverlight4和.NET Framework 4.0一部分的WPF之間的不一樣。這些不一樣期待在未來的版本中北減小。
Silverlight和WPF 架構概述
WPF爲開發人員提供了一個構建包括UI,媒體和文檔等的富窗體應用程序統一的編程模型。WPF使得軟件開發人員提供了一個聲明爲基礎的語言(XAML),用於指定基於矢量的圖形,能夠擴展並充分利用硬件加速的優點,提供了「用戶體驗」(UX)一個新的水平。
Silverlight是一個跨瀏覽器,跨平臺實施的.NET框架爲提供下一代豐富的交互式媒體和內容經過網絡,臺式機業務應用程序和瀏覽器承載的富互聯網應用程序(RIA),能夠集成數據和服務從許多來源。 Silverlight容許開發人員構建應用程序,顯著加強典型的最終用戶體驗,與傳統的Web應用程序相比。像WPF,Silverlight提供一個基於XAML語言來指定用戶界面。
Silverlight和WPF共享了許多相同的功能和特性,可是它們創建在不一樣的運行時的堆棧的頂部,就是下面的插圖。WPF利用完整的.NET Framework和執行上的公共語言運行時(CLR)。 Silverlight是基於XAML的一個子集,完整的.NET框架,並在不一樣版本的CLR執行。
Silverlight與WPF之間的區別
爲了保持Silverlight的小而輕,一些WPF和.NET Framework功能在Silverlight中是不可用的。正由於如此,有多是微妙和不那麼微妙的差異是要構建一個針對WPF和Silverlight的應用程序時,或者Silverlight和WPF之間過分應用程序時要慎重考慮。本節將描述在Prism團隊開發Prism是遇到的一些主要的不一樣。這些區別針對Silverlight4和WPF 4.0,當前編寫時的版本。
資源
資源是一些能夠存儲幾乎任何元素(字符串,畫刷,樣式,數據源,以及許多其餘的)的鍵-值的簡單的集合。資源能夠關聯到幾乎任何暴露了ResourceDictionary類型的Resoures屬性的元素類。如下是Silverlight和WPF關於資源的一些不一樣:
資源能夠包含靜態或者動態內容。動態內容能夠在運行時背改變而且資源的消費者將會自動的被更新。Silverlight中不支持動態資源引用;所以,只有靜態資源引用可用。
觸發器
觸發器容許設計者定義控件的可視化行爲,經過聲明式指定它的屬性在對於事件或者屬性改變時作出反映時如何改變,例如在點擊一個按鈕時突出顯示按鈕。一般,觸發器在當一個控件的屬性發生變化時被觸發而且致使控件的一個或者多個屬性發生變化。觸發器被定義在樣式內部而且能夠被應用與指定目標類型的任何對象。
Silverlight並不支持Styles,ControlTemplates,或者DataTemplates(Triggers集合並不存在與這些元素中)的觸發器。然而,經過使用Silverlight的可視化狀態管理器(VSM)來定義控件模板交互也能夠達到相似的行爲。使用VSM,控件的可視化行爲,包含任何動畫和轉換,被定義在空間的模板中。這些能夠在Blend4中很容易的作到。然而,要知道XAML文件將會變得更復雜而且內置在Silverlight中的模板並無與WPF兼容。
Blend SDK 提供了用於動畫的Blend 行爲而且可視化狀態管理器能夠用於WPF和Silverlight應用一致的外觀及感受。
數據綁定
WPF和SIlverlight都提供了對數據綁定的支持。如下是Silverlight和WPF之間數據綁定的主要不一樣:
- 在Silverlight中,滅有OneWayToSource數據流模式
- 在Silverlight中,你不能直接綁定到XML數據。一個可能的解決方法是將XML轉換爲CLR對象,而後綁定到CLR對象。
- 在Silverlight中,沒有XMLDatProvider類。
- 在Silverlight中,沒有DataTemplateSelector類。在WPF中,這個類提供了基於數據對象和數據綁定元素的選擇DataTemplate的方式。然而,爲了支持特定的MVVM場景,Prism在它的DataTemplateSelector類中爲Silverlight提供了相似的功能。
命令
如下是WPF和Silverlight中關於命令的一些不一樣:
- 在Silverlight中路由命令不可用。然而,在Silverlight中ICommand接口能夠用,容許開發人員建立它們自定義的命令。Prism類庫提供了DelegateCommand 和CompositeCommand 類爲簡單的命令的實現。
- 在WPF中,控件能夠經過它們的Command屬性鏈接到命令。經過作這些,開發人員能夠聲明關聯的控件和命令。這也意味者一個控件能夠經過命令交互以使得它能夠滑行命令而且使得它的狀態自動更新。在Silverlight中國,一些控件支持Command命令,可是不想WPF那樣多。對於那些沒有提供了Command屬性的Silverlight控件,考慮使用Blend 行爲基於觸發器來喚醒命令。
- 在Silverlight中沒有輸手勢和輸入綁定。
雜項
如下是Silverlight和WPF之間的一些雜項的差別:
- 在Silverlight中,UIElement類有一個內部構造方法;所以,你不能建立一個控件繼承它。
- 在Silverlight中,沒有對X:Type擴展標記的支持或者自定義擴展標記的支持。
- 在Silverlight中,沒有對X:Static擴展標記的支持或者自定義擴展標記的支持。在Silverlight中,添加到TabControl控件的子項不會像WPF中那樣自動的被封裝懂啊一個TabItem中。
- 所喲Silverlight網絡調用都必須是異步的。
- Silverlight的網絡,除了信任外的瀏覽器應用程序,對於服務器客戶端訪問策略原產地之外的網站服務器。
更多信息
你能夠從Visual Studio Gallery 中下載項目鏈接器或者打開Visual Studio,在工具菜單中點擊擴展管理,在擴展工具管理窗口的聯機庫中查找項目鏈接器。
關於組織程序集結構來得到在Silverlight和WPF之間二進制兼容性的優點的更多知識,請看CLR 團隊發佈的博客
"Sharing Silverlight Assemblies with .NET Apps":
關於WPF 架構的更多內容,請看MSDN上的
"WPF Architecture":
關於Silverlight 架構的更多內容,請看MSDN上的
"Silverlight Architecture"
:
關於WPF和Silverlight之間的不一樣的總結更多內容,請看MSDN上的
"WPF Compatibility" on MSDN:
關於可視化狀態管理器和它如何在建立控件中工做的更多內容,請看MSDN上的如下關於建立自定義控件的文章:
關於建立一個提高信任的應用程序的更多內容,請看MSDN上的, see "Trusted Applications" on MSDN: