[Silverlight]用Prism打造自己的程序开发架构

    大家都知道Prism自带的StockTraderRI的例子,这个例子给我们展示了由不同的功能模块(包括里面的页面)组合成主界面(shell)的方法;我当初看到这个例子总觉得怪怪的,原因是我的项目经验中基本没有出现过这样的需求,而出现最多的是“在主界面中显示(调用)不同的模块中的页面”:例如我在主界面有个菜单,如图:

1、我想在点击某个菜单项的时候将某个模块中的某个页面显示在主界面菜单下面的的内容区,而且要有显示的动画效果,如图:

这个动画效果不太好截图,其实是通过向左滑动来切换页面,大家下载代码看看就明白了,
2、各个模块都要按需加载,模块加载过程中要有进度显示,如图:

3、全部采用MVVM设计。
我想可能也有其他人有过我这样的困惑,所以为了让大家少走弯路,少造轮子,就有了这篇随笔,让我们不再困惑于StockTraderRI的那个例子,实现更适合自己项目的程序开发框架。

我的项目结构如图:

PrismDemoMain是主Silverlight程序,PrismDemoMain.Web用来宿主主程序;
PrimDemoCommon是Silverlight类库,提供给主程序和各个模块项目用来引用(提供公共对象)。
Modules解决方案文件夹下面的两个项目ModuleA和ModuleB是分别对应两个模块ModuleA和ModuleB,例如ModuleA的对象代码如下:

using System;
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;
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文件,很简单。

<Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                          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中就一行代码:

<ContentControl regions:RegionManager.RegionName="MainPageRegion"  HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />

这样做的好处是,假如我对这个主界面不满意,我可以方便的用其他的MainPageModule来替换这个,甚至可以做成多个主界面供用户选择。
载入模块,显示页面,动画效果等都是在这个模块完成的,例如点击菜单显示切换视图的代码如下:

 private void ShowCurrentMainView()
        {
            
//取得当前主视图的实例
            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 == nullreturn;
            ModuleInfo moduleInfo 
= moduleCatalog.Modules.FirstOrDefault(p => p.ModuleName == moduleName);
            
if (moduleInfo == nullreturn;
            
//判断模块是否已经初始化
            if (moduleInfo.State == ModuleState.Initialized)
            {
                
//显示当前主视图
                ShowCurrentMainView();
            }
            
else if (moduleManager != null)
            {
                moduleManager.LoadModule(moduleName);
            }
        }

引发显示主视图的事件,并动画显示主视图,请参考源代码。

posted @ 2011-07-25 17:54 小庄 阅读(2078) 评论(14) 编辑 收藏

 回复 引用 查看   
#1楼[楼主] 2011-07-25 18:00 小庄      
 回复 引用 查看   
#2楼 2011-07-25 20:48 tan_Cool      
支持做成项目模板
 回复 引用 查看   
#3楼 2011-08-01 17:25 @海滨@      
楼主,早请教你就好了。这个问题困扰我1天多的时间,后来终于发现是modulelogin模块引用了其他模块。
不过还是感谢您的回复,你这篇文章很好、很实用,我现在也在用Prism4+MEF+MVVM+Silverlight写框架。。。

 回复 引用 查看   
#4楼 2011-08-16 17:39 往事如锋      
非常好
 回复 引用 查看   
#5楼[楼主] 2011-08-17 09:54 小庄      
@往事如锋
呵呵,终于又一个识货的,欢迎提出宝贵意见。

 回复 引用 查看   
#6楼 2011-08-18 16:40 cggffc      
你好,为什么我下了你的代码运行老是报错啊,谢谢
 回复 引用 查看   
#7楼[楼主] 2011-08-19 09:32 小庄      
@cggffc
不是吧?哪里报错啊?有截图么?

 回复 引用 查看   
#8楼 2011-08-19 09:44 cggffc      
谢谢你的回复,也不是报错,就是点一个菜单时,并没有加载那个模块
 回复 引用 查看   
#9楼[楼主] 2011-08-19 09:57 小庄      
@cggffc
是不是已经加载了你点的那个菜单,不能重复加载的。

 回复 引用 查看   
#10楼 2011-08-19 10:11 cggffc      
哦,我又试了一下,是这样的:我在ie9中报错,在firefox中没有问题,谢谢,我给你发了qq号 希望能联系,ie9的问题,我再查查
 回复 引用 查看   
#11楼 2011-09-28 11:38 谢灵峰      
我又自己加了一个ModuleC做测试,代码都一样跟着写的的,可是点击后,loadModule方法报错了,异常是:找到多个与约束“((exportDefinition.ContractName == "Microsoft.Practices.Prism.Regions.RegionAdapterMappings") AndAlso (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") AndAlso "Microsoft.Practices.Prism.Regions.RegionAdapterMappings".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))”匹配的导出

楼主帮我看看是怎么回事啊

 回复 引用 查看   
#12楼[楼主] 2011-09-28 17:54 小庄      
你注意检查一下是否有中括号中的import参数和其他模块的重复了。
 回复 引用 查看   
#13楼[楼主] 2011-09-28 17:56 小庄      
@谢灵峰
你注意检查一下是否有中括号中的import参数和其他模块的重复了。

 回复 引用 查看   
#14楼 2012-01-18 01:18 ca47      
请问您的代码能在VS中运行吗?
我运行时出现了奇怪的情况,

不知道楼主有没有出现这种情况,是如何解决的呢?目前比较纠结。