SilverLight之路(三)

我们先以“风险测试”这一模块入手吧,这个模块功能相对比较简单,业务逻辑很清楚,做题,得分,出结果

效果如下

 

结果页

 

看似简单,但这里牵涉到的知识点可不少,比如ItemControls控件的使用,Chart控件的使用,程序集的动态加载,资源的应用程序级(是程序级别,不是程序集)共享等,可能在一篇里说不完,不怕,那就一步一步来吧。

在开始这一模块之前,先来整理一下我思路。我们的“项目”计划是使用全站SL,这样的话,如果全部功能都在一个项目中,那最终可能会使我们的XAP灰常巨大,如果访问一个页面是要等上几分钟的话,你认为我们的用户会有那么多的耐心吗?好的做法应该是各模块分开,按需加载。

既然如此,不用多想,再在我们的解决方案中新建一个项目,如下

 

在接下来的对话框中会询问在哪个网站上承载新建的sl应用程序

 

我们选择默认,并去掉生成测试页的选项,这样VS2010自动找到了我们解决方案中的网站项目,并添加了进去。按F6重新编译后可以看到,现在我们的项目结构如下

 

接下来我们要做的,就是把我们这几个项目连通,界面流程为:启动,显示登录界面,加载主界面,显示风险测试模块。

现在回到我们的主SL应用程序,也就是TFT-WebFirst-SL中,现在我们有了login页与mainpage页,之前为了显示login效果,我们在mainpage页里简单的把login页里show了出来,但现在需要改改了,很明显,我们现在缺少一个“容器”页,即用来在login与mainpage之间切换的父容器,我们新建一个Index页。(这可以在vs中操作也可以在blend中操作,但他们之间往往不同即时同步,比如我们新加了一个页面,如果马上打开blend就会看不到,如果在blend中新加了一个元素或命名了一个元素,同样无法在vs中马上反映出来,这时只要用vs编译一下即可)

相关代码类似如下

App.xaml.cs

private void Application_Startup(object sender, StartupEventArgs e)

{

//this.RootVisual = new MainPage();

this.RootVisual = new Index();

}

Index.xaml.cs

 

void Index_Loaded(object sender, RoutedEventArgs e)
{
Login l
= new Login();
l.ParentPage
= this;
this.Content = l;
}

public void GoToMainPage()
{
this.Content = new MainPage();
}

 

Login.xaml.cs

public Index ParentPage { get; set; }



void btnLogin_Click(object sender, RoutedEventArgs e)

{

ParentPage.GoToMainPage();

}

注:该项目主要以学习sl为目的,因此没有使用任何开发模式,代码实现上以也以简单实现功能为主

接下来,我们动态加载风险测试模块

主要代码如下

 

/// <summary>

/// 从XAP包中返回程序集信息(该方法来源网上)

/// </summary>

/// <param name="packageStream"></param>

/// <param name="assemblyName"></param>

/// <returns></returns>

public Assembly GetAssemblyFromXap(Stream packageStream, String assemblyName)

{

Stream stream
= Application.GetResourceStream(new StreamResourceInfo(packageStream, null), new Uri("AppManifest.xaml", UriKind.Relative)).Stream;

Assembly asm
= null;

XmlReader xmlReader
= XmlReader.Create(stream);

xmlReader.MoveToContent();

if (xmlReader.ReadToFollowing("Deployment.Parts"))

{

string str = xmlReader.ReadInnerXml();

Regex reg
= new Regex("x:Name=\"(.+?)\"");

Match match
= reg.Match(str);

string sName = "";

if (match.Groups.Count == 2)

{

sName
= match.Groups[1].Value;

}

reg
= new Regex("Source=\"(.+?)\"");

match
= reg.Match(str);

string sSource = "";

if (match.Groups.Count == 2)

{

sSource
= match.Groups[1].Value;

}

AssemblyPart assemblyPart
= new AssemblyPart();

StreamResourceInfo streamInfo
= Application.GetResourceStream(new StreamResourceInfo(packageStream, "application/binary"), new Uri(sSource, UriKind.Relative));

if (sSource == assemblyName)

{

asm
= assemblyPart.Load(streamInfo.Stream);

}

}

return asm;

}

}

void LoadRiskTest()

{
//加载风险测试模块

WebClient rtwc
= new WebClient();

rtwc.OpenReadCompleted
+= new OpenReadCompletedEventHandler(

(s, ex)
=>

{

Assembly assembly
= da.GetAssemblyFromXap(ex.Result, "RiskTest.dll");

UIElement element
= assembly.CreateInstance("RiskTest.MainPage") as UIElement;

this.BorderMainPanel.Child = element; //这里使用Border做为容器,是因为动态加载控件时,使用Canvas不能自适应大小

});

Uri xapUri
= new Uri(HtmlPage.Document.DocumentUri, "ClientBin/RiskTest.xap");

rtwc.OpenReadAsync(xapUri);

}

 

具体思路是通过WebClient到网站的ClientBin目录中下载RiskTest.xap包,然后通过读取其中的清单文件(AppManifest.xaml),加载该模块的主程序集(RiskTest.dll),然后通过反射创建其中的MainPage页面并加载到主应用程序中的指定位置。这个实现在网上资源好多,详细过程可以去搜索一下。

 

这样,我们的流程就算是完成了,现在运行应该就可以看到动态加载后的效果了,只不过现在我们的风险测试模块没有内容

 

现在我们来看看风险测试模块的界面布局,我们注意到在每个模块下都有一个左边的导航功能,如

 

但这里面又分上下两部分,显然,下半部分为各模块内部功能的导航,上半部分在每个模块中是一样的,因此,上半部分不应放到模块中,应该在主程序中实现。既然如此,那我们就需要在各模块的这一部分保留一个位置,在主程序加载各模块后再把主程序中的“用户信息”部分加进去,简单实现为在各模块的主界面中提供一个方法,并在主程序集中调用,实现代码如

模块mainpage.xaml.cs

 

public void FillUserInfoCtr(UserControl uc)

{

this.BorderUserInfo.Child = uc;

}

 

主程序调用时

 

void LoadRiskTest()

{
//加载风险测试模块

WebClient rtwc
= new WebClient();

rtwc.OpenReadCompleted
+= new OpenReadCompletedEventHandler(

(s, ex)
=>

{

Assembly assembly
= da.GetAssemblyFromXap(ex.Result, "RiskTest.dll");

UIElement element
= assembly.CreateInstance("RiskTest.MainPage") as UIElement;

this.BorderMainPanel.Child = element;

LoadUserInfo(element);

});

Uri xapUri
= new Uri(HtmlPage.Document.DocumentUri, "ClientBin/RiskTest.xap");

rtwc.OpenReadAsync(xapUri);

}



void LoadUserInfo(UIElement element)

{
//为各模块加载用户信息

UserInfo ui
= new UserInfo();

Type t
= element.GetType();

MethodInfo mi
= t.GetMethod("FillUserInfoCtr");

mi.Invoke(element,
new object[] { ui });

}

 

风险测试模块的主界面布局类似

 

  

BorderUserInfo就是用来加载主程序中的用户信息部分的容器

SVMain是该模块中各功能页面的展示区

posted @ 2011-06-10 13:09  第八颗流星  阅读(669)  评论(0编辑  收藏  举报