本主题帮助你理解来自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: