[Silverlight]用Prism打造自己的程序开发架构
大家都知道Prism自带的StockTraderRI的例子,这个例子给我们展示了由不同的功能模块(包括里面的页面)组合成主界面(shell)的方法;我当初看到这个例子总觉得怪怪的,原因是我的项目经验中基本没有出现过这样的需求,而出现最多的是“在主界面中显示(调用)不同的模块中的页面”:例如我在主界面有个菜单,如图:
1、我想在点击某个菜单项的时候将某个模块中的某个页面显示在主界面菜单下面的的内容区,而且要有显示的动画效果,如图:
这个动画效果不太好截图,其实是通过向左滑动来切换页面,大家下载代码看看就明白了,
2、各个模块都要按需加载,模块加载过程中要有进度显示,如图:
3、全部采用MVVM设计。
我想可能也有其他人有过我这样的困惑,所以为了让大家少走弯路,少造轮子,就有了这篇随笔,让我们不再困惑于StockTraderRI的那个例子,实现更适合自己项目的程序开发框架。
我的项目结构如图:
PrismDemoMain是主Silverlight程序,PrismDemoMain.Web用来宿主主程序;
PrimDemoCommon是Silverlight类库,提供给主程序和各个模块项目用来引用(提供公共对象)。
Modules解决方案文件夹下面的两个项目ModuleA和ModuleB是分别对应两个模块ModuleA和ModuleB,例如ModuleA的对象代码如下:
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.MefExtensions.Modularity;
using System.ComponentModel.Composition;
using PrismDemoCommon;
namespace ModuleA
{
[ModuleExport(typeof(ModuleA))]
public class ModuleA : IModule
{
public void Initialize()
{
//throw new NotImplementedException();
}
}
}
by the way,我个人更喜欢用MEF,Prism中我也只用MEF的那个扩展,例如MefBootstrapper等,甚至有点讨厌unity那个,可能是代码洁癖的原因吧,其实整个程序都可以用MEF来管理对象的初始化,杜绝new object代码,可以参照我上一篇随笔中的代码。
ModuleA项目中包含两个文件夹Views和Viewmodels,这两个文件夹各放什么大家都知道;值得一提的是,我将Views中的各个View分成两类,一类是MainView,继承自IMainView接口,另一类则不用继承这个接口,MainView是用来显示在主界面内容区的View,继承自IMainView也是为了能在主界面内容区显示方便,而其他View则是被MainView调用的View。
ModuleA中的View1的代码如下:
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel.Composition;
using ModuleA.ViewModels;
using PrismDemoCommon;
namespace ModuleA.Views
{
[Export("ModuleA$View1", typeof(IMainView))]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class View1 : UserControl,IMainView
{
[ImportingConstructor]
public View1(ViewModel1 viewModel)
{
InitializeComponent();
this.DataContext = viewModel;
}
}
}
看一下模块ModulesCatalog.xaml文件,很简单。
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<!-- Module info without a group -->
<Modularity:ModuleInfo Ref="ModuleA.xap" ModuleName="ModuleA" ModuleType="ModuleA.ModuleA, ModuleA" InitializationMode="OnDemand" />
<Modularity:ModuleInfo Ref="ModuleB.xap" ModuleName="ModuleB" ModuleType="ModuleB.ModuleB, ModuleB" InitializationMode="OnDemand" />
</Modularity:ModuleCatalog>
PrismDemoMain主程序中有个MainPageModule的文件夹,其实里面也是一个Prism的模块,是该程序真正的主界面实现,采用View注入到shell中,shell中就一行代码:
这样做的好处是,假如我对这个主界面不满意,我可以方便的用其他的MainPageModule来替换这个,甚至可以做成多个主界面供用户选择。
载入模块,显示页面,动画效果等都是在这个模块完成的,例如点击菜单显示切换视图的代码如下:
{
//取得当前主视图的实例
IMainView mainView = ServiceLocator.Current.GetInstance<IMainView>(currentMainViewName);
if (mainView != null)
{
eventAggregator.GetEvent<ShowMainViewEvent>().Publish(mainView);
}
}
private void ActiveView(object obj)
{
string mainViewName = obj.ToString();
//如果当前视图就是要激活的视图,则返回
if (currentMainViewName == mainViewName) return;
currentMainViewName = mainViewName;
//获取激活该视图需要的模块名称。
string moduleName = currentMainViewName.Substring(0, currentMainViewName.IndexOf("$"));
//初始化ModuleManager,加载事件等
InitializeModuleManager();
//根据模块名取得模块信息
ModuleCatalog moduleCatalog = ServiceLocator.Current.GetInstance<ModuleCatalog>();
if (moduleCatalog == null) return;
ModuleInfo moduleInfo = moduleCatalog.Modules.FirstOrDefault(p => p.ModuleName == moduleName);
if (moduleInfo == null) return;
//判断模块是否已经初始化
if (moduleInfo.State == ModuleState.Initialized)
{
//显示当前主视图
ShowCurrentMainView();
}
else if (moduleManager != null)
{
moduleManager.LoadModule(moduleName);
}
}
引发显示主视图的事件,并动画显示主视图,请参考源代码。