内部 Microsoft 模式和实践
构建 WPF 和 Silverlight 的应用程序使用单个代码基本使用 Prism
Erwin van der Valk
内容
简介
创建 Prism Silverlight 的版本
创建项目的链接器
在 Silverlight 中测试驱动开发
构建针对多的应用程序
标识哪些轻松可以为多个目标
分隔的演示文稿
生成特定于平台的服务
避免不一致,并尝试保留一个单独的代码狅
适应不同的平台的功能
条件编译
分部类
警告
摘要
复合应用程序指南 WPF 和 Silverlight,affectionately 也称为 Prism v2,已出了几个月现在。Prism 提供指导在区域之一是能够针对 Windows 演示基础 (WPF) 和 Silverlight 应用程序。它最初的有趣的我们的指南的这一部分满足非常有点电阻。为什么我们着重多目标的迭代在前几个时我们可能会花费组合提供指南我们时间?但在发布后发布的 Prism v2 项目,我们找到了大量的客户非常喜欢本指南的这一部分。尤其是,他们愿意帮助面向多-我们构建项目链接器工具,基本原样,该工具也已收到无法有我们想像的方法中正在使用。
因此一下我们所用编写面向多的应用程序和 Prism 如何帮助您执行同样的方法。
简介
我们开始工作 Prism v2,在 2008 年 8 月时, 我们知道我们想要同时支持 WPF 和 Silverlight。Silverlight 使大大大向关闭该间隔之间的一个丰富的客户端应用程序和访问功能和易用性 Web 应用程序的部署。第一个版本的 Prism 目标仅 WPF,但由于 WPF 和 Silverlight 非常类似,我们知道它不是太难创建 Silverlight 版本。并写入 Prism 有助于松散耦合,模块化的应用程序,因为我们认为 Prism 可能也有助于使您编写单个代码库从目标 WPF 和 Silverlight 的应用程序。在于,当然,虽然 WPF 和 Silverlight 类似,它们不是二进制兼容。API 和 XAML 语言本身也有微小差别,它很难 multi-target。
多的目标和 ShouldYou 关心的原因是什么?
面向多的是能够从一个代码库的多个平台为目标。在这种情况下我们正在谈论针对 Microsoft.NET 框架版本 3.5,包含 WPF 的普通桌面版本和.NET 框架的 Silverlight 版本。
因此为什么应您关心此功能?最明显的原因是要充分利用 WPF 和 Silverlight 的优点。在 WPF 中,您可以生成与现有与如 Office 的应用程序充分利用客户端平台进行交互的应用程序。也很容易通过 COM 互操作或 Windows 窗体的互操作,如重用现有的资产的。
WPF 为您提供更多的功能,而 Silverlight 提供多一个更好地到达因为它在多个平台上运行。它还在运行一个受保护的沙箱,以便最终用户可以安全地安装 Silverlight 而无需管理权限。您不需要完全 multi-target 应用程序,使其更值得。渚嬪的方式 您可能会使客户能够查看和更改自己的信息的一些在 Internet 上公开内部应用程序的小的部分。
创建 Prism Silverlight 的版本
即使我们认为该多目标就是我们的客户一有价值的能力,我们还必须创建一个有点 selfish 原因。我们想要生成的 Prism,Silverlight 版本,但我们不希望维护两个基本代码的开销。因为我们不知道多少代码我们能够重用,我们几个峰值。(在"灵活"术语) 一个峰值时间-装箱的研究,允许您了解有关问题域,以便您可以进行一个更有效的估计。因此我们几个查看多少 Prism v1 代码的基本我们可以迁移到 Silverlight 和如何硬它就是我们代码移植到 Silverlight 的峰值。fascinating 结束。我们的估计我们可以重用绕 Prism 库中代码的 80%而不修改它。
理想情况下,将有我们喜欢能够创建单个项目并将它编译到 WPF 和 Silverlight。存在这种方法的问题是输出的在 Visual Studio 中的,项目系统假定项目有一组引用、 一个编译器和一种类型。我们尝试多种方法来获取单个项目来发出输出 WPF 和 Silverlight,但它们都不起作用到我们的满意度。
实际上也起作用的一种方法是创建两个项目并链接到另一个项目文件。有关这种方法在好的事情是您精确控制通过什么每个输出应类似于。渚嬪的方式 可以控制哪些文件应该共享或应为每个项目引用的程序集。链接的文件的任何更改将立即反映在这两个项目。
创建项目的链接器
我们 spiking 阶段之后, 我们同意的链接文件的方法使用,尽管它是非常乏味和容易出错。然后,我们有大量的讨论: 应我们花费大量的时间以构建一个工具,帮助多目标而不是花的时间创建指南吗?可能不明显,但生成并传送到 Visual Studio 集成的工具对我们非常昂贵。集成使用 Visual Studio 将位的时间和其自身。将添加到的时间花费生成的安装程序根据严格 Microsoft 内部签名准则签名和测试上多个不同的环境的工具。所有这些因素减少的时间我们可以创建实际的指南。但由于我们非常强认为此工具将帮助我们在我们的工作,并给我们的客户非常有用,我们决定继续操作并构建它。
在两周迭代的第一个几,我们从事几乎以独占方式项目链接器。之前所述,我们收到来自社区的相当的一些反馈。原因我们花费时间上针对多的?Wasn't Prism 一个项目来帮助构建复合应用程序吗?但是,虽然我们已创建项目的链接器,我们还使用它创建的 Prism Silverlight 版本。因为我们使用的项目链接器 (在内部,这称为狗的 fooding,或吃自己止食品),和我们确信我们工具会非常可用。
我们有意保留项目的链接器非常简单。它不在它的作用,神奇并做您使用项目链接器可以轻松地手动完成。顾名思义,您可以在一起,如 图 1 所示链接两个项目。将您添加到第一个项目中的任何文件将作为链接文件中添加到第二个项目中。如移动、 重命名,或删除将也会反映更改。
图 1,则 使用项目链接器之间的链接文件的项目
因为您可能希望控制要链接的文件,我们实现了一个简单的命名约定。通过默认,链接文件扩展名为.xaml 名称的文件除外的所有文件。但是,如果想要创建特定于单个平台的文件,您可以附加的后缀.Desktop.Silverlight 它。如果您希望,您可以更改此命名约定。在项目文件中可以找到正则表达式,确定是否需要将链接文件。下面是一个示例:
ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?Silverlight
(\\.*)?$;\.desktop;\.Silverlight;\.xaml;^service references(\\.*)?$;\. clientconfig;^web references(\\.*)?$"
要在创建 Prism v2 宝贵,事实证明项目链接器。 后使用该次要突发的这种方法我们将介绍以后,它是这样一个愉快体验能够一次编写代码,并将它编译到一个 Silverlight 和 WPF 版本。 稍后后, 项目成员必须额外功能,它们会喜欢有,项目链接器的列表即使没有这些的功能该工具非常切换而可用。 该工具的简单性变得比我们想像的有很多更通用。 它有 CodePlex 上一段时间后,我们听到人他们未使用它只是为了创建 Silverlight 和 WPF 应用程序。 一些人还使用它链接不同的 Visual C++ 项目在一起。 其他人使用它具有相同的业务运行在 Silverlight 的应用程序和 Web 服务使用 Silverlight 应用程序中的验证规则集。
在 Silverlight 中测试驱动开发
执行在 Silverlight 中的测试驱动开发用于另一个区域中我个人发现多个目标很方便。 我的测试驱动开发的一个狂热。 尽管没有一个极好的单元测试框架可用 Silverlight,它有一些缺点。 它的 Visual Studio,不集成,并不能选择的测试运行。 它将在您测试的程序集中运行的所有测试。
现在,即使我要编写将只在 Silverlight 上运行的应用程序,我仍将创建该项目,只编写和运行单元测试的链接的 WPF 版本。 我喜欢有能够编写测试并直接从 Visual Studio 然后运行它。 秒内, 将却结果的测试,因此,"->-> 修复-> 测试的测试代码"周期很短。 这是用于我的工作效率。 只是不要忘记您检查您的代码中,因为有时,测试将传递一个平台上并在其他失败前运行 Silverlight 单元测试。
构建针对多的应用程序
生成 Prism 库、 股票交易商引用实现和快速入门的 Silverlight 版本,时我们学习了一些有价值的最佳做法有关多的目标。 最重要的最佳做法是一个松散耦合和模块化体系结构可以真正帮助您针对多的工作。 松散耦合体系结构允许您的应用程序的部分您想要 multi-target 选择。
标识哪些轻松可以为多个目标
您可以构建您的解决方案来支持面向多-之前,重要的了解可以和不能很容易地为面向多的内容。 一般情况下,您可以说出最相关的业务逻辑的代码可以非常轻松地将多为目标。 例如:
- 表示逻辑。 这是在响应用户操作和控件的数据传递到可视元素的逻辑。
- 业务逻辑和业务规则。 业务逻辑驱动器的业务流程,并且业务规则可以在业务实体上执行验证。
- 业务实体。 这些是代表您的应用程序的数据的类。
通常很难 multi-target 多与基础结构相关的代码。 下面是示例:
- 视觉元素 (视图) 指定控件,如的可视元素的方式不同足够 WPF 和 Silverlight,对这些硬盘 multi-target 之间。 对每个的平台有不同的控件不仅用于指定布局的 XAML 也具有不同的功能。 虽然这并不难 multi-target 非常简单的视图或某些简单样式,您将快速运行到限制。
- 配置设置 Silverlight 不包括 System.Configuration 命名空间,不支持配置文件。 如果要使您 Silverlight 的应用程序配置,您需要生成一个自定义解决方案。
- 数据访问 Silverlight 应用程序可以通过数据的访问,唯一方法是通过 Web 服务。 与不同的 WPF,Silverlight 应用程序不能直接访问数据库。
- (与其他应用程序、 COM,或 Windows 窗体) 的互操作 WPF 应用程序在完全信任环境中的可以与您的计算机上其他的应用程序进行交互,或使用现有的资产,如 COM 或 Windows 窗体对象。 这是不可能在 Silverlight,因为它在一个受保护的沙箱中运行的。
- 日志记录和跟踪。 因在受保护的沙箱,Silverlight 应用程序无法写入日志信息到文件 (除独立存储中) 的事件日志或跟踪信息。
为了设计允许您轻松地重复使用您的业务逻辑的应用程序,您应再试图分隔的内容很容易 multi-target 到从 multi-target 困难的内容。 有趣的是,这是完全的典型 Prism 应用程序体系结构。 图 2 显示了 Prism 应用程序的典型体系结构。
图 2 典型 Prism 应用程序结构
在当前的关系图视图是执行应用程序的可视化效果方面的类。 通常,这些是控件和页,并在 WPF 或 Silverlight 应用程序的情况下它们通常定义布局在 XAML 中。 您的应用程序的逻辑被分解成单独的类。 我将深入有点后面这在设计模式时我谈论分隔的演示文稿的模式。
在此关系图中的应用程序服务可以大量的各种各样的功能。 渚嬪的方式 一个记录器或数据访问组件可认为应用程序服务。 prism 还提供了几个这些服务,(如在 RegionManager 或在 XapModuleTypeLoader。 我介绍有关构建特定于平台的服务时,我将更讨论这些服务。
分隔的演示文稿
作为我们提供 Prism 本指南的建议您将表示逻辑从应用程序的可视化效果方面。 大量的模型-视图-ViewModel 或模型-View-Presenter 的设计模式可帮助您与此。 这些模式的内容最共同拥有的是它们描述如何拆分您用户界面相关代码 (和标记) 到单独的类,每个不同的职责。 图 3 显示了模型-视图-ViewModel 模式的示例。
图 3 模型-视图-ViewModel 模式的示例
模型类有代码以包含和访问数据。 视图通常是具有的某些数据模型和 ViewModel 中显示的代码 (最好是在 XAML 标记的形式) 的控件。 然后没有一个名为 ViewModel、 PresentationModel,或将保存尽可能 UI 逻辑的演示者的类。 通常,使您与 UI 相关的代码的单位的可测试尽可能实现分隔的演示文稿图案。 非常难单元测试代码视图中的是这些演示文稿模式帮助您尽可能 ViewModel 可测试类中放置尽可能多的代码分隔开。 理想情况下,您将不包含任何代码视图只是一些 XAML 标记定义您的应用程序的可视方面,某些显示来自您 ViewModel 和模型的数据绑定表达式中。
多的目标时,分隔的演示文稿图案将具有另一个重要的优点。 它允许您重复使用您的 UI 逻辑的所有,因为具有该逻辑分解成单独的类中。 虽然这并不难 multi-target 一些则在视图中 (XAML、 控件和代码隐藏) 我们已经找到了 WPF 和 Silverlight 之间的差别是大足够的 multi-targeting XAML 并不可行。 XAML 具有不同的能力,可用于 WPF 和 Silverlight 控件不相同。 这不仅会影响在 XAML 中,但它也会影响代码隐藏。
尽管这不可能您能够重用您与 UI 相关的代码的所有,分隔的演示文稿模式可以尽可能多的表示逻辑尽可能重新使用。
生成特定于平台的服务
生成 Prism 库和该股票交易商引用实现,时我们严格遵循单一的责任原则。 这一原则描述了每个类,应只有一个理由更改。 如果类地址的多个问题或有多个职责,它具有多个要更改的原因。 渚嬪的方式 可以从数据库加载报表和打印该报表的类可以更改数据库更改或如果更改报表的布局。 如果您的类不太多有趣指示: 如果您发现要确定描述它的职责的类的名称的困难,则它具有太多的责任。
如果您遵循单一的责任原则,您将通常得到很多较小的类都具有其自己的离散责任和一个描述性名称。 我们通常考虑这些类为应用程序的服务的许多因为它们提供您的应用程序服务。
此单个的责任原则真正帮助时多的目标。 需要,渚嬪的方式 加载过程中 Prism 模块。 此过程的各个方面的大量 WPF 和 Silverlight 相近。 某些相似之处包括如何在 ModuleCatalog 跟踪的模块存在于系统和在 ModuleInitializer 如何创建模块实例并在其上调用 IModule.Initialize() 方法。 但然后同样,WPF 和 Silverlight 之间如何,我们正在加载包含该模块的程序集文件非常有点不同。 图 4 说明了这。
图 4 Prism 中加载的模块
这是非常合理的 WPF 应用程序从磁盘加载的模块。 因此,这是在 FileModuleTypeLoader 的用途。 因为其受保护的沙盒不提供访问鏂囦欢绯荤粺这不会使适合一个 Silverlight 的应用程序。 但是对于 Silverlight,您需要一个 XapModuleTypeLoader.xap 文件中加载模块。
我们创建的每个在不同职责范围较小类,因为它是很多更易于重复使用这些类中的大多数和创建仅特定于平台的服务来封装平台之间不同的行为。
避免不一致,并尝试保留一个单独的代码狅
鍗充娇 Prism 中的大多数功能很容易地移植到 Silverlight 中,我们不可避免地遇到情况下,我们将在其中依赖 Silverlight 中不存在的 WPF 中的一个功能。 依赖项属性的继承是其中之一。 在 WPF,可以在控件上设置依赖项属性,它将自动继承及其子级的所有。 我们使用此功能区域相关联区域经理。 遗憾的是,自动属性继承在 Silverlight 中不可用。
Silverlight,我们不得不创建一个区域经理可能位于通过一些其他机制之前延迟的区域创建的解决方案。 调整几我们可以重复此代码使用 WPF 的。 我们可以保存在原始的、 更简单的解决方案的 WPF 和 Silverlight,使用新的解决方案,但然后我们将不得不维护两个基本代码,并提供一个不同的公共 API。
试图生成使用 WPF 和 Silverlight 中的一个功能,您将不可避免地运行到在平台之一不支持您要使用的功能的情况。 这种情况下的您最佳防御是尝试解决这些不兼容"问题",并在这两种环境中创建一个有效的解决方案。 维护一个代码库很多可以比更为维护两个代码轻松使用!
适应不同的平台的功能
某些情况下它不会有意义或不可能解决平台的差异如没有普通的解决方案将 WPF 和 Silverlight 中工作时。 发生这种情况时有一些要考虑的策略。 对于任何但小且独立的平台的差异建议构建特定于平台的服务。 但您可以为小的平台的差异考虑条件编译或分部类。
条件编译
所要做以适应不同的平台功能的最简单的事情是使用条件编译。 可以创建代码的节被编译到 Silverlight 只能或只 WPF 使用 # if SILVERLIGHT pre-compiler 语句, 图 5 ,所示。 这看起来极大的方便,但在方法或类可以很快变得无法读取使用这种方法。
图 5 条件编译
#if SILVERLIGHT
Application.Current.RootVisual = shell;
#else
shell.Show();
#endif
我们已经发现应使用 # if 指令 pre-compiler 仅 sporadically 更改单个代码行。 因为确实会受到影响可读性,不要建议使用的任何但这种非常简单的情况下,此方法。
分部类
您可以使用另一个方法是分部类的用法。 使用此技术,可以创建小的一个或两个方法中的主要共享,但不同的类。 此技术非常可用于调整 WPF 和 Silverlight 之间的小实现更改。
我发现异常的区域是特别有用的分部类。 在.NET Framework 中,通常建议使您异常可序列化。 但是,Silverlight 不支持 [可序列化] 属性。 使用分部类,可以重复使用大多数在异常代码,但您可以仅在.NET 代码的变量上应用 [可序列化] 属性,如 图 6 所示。
图 6 的异常的分部类
// MyException.cs
Public partial class MyException : Exception
{
. . .
}
// Desktop only additions to this class.
// MyException.Desktop.cs
[Serializable]
Public partial class MyException : Exception
{
protected MyException (SerializationInfo info, StreamingContext context) : base(info, context) { }
}
遗憾的是,分部类从可发现性和可读性问题会降低。 它并不立即清除在哪个文件驻留您的功能。 类应具有一个单一的责任,并被命名为以反映该单个的责任。 如果您发现一个类不和在 WPF 中的另一个在 Silverlight 中的一个方法中的内容,它不是以下单个的责任模式。 为一个描述性的名称服务提取该特定于平台的代码通常是更好的解决方案。
因为 Visual Studio 永远不会设计为适应多的目标,您可能会运行到由该方法所导致的一些特点。 它们都不是非常严重,但知道什么是有效的帮助。
Visual Studio 知道链接的文件可放在不同类型的项目中。 具体取决于是否打开链接的文件从 WPF 或 Silverlight,它将相应地调整,智能感知。 通常,很可能您使用的语言构造中只有一个平台可用的。 编译您的解决方案,因为您的代码是无效的平台之一时,您将会遇到生成错误。 具体取决于是否打开 WPF Silverlight 中的该文件将确定是否将会看到红色的波浪。 但一旦您增长习惯于这一事实在一个项目中的更改可以断开其链接的项目,您快速将知道要查找。
项目引用也是有意义的。 项目链接器不会自动添加引用。 我们构建该工具,时查找到此但没有太多的边缘情况下,使该可靠。 例如,Silverlight 的二进制文件有 WPF 比二进制文件的名称不同。
最后,您可以将几乎任何类型文件,如资源文件的链接。 然而,您应确保属性,如在编译选项是与这两个项目之间。
我们的客户的许多绝对喜欢 Prism。 即使您的应用程序不需要 Prism 提供复合功能,它可以仍受益极其灵活 Prism 将提升的体系结构。 很容易地 multi-target 您的应用程序是那些只是一个优点,并且它是一个非常好! 您可以下载二进制文件和 项目链接器的源代码文件从 Microsoft 下载中心。 您可以了解更多有关信息 联机 prism.
Erwin Van der Valk Microsoft 已经模式开发人员,操作团队。 他也是一个 scuba diver、 一个功夫 fu 母版和一个金属 guitarist。 为他提供了一些代码 — — 他向右、 kicking 和尖叫,’ll 检查并时这样做将大量的噪音! 签出他在使该噪音 erwinvandervalk。 网络.