Genesis Smart Client v2.0 - Ruby - 用户体验平台






4.50/5 (6投票s)
本文介绍了 Genesis Smart Client Framework v2.0 (Project Ruby)。它专注于用户界面组件,称为用户体验平台 (UXP),使用 PRISM for WPF 和 Silverlight 开发。
引言
在过去的一年里,我通过提供完善的文档和支持,将我自己的 C# 智能客户端平台开源给各位。当时的代码已经有 5 年历史,相当陈旧。为了跟上时代和标准,我一直在计划从头开始重写这个平台。届时将不再向后兼容,但 Genesis v1.0 将永久在线可用。
在 Genesis v1.0 的开发过程中,我曾认为自己比其他人聪明,于是为开发过程中出现的各种问题制定了自己的标准。之后,我掌握了项目管理和规划的艺术,并随着年龄的增长获得了一些智慧。Genesis v2.0 的规划已超过一年,开发工作已在更基础的层面进行,为 Genesis v2.0 做准备。我坚持遵循标准并采用了 Microsoft Patterns and Practices,以确保新项目能被广泛接受,并拥有更广泛的支持基础。
本文将详细介绍我在创建用户体验平台 (UXP) 过程中所做的所有决定,该平台支持 WPF 和 Silverlight 的多目标开发。源代码已上传至 CodePlex,变更集为 46607。您可以通过 此处 下载。WPF 和 Silverlight 的多目标支持得益于 Microsoft 的 Patterns and Practices 团队的 PRISM (Composite Application Library)。
我将使用 Visual Studio 2010 和 Microsoft .NET 4.0 的最新源代码版本。同时,在等待 PRISM v4 正式发布期间,我还包含了 Composite Application Library 的一个自定义 .NET 4.0、VS2010 版本的 DLL。我知道几周前 (截至 2010 年 6 月) 他们发布了 PRISM v2.2,但其中一些库仍然使用 .NET 3.5。我的版本是 100% Microsoft .NET 4.0。
设计考虑因素
PRISM
我选择 PRISM (Composite Application Library) 作为 Genesis v2.0 创建模块的推荐实践。我主要的设计考虑是摆脱 Genesis v1.0 框架中加载新模块所需的繁琐配置。PRISM 内置了对基于 XAML 的配置文件支持,并可自动下载缺失的文件。
PRISM 提供了一种加载模块和创建控制反转 (IOC) 容器的机制。这使得方法更加模块化。我还发现,得益于 Project Linker,这种方法在创建多目标 (WPF 和 Silverlight) 项目时非常有用,而且几乎没有麻烦。
PRISM v4 看起来很有前景,特别是考虑到它对 Windows Phone 7 Series 的支持的可能性。
模型视图视图模型 (MVVM)
PRISM 鼓励实现标准的模型视图视图模型 (MVVM) 模式。我个人采用 MVVM 模式的动力来自我 2010 年 1 月开发的第一个 Silverlight 应用程序。我未能应用 MVVM 模式,导致开发很快变成了一场维护性的噩梦,需要“弄清楚各种绑定和随机的 RIA (beta 2) 查询之间发生了什么”。
2 月份,我完善了一种新的 MVVM 设计,发现在应用程序执行过程中,这种设计更加可预测和可控。我还放弃了 RIA,我们现在就来谈谈这个。
RIA 是纯粹的邪恶
我不是在批评 RIA,事实上我觉得它很巧妙,但仅此而已。它不能被视为开发任何东西的严肃或可靠的平台,前提是你能度过开发阶段。我说 RIA 是邪恶的,因为它表面上看起来一切都很友好 Pleasant。它让你产生一种错误的预感,认为事情会很容易,但你的直觉告诉你,由于你无法弄清楚它内部到底发生了什么“黑魔法”,它最终会反过来咬你一口!
Microsoft 在 VS2010 中为 RIA 做了很多工作,我的经验是基于 VS2008 的。现在我对 RIA 如何进行“舞台表演”有了深入的理解,足以让我决定实现一个我一直在为 Genesis v2.0 的纯 WPF 版本开发的设想,使其也能与 Silverlight 一起工作。在实现目标后,我决定为 Genesis v2.0 添加 Silverlight 支持,并使用 Composite Application Library。
NetTiers
NetTiers?不可能,我听到你这么说!我相信大多数人对 NetTiers 有很棒的体验,也有很多人从内心深处憎恨它。我发现 NetTiers 是一个很好的服务型数据层实现。通过一点调整和集成在线模板,我将模板改造成了一个支持 WCF 服务和 WF4 工作流的代码生成器。
我抛弃了所有的 Web Forms、Web Admin 和 Windows Controls,专注于通过 WCF 将实体传递给我的 XAML 绑定。我还开发了模板,为 NetTiers 生成代码的每个方法生成 WF4 工作流活动 (例如 CRUD 的 Insert
、Update
、Delete
、GetAll
、GetByPrimaryKey
、GetByForeignKeys
等)。这些活动将使用 WCF 客户端代理连接到 WCF 服务器。我还包含了一些漂亮的 Activity Designers,使工作流更容易使用。
通过使用工作流服务应用程序,我可以将工作流通过 WCF 提供给 WPF 和 Silverlight。坏消息是,本文不包含任何后端代码,因为我只编写了用户界面。我将在下一篇文章中介绍后端。
第三方库
正如那些一直关注 Genesis v1 免费开源版本演变的人所知道的,默认用户界面曾基于 DevComponents Ribbon。我移除了代码中的所有许可证密钥,并发布了演示库。几个月后,我删除了对该库的所有引用,并发布了一个纯粹基于标准 .NET 控件的版本。
这个版本是从头开始构建的,只使用了大家使用 Visual Studio 2010 和 Microsoft .NET 4.0 都应该能够访问的标准 .NET 组件。
架构
Genesis v2.0 - Ruby 的用户体验平台 (UXP) 由 Shell、Infrastructure 库和 View 库组成。在 StockTraderRI PRISM 示例和所有建议中,Shell 包含带有所有已配置区域的主窗口。我觉得这非常有限,于是我实现了一个机制,可以像加载普通模块一样加载主窗口。这意味着您可以提供自己的主窗口,从而不受我所认为的良好设计实践的限制。这就是 View 模块的目的。在本文后面讨论每个模块时,我将详细介绍代码更改。
使用 PRISM 使我无需创建模块管理框架,并能被社区中的熟悉者更广泛地采用,同时为新开发者提供良好的支持。它也是许多企业开发人员中接受的标准,并基于 Microsoft Patterns and Practices 的研究。看起来大家都是从 Composite Application Block (CAB) 和 Smart Client Software Factory (SCSF) 模式中学习的。PRISM 轻量级,总开销不超过 300KB。正常开发周期中的代码要求也几乎是透明和非侵入性的。
单元测试也是需求列表中的一个重要部分,PRISM 再次出手相助,并得到了 MVVM 的支持。PRISM 与 MVVM 使得轻松进行单元测试、模拟和用户界面测试自动化成为可能。模拟是通过 Moq 完成的。
源代码
源代码分为 Desktop (WPF) 和 Web (Silverlight),每个都有子目录:Infrastructure、Modules、Shell 和 Unit Tests。我使用 Project Linker (可在 compositewpf.codeplex.com 上找到) 来链接 Silverlight 项目到 WPF 项目。Project Linker 会自动将 WPF 项目中创建/修改的文件复制到相应的 Silverlight 项目。任何包含 .Desktop 或 .Silverlight 的文件都不会从其父项目中复制。
源代码已上传至 Code Plex,变更集为 46607。您可以通过 此处 下载。
基础结构
![]() |
第一个项目是 _BlueMarble.Genesis.Infrastructure_ 项目。它包含了应用程序所有层通用的基础功能。这意味着每个模块都必须引用此项目才能在复合应用程序的上下文中正常运行。我从 Composite Application Library 团队提供的 StockTraderRI 示例应用程序中复制了一些功能。这是为了方便多目标开发。他们已经考虑到了多目标开发者会面临的许多障碍。 在 **深橙色** 突出显示的区域,您将看到构成此库的文件夹和文件。在浅橙色突出显示的区域,您将看到等效的 Silverlight 项目。它与 WPF 项目逐字匹配,除了文件名中包含 .Desktop 或 .Silverlight 的地方。 Backgrounds 文件夹包含我正在开发的一些动态背景模板。目前 Genesis v2.0 - Ruby 只有一个默认背景。此背景包括 Ruby (我的鹦鹉) 的 XAML 图像、信息氛围 (将文本淡入淡出) 以及结合了这两个组件的单个控件引用的背景本身。 Behaviors 文件夹包含处理复合应用程序多个组件行为的代码,包括对话框的显示方式。具体来说,此文件夹中包含 .Desktop 和 .Silverlight 文件,用于处理通过 WPF 和 Silverlight 显示客户端窗口的差异。大部分代码都从 StockTraderRI 示例中复制而来,因为它们涉及 WPF 和 Silverlight 之间的一致性。 Converters 文件夹包含可供多种项目类型使用的通用转换器。这些都不是特定于用户体验平台功能的。 Interfaces 文件夹包含模块化主窗口视图所使用的接口。这包括 Resources 文件夹包含 Infrastructure 库的所有资源,包括基本的 Infrastructure 项目根目录中的文件包含全局错误/警告抑制、验证异常管理、一个可视化树助手。我修改了 Observable Command 实现,允许 WPF 和 Silverlight 使用相同的命令系统。 |
IShellView.cs
namespace BlueMarble.Genesis.Infrastructure
{
public interface IShellView
{
void ShowView();
}
}
IShellViewModel.cs
using System.Windows;
namespace BlueMarble.Genesis.Infrastructure
{
public interface IShellViewModel
{
IShellView View { get; }
DependencyObject ShowView();
}
}
Shell
![]() |
第二个项目是 _BlueMarble.Genesis.Shell_ 项目。它包含了加载所有模块并显示主窗口所需的基础功能。 在 **深橙色** 突出显示的区域,您将看到构成此库的文件夹和文件。在浅橙色突出显示的区域,您将看到等效的 Silverlight 项目。它与 WPF 项目逐字匹配,除了文件名中包含 .Desktop 或 .Silverlight 的地方。 Bootstrapper 文件夹包含 Unity Bootstrapper。此代码负责加载模块并显示主窗口。它从 _App.xaml.cs_ 文件调用。在屏幕截图中,您可以看到 _ShellBootstrapper.Desktop.cs_。此文件包含仅特定于 WPF 平台的功能,不会链接到 Silverlight 项目。此文件专门包含 WPF 客户端如何使用 Enterprise Library 处理异常的功能。 Converters 文件夹包含 Shell 正常运行所需的转换器。 Resources 文件夹包含 Shell 所需的各种资源,包括错误字符串和其他可本地化内容。 |
ShellBootstrapper.cs
protected override DependencyObject CreateShell()
{
return null;
}
我修改了 CreateShell
方法,使其简单地返回 null
,以便将主窗口与 Shell 分离。
App.XAML.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
#if (DEBUG)
RunInDebugMode();
#else
RunInReleaseMode();
#endif
this.ShutdownMode = ShutdownMode.OnMainWindowClose;
}
private static void RunInDebugMode()
{
UnityBootstrapper bootstrapper = new ShellBootstrapper();
bootstrapper.Run();
}
private static void RunInReleaseMode()
{
AppDomain.CurrentDomain.UnhandledException += AppDomainUnhandledException;
try
{
UnityBootstrapper bootstrapper = new ShellBootstrapper();
bootstrapper.Run();
}
catch (Exception ex)
{
HandleException(ex);
}
}
private static void AppDomainUnhandledException
(object sender, UnhandledExceptionEventArgs e)
{
HandleException(e.ExceptionObject as Exception);
}
private static void HandleException(Exception ex)
{
if (ex == null)
return;
ExceptionPolicy.HandleException(ex, "Default Policy");
MessageBox.Show
(BlueMarble.Genesis.Shell.Resources.ErrorStrings.UnhandledException);
Environment.Exit(1);
}
}
主窗口视图
![]() |
第三个项目是 _BlueMarble.Genesis.View_ 项目。它包含 Shell 应该显示的主窗口。目的是让开发人员能够设计自己的主窗口,并按照他们要求布局区域。开发人员不必局限于使用我预先确定的 在 **深橙色** 突出显示的区域,您将看到构成此库的文件夹和文件。在浅橙色突出显示的区域,您将看到等效的 Silverlight 项目。它与 WPF 项目逐字匹配,除了文件名中包含 .Desktop 或 .Silverlight 的地方。 _ShellView.Desktop.xaml_ 文件是 WPF 平台特有的,在 Silverlight 项目中存在一个等效的 _ShellView.Silverlight.xaml_ 文件。此文件包含此应用程序主窗口的基本 XAML 布局。 _ShellView.Desktop.xaml.cs_ 文件实现了 _BlueMarble.Genesis.Infrastructure_ 库中的 _ShellViewModel.cs_ 文件包含主窗口的 ViewModel。 _ShellViewModel.cs_ 文件实现了 _BlueMarble.Genesis.Infrastructure_ 库中的 _ViewModule.cs_ 文件包含模块逻辑,并在应用程序启动时处理加载主窗口。 |
ViewModule.cs
public class ViewModule : IModule
{
private readonly IUnityContainer container;
private readonly IRegionManager regionManager;
public ViewModule(IUnityContainer container, IRegionManager regionManager)
{
this.container = container;
this.regionManager = regionManager;
}
#region IModule Members
public void Initialize()
{
RegisterViewsAndServices();
IShellViewModel shellViewModel = this.container.Resolve<IShellViewModel>();
shellViewModel.ShowView();
}
protected void RegisterViewsAndServices()
{
this.container.RegisterType<IShellViewModel, ViewModel>();
this.container.RegisterType<IShellView, Shell>();
}
#endregion
}
_ViewModule.cs_ 文件是 View Module 的控制器,它负责在 IoC 容器中注册 Shell View 和 Shell ViewModel。注册后,它会创建一个新的 Shell ViewModel 实例并调用 ShowView
方法。
public DependencyObject ShowView()
{
View.ShowView();
return View as DependencyObject;
}
WPF 窗口

此图像显示了 WPF 应用程序的输出。
Silverlight 窗口

此图像显示了 Silverlight 应用程序的输出。
在这两个应用程序中,动画都能正确播放,用户可以看到用户界面。至此,Genesis Smart Client Framework v2.0 (Project Ruby) 的用户体验平台 (UXP) 就完成了。在下一篇文章中,我将添加连接用户安全系统和存储每个用户设置和权限的数据库所需的业务逻辑。
更新
- 2010 年 6 月 3 日 - 发布原始文章
- 2010 年 6 月 7 日 - 更新文章,包含代码示例和更具体的文件描述