2013年12月19日星期四

Silverlight MVVM 贴近实战(二)

Silverlight MVVM 贴近实战(二)

时间:2012-05-30 18:14来源:51cto.com 作者:BruceAndLee 点击:2453次
上篇我们看了登陆界面,本篇我对服务端进行一个剖析。以及主界面的设计。我们上次忘记解释一个Silverlight的项目如何寄宿在MVC项目上。我们先来看看如何寄宿。看下图 我们看到了MISInfoManage这个项目是一个Silverlight项目。它寄宿在MISInfomanage.Web.Host这个MVC3项目中。流程是这样的,Silverlight寄宿在MISInfomanage
  
  上篇我们看了登陆界面,本篇我对服务端进行一个剖析。以及主界面的设计。我们上次忘记解释一个Silverlight的项目如何寄宿在MVC项目上。我们先来看看如何寄宿。看下图
  我们看到了MISInfoManage这个项目是一个Silverlight项目。它寄宿在MISInfomanage.Web.Host这个MVC3项目中。流程是这样的,Silverlight寄宿在MISInfomanage.Web.Host上,MISInfomanage.Web.Host这个项目它的启动路径如下图所示,是Home/Index,完整路径就是Http://localhost:port/Home/Index。
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.Mvc;  
 
namespace MISInfoManage.Web.Controllers  
{  
    public class HomeController : Controller  
    {  
        //  
        // GET: /Index/  
        public ActionResult Index()  
        {  
            return View("~/Views/Index.cshtml");  
        }  
    }  
}  
  当项目运行起来后,直接将页面导航至Index.chtml。所以到这里大家应该能想来Silverlight就寄宿在这个界面上。我们看看代码是如何寄宿的。
@{  
    Layout = null;  
}  
<!DOCTYPE html
<html
<head
    <title>Index1</title
    <style type="text/css"
    html, body  
    {  
        height: 100%;  
        overflow: auto;  
    }  
 
    body  
    {  
        padding: 0;  
        margin: 0;  
    }  
 
    #silverlightControlHost  
    {  
        height: 100%;  
        text-align: center;  
    }  
</style
    <script type="text/javascript" src="../../Silverlight.js"></script
    <script type="text/javascript"
    function onSilverlightError(sender, args) {  
        var appSource = "";  
        if (sender != null && sender != 0) {  
            appSource = sender.getHost().Source;  
        }  
 
        var errorType = args.ErrorType;  
        var iErrorCode = args.ErrorCode;  
 
        if (errorType == "ImageError" || errorType == "MediaError") {  
            return;  
        }  
 
        var errMsg = "Silverlight 应用程序中未处理的错误 " + appSource + "\n";  
 
        errMsg += "代码: " + iErrorCode + "    \n";  
        errMsg += "类别: " + errorType + "       \n";  
        errMsg += "消息: " + args.ErrorMessage + "     \n";  
 
        if (errorType == "ParserError") {  
            errMsg += "文件: " + args.xamlFile + "     \n";  
            errMsg += "行: " + args.lineNumber + "     \n";  
            errMsg += "位置: " + args.charPosition + "     \n";  
        }  
        else if (errorType == "RuntimeError") {  
            if (args.lineNumber != 0) {  
                errMsg += "行: " + args.lineNumber + "     \n";  
                errMsg += "位置: " + args.charPosition + "     \n";  
            }  
            errMsg += "方法名称: " + args.methodName + "     \n";  
        }  
 
        引发新错误(errMsg);  
    }  
    </script
</head
<body
    <form id="form1" runat="server" style="height: 100%"
    <div id="silverlightControlHost"
        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" 
            width=
"100%" height="100%"
            <param name="source" value="/ClientBin/MISInfoManage.xap" /
            <param name="onError" value="onSilverlightError" /
            <param name="background" value="white" /
            <param name="minRuntimeVersion" value="4.0.50826.0" /
            <param name="autoUpgrade" value="true" /
            <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration: none"
                <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="获取 Microsoft Silverlight" 
                    style=
"border-style: none" /
            </a
        </object
          
    </div
    </form
</body
</html
) 关键就在于Silverlight.js的引用,以及object中param的设置。在这里需要注意的是这个source属性,它的value值是指xap包所在的位置,如下所示,位于ClientBin下。 光凭这些代码还不能完成寄宿,我们还需要在MISInf
   )
  关键就在于Silverlight.js的引用,以及object中param的设置。在这里需要注意的是这个source属性,它的value值是指xap包所在的位置,如下所示,位于ClientBin下。
  光凭这些代码还不能完成寄宿,我们还需要在MISInfomanage.Web.Host上点击右键,选择Silverlight Applications。添加要寄宿的Silverlight项目,如下
  OK,这样就完成了一个Silverlight项目的寄宿。谈完这个,我们再回到Server端机构的解析,我就以一个登陆流程来解释。首先用户在登录界面点击登陆按钮,调用Login/GetUser/{id}。
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using System.Web.Mvc;  
 
namespace MISInfoManage.Web.Controllers  
{  
    using Server.Business.Interface;  
    using Server.Domain;  
    using Server.Model.edmx;  
    public class LoginController : Controller  
    {  
        //  
        // GET: /Login/  
        IUserBiz userBiz;  
        public LoginController(IUserBiz userBiz)  
        {  
            this.userBiz = userBiz;  
        }  
 
        public JsonResult GetUser(string id)  
        {  
            Users user = userBiz.GetUser(id);  
            return Json(user, JsonRequestBehavior.AllowGet);  
        }  
    }  
}  
  这个时候,程序要调用UserBiz,UserBiz要调用UserService,UserService要调用UserRepository,而UserRepository又继承BaseRepository,在每一层调用都有IOC存在。IOC怎么工作呢,先看看Global.asax.cs。
protected void Application_Start()  
        {  
            AreaRegistration.RegisterAllAreas();  
            IoCHelper.InitializeWith(new DependencyResolverFactory());  
            ControllerBuilder.Current.SetControllerFactory(new ResolverControllerFactory().GetControllerFactory());  
            RegisterRoutes(RouteTable.Routes);  
        } 
  Controller的创建已经交由DependencyResolverFactory这个依赖注入工厂接手,所以我们看到Controller中的IUserBiz而不是UserBiz。那么这个注入在哪里配置着呢?在Host项目下,如下图所示
  

  就是这个Unity.Login.Config。因为目前我只做了个登陆功能,所以在这里只有一个Config文件。那么Unity是怎么读取这个配置文件的呢?我们看看Server端IOC项目下有个UnityContainerBuilder类,他是负责创建依赖注入容器的。在它里面有这么一段代码
private IEnumerable<string> GetConfigFilePaths()  
        {  
            string path = AppSettingsHelper.GetString("UnityConfigPath""Config/Unity");// 默认值为"Config/Unity"  
            return Directory.GetFiles(PathHelper.LocateServerPath(path))  
                .Where(fullName => Path.GetExtension(fullName).Equals(".config", StringComparison.CurrentCultureIgnoreCase));  
        } 
  就是这个方法来抓取所有的注入配置文件。我们来看看注入配置文件。
<?xml version="1.0"?
<configuration
  <configSections
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/
  </configSections
  <unity
    <typeAliases
      <typeAlias alias="IUserRepository" type="Server.Repository.Interface.IUserRepository, Repository" /
      <typeAlias alias="UserRepository" type="Server.Repository.Implement.UserRepository, Repository" /
      <typeAlias alias="IUserService" type="Server.Application.Interface.IUserService,Application" /
      <typeAlias alias="UserService" type="Server.Application.Implement.UserService, Application" /
      <typeAlias alias="IUserBiz" type="Server.Business.Interface.IUserBiz,Business" /
      <typeAlias alias="UserBiz" type="Server.Business.Implement.UserBiz, Business" /
 
      <typeAlias alias="IDatabaseFactory" type="Server.Domain.IDatabaseFactory, Domain" /
      <typeAlias alias="DatabaseFactory" type="Server.Domain.DatabaseFactory,Domain" /
      <!--控制器 开始--> 
      <typeAlias alias="IControllerFactory" type="System.Web.Mvc.IControllerFactory, System.Web.Mvc,Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /
      <typeAlias alias="UnityControllerFactory" type="Server.IOC.IocResolver.UnityControllerFactory,IOC" /
      <typeAlias alias="LoginController" type="MISInfoManage.Web.Controllers.LoginController, MISInfoManage.Web.Host" /
      <!--控制器 结束--> 
    </typeAliases
 
    <containers
      <container name="ContainerEF"
        <!--登录--> 
        <register type="IDatabaseFactory" mapTo="DatabaseFactory"
          <constructor
            <param name="connectionName"
              <value value="MISInfoEntities"></value
            </param
          </constructor
        </register
 
        <register type="IUserRepository" mapTo="UserRepository"
          <constructor
            <param name="dataBaseFactory"
              <dependency type="DatabaseFactory"></dependency
            </param
          </constructor
        </register
 
        <register type="IUserService" mapTo="UserService"
          <constructor
            <param name="dataContext"
              <dependency type="DatabaseFactory"></dependency
            </param
            <param name="userRepository"
              <dependency type="UserRepository"></dependency
            </param
          </constructor
        </register
 
        <register type="IUserBiz" mapTo="UserBiz"
          <constructor
            <param name="userService"
              <dependency type="UserService"></dependency
            </param
          </constructor
        </register
 
        <register type="IControllerFactory" mapTo="UnityControllerFactory"
        </register
 
        <register type="LoginController" mapTo="LoginController"
          <constructor
            <param name="userBiz"
              <dependency type="UserBiz"></dependency
            </param
          </constructor
        </register
      </container
    </containers
  </unity
</configuration
<typeAlias alias="IUserService" type="Server.Application.Interface.IUserService,Application" /> 
  这个配置是一个声明别名的配置,type制定其所在的完整命名空间,所在程序集。类似于这样的配置都一样。我们重点看ContanerEF中的配置。首先是IDatabaseFactory的配置。DataBaseFactory的代码如下
public class DatabaseFactory : IDisposable, IDatabaseFactory  
    {  
        DbContext dbContext;  
        string connectionName;  
        public DatabaseFactory(string connectionName)  
        {  
            this.connectionName = connectionName;  
        }  
        public DbContext Get()  
        {  
            dbContext = new DbContext(connectionName);  
            return dbContext;  
        }  
        public void Dispose()  
        {  
            if (dbContext != null)  
            {  
                dbContext.Dispose();  
            }  
        }  
    } 
) 在UserRepository中我们需要给BaseRepository传递一个databaseFactory,用来创建对应的DBContext。如下所示 public class UserRepository:BaseRepositoryUsers,IUserRepository { public UserRepository(IDatabas
   )
  在UserRepository中我们需要给BaseRepository传递一个databaseFactory,用来创建对应的DBContext。如下所示
public class UserRepository : BaseRepository<Users>, IUserRepository  
    {  
        public UserRepository(IDatabaseFactory dataBaseFactory)  
            : base(dataBaseFactory)  
        { }  
    } 
  在这里,因为我们Login界面所用到的DbContext的链接字符串如下
<connectionStrings
    <add name="ApplicationServices" 
         connectionString=
"data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" 
         providerName=
"System.Data.SqlClient" /
    <add name="MISInfoEntities" connectionString="metadata=res://*/edmx.MISInfo.csdl|res://*/edmx.MISInfo.ssdl|res://*/edmx.MISInfo.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=ZZCYIC6VXOK5UXO\MSSQLSERVER08;initial catalog=MISInfo;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" /
  </connectionStrings
  所以注入的时候,我们把DatabaseFactory构造函数中的connectionName注入为MISInfoEntities。对于其他的注入参数,都是采用构造函数注入。相信大家看看就清楚了。在最后我们看看BaseRepository的构造函数。
public BaseRepository(IDatabaseFactory dataBaseFactory)  
        {  
            context = dataBaseFactory.Get() as DbContext;  
            context.Configuration.LazyLoadingEnabled = true;  
        } 
  由此我们可以看出,如果你的Databasefactory的connectionstring是MISInfoEntities的话,那么BaseRepository接收到的将会是MISInfoEntities(继承DbContext)。如果是PersonEntities,那么Databasefactory的Get方法获取的是PersonEntities(继承DbContext)。这样就可以根据配置文件很容易的更换数据源,或者有时候叫工作单元,从而实现低耦合和控制反转。好了,最后我们一起看看我设计的主界面。
  

  ok,最后我们看看设计代码。
<UserControl 
    x:Class=
"MISInfoManage.MainPage" 
    xmlns=
"http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x=
"http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:navigation=
"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"   
    xmlns:uriMapper=
"clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation" 
    xmlns:d=
"http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
    mc:Ignorable=
"d" 
    xmlns:source=
"clr-namespace:MISInfoManage.Resources"
    <UserControl.Resources
        <source:MainPageResource x:Key="Resource"></source:MainPageResource
    </UserControl.Resources
    <Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}" 
        <Grid.Background
            <ImageBrush ImageSource="/MISInfoManage;component/Images/windows.jpg" Stretch="UniformToFill"></ImageBrush
        </Grid.Background
        <Grid.RowDefinitions
            <RowDefinition Height="*"></RowDefinition
            <RowDefinition Height="Auto"></RowDefinition
        </Grid.RowDefinitions
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Row="0"
            <StackPanel Orientation="Vertical" HorizontalAlignment="Left"
                <Border CornerRadius="5"  BorderBrush="Aqua" BorderThickness="0" MouseMove="Border_MouseMove" MouseLeave="Border_MouseLeave"
                    <StackPanel Orientation="Vertical" x:Name="StackPanel1" Margin="10,10,0,10"
                        <Image Source="/MISInfoManage;component/Images/Navigation/UserManage.png" Width="60"/
                        <TextBlock x:Name="Tb_UserManage" Text="{Binding Tb_UserManage,Source={StaticResource Resource}}" Style="{StaticResource MainPageTextStyle}"/
                    </StackPanel
                </Border
                <Border CornerRadius="5"  BorderBrush="Aqua" BorderThickness="0" MouseMove="Border_MouseMove" MouseLeave="Border_MouseLeave"
                    <StackPanel Orientation="Vertical" Margin="10,10,0,10"
                        <Image Source="/MISInfoManage;component/Images/Navigation/ArchiveInfo.png" Width="60"/
                        <TextBlock x:Name="Tb_ArchiveManage" Text="{Binding Tb_ArchiveManage,Source={StaticResource Resource}}" Style="{StaticResource MainPageTextStyle}"/
                    </StackPanel
                </Border
                <Border CornerRadius="5"  BorderBrush="Aqua" BorderThickness="0" MouseMove="Border_MouseMove" MouseLeave="Border_MouseLeave"
                    <StackPanel Orientation="Vertical" Margin="10,10,0,0"
                        <Image Source="/MISInfoManage;component/Images/Navigation/SystemManage.png" Width="60"/
                        <TextBlock x:Name="Tb_SystemManage" Text="{Binding Tb_SystemManage,Source={StaticResource Resource}}" Style="{StaticResource MainPageTextStyle}"/
                    </StackPanel
                </Border
            </StackPanel
        </ScrollViewer
        <Button x:Name="BtnLoginInfo" BorderBrush="AliceBlue" Grid.Row="1" Height="30" BorderThickness="1" Background="AliceBlue" FontSize="16"></Button
    </Grid
</UserControl
  后台cs代码如下
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.Navigation;  
using System.Windows.Shapes;  
using MISInfoManage.Resources;  
 
namespace MISInfoManage  
{  
    public partial class MainPage : UserControl  
    {  
        public MainPage()  
        {  
            InitializeComponent();  
        }  
 
        public MainPage(string userName):this()  
        {  
            this.BtnLoginInfo.Content = string.Format(MainPageResource.Btn_LoginInfo,userName);  
        }  
 
        private void Border_MouseMove(object sender, MouseEventArgs e)  
        {  
            Border border = sender as Border;  
            border.Background = new SolidColorBrush(Colors.White);  
            border.Background.Opacity = 0.5;  
            border.BorderThickness = new Thickness(1);  
        }  
 
        private void Border_MouseLeave(object sender, MouseEventArgs e)  
        {  
            Border border = sender as Border;  
            border.Background = new SolidColorBrush();  
            border.BorderThickness = new Thickness(0);  
        }  
    }  
  好了,本节到此结束,下一节重点进入增删改查,数据验证,双向,单向绑定。
  本文来自BruceAndLee的博客,原文地址:http://leelei.blog.51cto.com/856755/883133
http://silverlightchina.net/html/tips/2012/0530/16352.html