Prism学习之三 Silverlight 从Hello World开始



Prism学习笔记(一) 从Hello World开始

分类 Prism 941人阅读 评论(1) 收藏 举报
 

就像以往的入门,从伟大的Hello World开始吧。其实网上已经有不少Prism的入门图例,但是就只是很简单的说了一下操作过程,为什么要这么写代码和背后原理写的很少,看了以后有点知其然而不知其所以然的感觉。

不要小看Hello World这个简单的程序,他其实已经包含了不少重要的概念,比如容器,依赖注入,为什么要Bootstrapper这个类,Shell是干什么用的,我将会在我的笔记里面一条条的详细介绍他们。

先新建一个Silverlight Application,命名为MyPrism。

 建好后可以看见一个App.xaml和MainPage.xaml文件,App.xaml的作用就相当与.Net web程序中的Global.asax文件,其中包括了Application_Startup方法.

private void Application_Startup(object sender,StartupEventArgs e)
{
       this.RootVisual = new MainPage();
}

这个方法实现的是整个程序的入口,Application.RootVisual 的属性获取或设置主要应用程序用户界面,只能从代码设置RootVisual 属性的值一次,尽管可以通过任意次数获取其值。这里是把MainPage.xaml作为了初始页面

我们要修改这部分用我们的自己Bootstrapper来替换他

private void Application_Startup(object sender,StartupEventArgs e)
 {
      Bootstrapper bootstrapper = new Bootstrapper();
      bootstrapper.Run();

}

程序会提示找不到Bootstrapper类,我们后面会创建他。

首先要添加Prism的assemblies:

  • Microsoft.Practices.Prism.dll
  • Microsoft.Practices.Prism.UnityExtensions.dll
  • Microsoft.Practices.ServiceLocation.dll
  • Microsoft.Practices.Unity.Silverlight.dll

记住在安装完Prism以后,进入Prism4的文件夹,双击执行RegisterPrismBinaries.bat文件,就能把所有的Prism assemblies添加入本地类库,你就能在add reference的.Net tab下找到他们了。具体每个作用,我这里就偷懒贴一端官方原文吧

Microsoft.Practices.Prism.dll. This assembly contains the implementation of the Prism Library core components,such as modularity,logging services,communication services,and deFinitions for several core interfaces. It also contains the implementation of the Prism Library components that target Silverlight applications,including commands,regions,and events.

Microsoft.Practices.Prism.UnityExtensions.dll. This assembly contains base and utility classes you can reuse in applications built with the Prism Library that consume theUnity Application Block. For example,it contains a bootstrapper base class,theUnityBootstrapperclass,that creates and configures a Unity container with default Prism Library services when the application starts.

Microsoft.Practices.Unity.Silverlight.dll. This assembly enables you to use the Unity Application Block in your application. By default,applications built using Prism use the Unity Application Block. However,developers who prefer to use different container implementations can build adapters for them using the provided extensibility points in the Prism Library.

Microsoft.Practices.ServiceLocation.dll. This assembly contains the Common Service Locator interface used by Prism to provide an abstraction over Inversion of Control (IoC) containers and service locators; therefore,you can change the container implementation with ease.

在创建Bootstrapper之前,先重命名MainPage.xaml为Shell.xaml,方法如下:

Shell.xaml文件的作用:

Prism的理念是用Shell页面做主显示页面,将其划分成不同的区域 Region,然后把其他写好的module嵌入Shell页面中对应的Region,最终展现给用户

修改Shell.xaml文件内容为 

<UserControl x:Class="MyPrism.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Regions="http://www.codeplex.com/prism"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <ItemsControl Name="MainRegion" Regions:RegionManager.RegionName="MainRegion"/>

</UserControl>

 

关键就是这句 <ItemsControlName="MainRegion" Regions:RegionManager.RegionName="MainRegion"/>表示Shell中包含着一个名为“MainRegion”的Region。

xmlns:Regions="http://www.codeplex.com/prism"
用来引用Prism assembly.

接下来我们创建展示页面内容的module,add new project,选择Silverlight Class Library,命名为HelloWorldModule.

 新建一个Views文件夹,在这个歌文件夹下添加一个Silverlight User Control文件HelloWorldView.xaml

HelloWorldView.xaml包含我们最终要显示内容,在其中添加一个TextBlock控件,显示Hello World内容

<UserControl x:Class="HelloWorldModule.Views.HelloWorldView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Text="Hello World" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>
    </Grid>
</UserControl>


接下来重命名Class.cs为HelloWorldModule.cs

还记得我们在前面建立的Shell.xaml文件么?HelloWorldModule.cs将会把HelloWorldView.xaml绑定到Shell中的MainRegion。下面是HelloWorldModule.cs代码,由于这里引用了Microsoft.Practices.Prism.Modularity和Microsoft.Practices.Prism.Regions,所以要在reference中添加Microsoft.Practices.Prism。

using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;

namespace HelloWorldModule
{
    public class HelloWorldModule: IModule
    {
        private readonly IRegionManager regionManager;

        public HelloWorldModule(IRegionManager regionManager)
        {
            this.regionManager = regionManager;
        }

        public void Initialize()
        {
            regionManager.RegisterViewWithRegion("MainRegion",typeof(Views.HelloWorldView));
        }
    }
}

这里使用了依赖注入的概念,依赖注入又被称为反转注入IOC,“使得对象只依赖IOC容器,不再相互依赖,而是在对象创建时,由IOC容器将其依赖的对象注入Inject其体内,故又称依赖注入依赖注射模式”,笔者第一次看到这段定义的时候也是莫名其妙,后来才发现其实依赖注入简单的很,网上文章很多,这里有一个链接http://www.cnblogs.com/xingyukun/archive/2007/10/20/931331.html 讲的还不错,有兴趣可以看看。

HelloWorldModule类继承了IModule interface,只有这样才可以被添加入容器的Module Collection中。

在HelloWorldModule类中,定义类的构造函数时,注入了IRegionManager regionManager这个对象,也就是说HelloWorldModule不会创建自己的IRegionManager对象,他将接受容器给他的IRegionManager对象。我会在最后的介绍BootStrapper中再提到这点。

记得在Shell.xaml中我们添加<ItemsControlName="MainRegion" Regions:RegionManager.RegionName="MainRegion"/>控件么Initialize()将会把HelloWorldView注册到regionManager中的MainRegion。

这是怎么实现的呢,我们来进入最后一项工作创建Bootstrapper类了。回到MyPrism中新建一个Bootstrapper.cs文件,将其代码修改如下:

using System;
using System.Windows;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Unity;

namespace MyPrism
{
    public class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return Container.Resolve<Shell>();
        }

        protected override void InitializeShell()
        {
            base.InitializeShell();
            Application.Current.RootVisual = (UIElement)this.Shell;
        }

        protected override void ConfigureModuleCatalog()
        {
            base.ConfigureModuleCatalog();

            ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
            moduleCatalog.AddModule(typeof(HelloWorldModule.HelloWorldModule));
        }

    }
}

Bootstrapper类将我们之前创建的Shell,HelloWorldModule整合在一起。

这里有三个方法,按执行次序来排是ConfigureModuleCatalog,CreateShell 和InitializeShell:

首先ConfigureModuleCatalog()中将HelloWorldModule添加入moduleCatalog,这个moduleCatalog将会管理所有的modules.

接着CreateShell() 产生了一个Shell instance,将作为我们的主页面

最后InitializeShell()将这个Shell instance赋值给 Application.Current.RootVisual,最终在页面展示出来。(在本文的最开始我们修改了App.xaml.cs文件,将 Application.Current.RootVisual赋值工作移到这里来完成)。

初学者可能不太能理解Container.Resolve<Shell>();的用法,这里有篇文章写的很清楚了,可以看看 http://www.cnblogs.com/inrie/archive/2008/04/18/1159002.html

其实Container.Resolve<Shell>();就是返回一个Shell的instance,只不过是用Container来控制,一般来说在使用Resolve获得instance之前先要使用Container.RegisterType来注册类,如果没注册,就单纯的返回一个实例。所有在这里你也可以直接用new:

        protected override DependencyObject CreateShell()
        {
            return new Shell();
        }

我们这个Shell比较简单,单纯的就一个控件,使用Container好处在于,但如果遇到比较复杂的Shell,其引用依赖于其他类,Resolve可以自动帮你把其他类都创建好,返回给你一个实例,用户不用管后台那些类的关系,谁要先建,谁引用谁,这些麻烦事情Container都帮你搞定。

说到这里我们也大功告成了。按一下F5键,将会得到下面的结果:

 

这是一个Prism的最基本的应用,里面包含其主要的思想,后面我们将讨论MVVM模式。

注:我的写作风格可能会令读者不太适应,大多数正规的教程一般都会先写原理框架,后写实际应用实例,实例配和原理。不过我不太喜欢这样,经常会都会刚开始的时候原理看的云里雾里,一知半解,很容易有疲惫感。等看到后面的实际应用时,又不知道要和前面哪段原理结合,效率很低。我觉得还是理论配合实践,把理论的东西分散到具体的应用中去,先实践,遇到障碍了再做原理分析。这样入手比较有成就感,不太容易被一堆概念吓倒。不知道效果如何。。。

代码http://files.cnblogs.com/mindflying/MyPrism.zip

Prism学习笔记(二)简单的MVVM模式


分类 Prism 1017人阅读 评论(0) 收藏 举报
 

现在我们进入Prism的一条重要支柱MVVM模式。MVVM模式简单来说就是把页面UI和后台逻辑分开,这样做的好处是能使你的程序更容易测试,维护和改进。下面的图来自于Prism4的教程显示了MVVM模式的基本工作原理:

好,废话少说,开始Coding吧。

打开之前创建的MyPrism程序:

其中HelloWorldModule下只有一个View页面HelloWorldView.xaml,其只实现一个简单显示HelloWorld控件作用,不包括任何逻辑代码

为了实现MVVM模式,我们需要添加一个viewmodel文件和Model文件

在Helloworldmodule下新建两个文件夹,命名为Models和viewmodels,分别添加一个cs文件,如下图:

接下来,修改HellowWorldView页面,实现一个简单的加法界面

 代码如下:

<UserControl x:Class="HelloWorldModule.Views.HelloWorldView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:ds="clr-namespace:HelloWorldModule.viewmodels"
    d:DesignHeight="429" d:DesignWidth="618" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <UserControl.DataContext>
        <ds:HelloWorldviewmodel/>
    </UserControl.DataContext>
    
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDeFinitions>
            <RowDeFinition Height="63"/>
            <RowDeFinition Height="40"/>
            <RowDeFinition Height="50"/>
        </Grid.RowDeFinitions>
        <Grid.ColumnDeFinitions>
            <ColumnDeFinition Width="150"/>
            <ColumnDeFinition Width="50"/>
            <ColumnDeFinition Width="150"/>
            <ColumnDeFinition Width="50"/>
            <ColumnDeFinition Width="150"/>  
        </Grid.ColumnDeFinitions>        
        
        <TextBlock Grid.ColumnSpan="5" Text="Hello World" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>
        <TextBox Grid.Row="1" Grid.Column="0"  Height="23"  Name="Number1" Text="{Binding HelloWorldModel.Number1,Mode=TwoWay}" Width="120"/>
        <TextBox Grid.Row="1"  Grid.Column="2" Height="23"  Name="Number2" Text="{Binding HelloWorldModel.Number2,Mode=TwoWay}" Width="120"/>
        <sdk:Label Grid.Row="1"  Grid.Column="4" Height="28" Name="Result" Content="{Binding HelloWorldModel.Result,Mode=TwoWay}" Width="120"/>
        <sdk:Label Grid.Row="1"  Grid.Column="1" Height="23" Name="label1" Content="+" Width="30"/>
        <sdk:Label Grid.Row="1"  Grid.Column="3" Height="22" Name="label2" Content="=" Width="30"/>
        <Button Content="Add" Grid.Row="2"  Grid.Column="4" Command="{Binding AddCommand}" Height="25"  Name="btnAdd" Width="75"/>

    </Grid>
</UserControl>




其中的关键代码

xmlns:ds="clr-namespace:HelloWorldModule.viewmodels"

<UserControl.DataContext>
     <ds:HelloWorldviewmodel/>
</UserControl.DataContext>


这里吧HellowWorldviewmodel类所为HellowWorldView的DataContext,也就是说View中的所有数据绑定都在HellowWorldviewmodel类中定义。

<TextBox Grid.Row="1" Grid.Column="0"  Height="23"  Name="Number1" Text="{Binding HelloWorldModel.Number1,Mode=TwoWay}" Width="120"/>
<TextBox Grid.Row="1"  Grid.Column="2" Height="23"  Name="Number2" Text="{Binding HelloWorldModel.Number2,Mode=TwoWay}" Width="120"/>
<sdk:Label Grid.Row="1"  Grid.Column="4" Height="28" Name="Result" Content="{Binding HelloWorldModel.Result,Mode=TwoWay}" Width="120"/>

这三个控件中,Text绑定了HelloWorldModel.Number1属性,并且是双向绑定,双向绑定既是,如果程序既可以将HelloWorldModel.Number1属性绑定到页面,也可以从页面得到数据。如将其改为单向绑定,程序就无法读取客户输入的数字了。

 

<Button Content="Add" Grid.Row="2"  Grid.Column="4" Command="{Binding AddCommand}" Height="25"  Name="btnAdd" Width="75"/>

此处将Button绑定到我们自定义的AddCommand中。

好接下来我们就要在HellowWorldviewmodel类中实现Number1,Number2,Result这个三个绑定参数和AddCommand命令。

下面是HellowWorldModel类代码

using System.ComponentModel;

namespace HelloWorldModule.Models
{
    public class HelloWorldModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public int Number1 { get; set; }

        public int Number2 { get; set; }

        private int result;

        public int Result
        {
            get
            {
                return this.result; 
            }

            set
            {
                if (value != this.result)
                {
                    this.result = value;
                    if (this.PropertyChanged != null)
                    {
                        this.PropertyChanged(this,new PropertyChangedEventArgs("Result"));
                    }
                }
            }
        }
         
    }
}


在HellowWorldModel类中我们定义了基本的参数属性Number1,Number2,Result。Number1,Number2较为简单。Result中包含了一个PropertyChanged的事件,该事件的作用简单来说就是,一旦Result值发生了变化,他将通知所有的绑定Result的控件,刷新数据。为了实现该事件,该类必须继承INotifyPropertyChanged。具体的原理这里就不多说了,网上多的是。

HellowWorldviewmodel类代码

using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using HelloWorldModule.Models;

namespace HelloWorldModule.viewmodels
{
    public class HelloWorldviewmodel
    {
        private readonly HelloWorldModel  helloWorldModel;

        public HelloWorldviewmodel()
        {
            this.helloWorldModel = new HelloWorldModel();           
            this.AddCommand = new DelegateCommand<object>(this.OnSubmit);
        }

        public HelloWorldModel HelloWorldModel
        {
            get { return this.helloWorldModel; }
        }

        public ICommand AddCommand { get; private set; }

        private void OnSubmit(object obj)
        {
            helloWorldModel.Result = helloWorldModel.Number1 + helloWorldModel.Number2;
        }

   
    }
}

HellowWorldviewmodel中创建了一个HellowWorldModel的实例,同时创建AddCommand方法,并实现其具体操作OnSubmit,这些都用来绑定到HellowWorldView中。

OnSubmit中将Number1和Number2的数据相加赋给了Result,由于属性Result实现了PropertyChanged事件,其值一发生变化,便刷新了Result控件值。

好所有的代码都完成了,按F5键运行,得到以下结果。

这篇文章主要是谈了MVVM模式的基本架构和功能实现。关键就在于数据绑定和PropertyChanged事件。

另外读者可能会有疑惑为什么要把HellowWorldModel类和HellowWorldviewmodel类分开写,其实完全可以把两者和为一个类。Prism官方给出的viewmodel和Model的区别是:

viewmodel:The view model in the MVVM pattern encapsulates the presentation logic and data for the view. viewmodel封装了展现的逻辑和数据。

Model:The model in the MVVM pattern encapsulates business logic and data. Model封装了业务的逻辑和数据。

笔者里的理解是分开是为了使代码更清晰,便于阅读。像页面操作如本例中的数据相加操作属于页面的逻辑,因此放在HellowWorldviewmodel中,如果有数据库读取或者和其他模块交互,那就属于业务逻辑,就应该放在HellowWorldModel中。

希望大家指正。

代码http://files.cnblogs.com/mindflying/MyPrism_BasicMVVM.zip

相关文章

如何在Silverlight4(XAML)中绑定IsEnabled属性?我试过简单的...
我正在编写我的第一个vb.net应用程序(但我也会在这里标记c#,...
ProcessFile()是在UIThread上运行还是在单独的线程上运行.如...
我从同行那里听说,对sharepoint的了解对职业生涯有益.我们不...
我正在尝试保存一个类我的类对象的集合.我收到一个错误说明:...
我需要根据Silverlight中的某些配置值设置给定控件的Style.我...