posts - 1180, comments - 162, trackbacks - 1, articles - 5
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

2011年6月10日

概要:

将所有的VM在加载到Application的Static Resource中,然后在View中用标签指定。

实现:

1)采用特性指定要添加到StaticResource中的对象

publicclassStaticResourceAttribute : Attribute
    {
        publicstringKey { get; set; }
  
        publicStaticResourceAttribute(stringkey)
        {
            this.Key = key;
        }
    }

2)从当前的程序集中,把所有标记了StaticResourceAttribute的VM加载到AppResource中

publicclassViewModelManager
    {
        privatestaticApplication app = Application.Current;
  
        publicstaticvoidInjectViewModelsToResources()
        {
            Assembly executingAssembly = Assembly.GetCallingAssembly();
            foreach(Type type inexecutingAssembly.GetTypes())
            {
                var attributes = type.GetCustomAttributes(false);
  
                foreach(var attribute inattributes)
                {
                    if(attribute isStaticResourceAttribute)
                    {
                        var obj = Activator.CreateInstance(type);
                        if(!app.Resources.Contains(type.Name))
                            app.Resources.Add(type.Name, obj);
                    }
                }
            }
        }
  
        publicstaticT GetViewModelFromResources<T>()
        {
            var key = typeof(T).Name;
            if(app.Resources.Contains(key))
                return(T)app.Resources[key];
            else
                returndefault(T);
        }
    }

在主窗体中调用:

publicpartialclassMainPage : UserControl
    {
        publicMainPage()
        {
            ViewModelManager.InjectViewModelsToResources();
              
            InitializeComponent();         
        }
    }

3)View写法

<UserControlx:Class="XXX .LoginView"
 <UserControl.DataContext>
        <BindingSource="{StaticResource LoginViewModel}"/>
    </UserControl.DataContext>
  
  
</UserControl>

结论:

这样处理后,实现了VM的"单例",多个View关联同一个VM时可以共享数据。

posted @ 2011-06-10 07:26 linFen 阅读(203) 评论(0) 编辑

一、背景:与用户界面 (UI) 相关的最大的问题就是大量的凌乱的代码,原因两个:

(1)  用户界面包含负责的逻辑用于维护界面相关对象;

(2)      其次也包含了应用程序状态的维护。

用户界面的3大问题:状态 (State) , 逻辑 (Logic) ,同步 (Synchronization),其中状态是用户界面最关心的问题之一。

二、简述MVC、MVP、MVVM

(1)     MVC:模型-视图-控制器(Model View Controller),它强制性的使应用程序的输入、处理和输出分开。

(2)    MVP:模型-视图-表现类(Model-View-Presenter)

(3)    MVVM:模型-视图-视图模型(Model-View-ViewModel)

三、比较

(1)    发展过程:MVC->MVP->MVVM

(2)    MVC->MVP

MVC中Model不是纯Model,因为它要有View的一些数据结构。

 

 

(3)  MVC、MVP->MVVM

 

View没有大量代码逻辑。结合WPF、Silverlight绑定机制,MVP演变出了MVVM,充分利用了WPF、Silverlight的优势,将大量代码逻辑、状态转到ViewModel,可以说MVVM是专门为WPF、Silverlight打造的。

 

(4)用户界面问题比较

MVC

MVP

MVVM

V

C

V

P

V

VM

状态

逻辑

同步

四、MVVM

(1) 组成部分Model、View、ViewModel

(a)  View:UI界面

(b) ViewModel:它是View的抽象,负责View与Model之间信息转换,将View的Command传送到Model;

(c)     Model:数据访问层

 

 (2)  View与ViewModule连接:

(a)     Binding Data:实现数据的传递

(b)      Command:实现操作的调用

(c)      AttachBehavior:实现控件加载过程中的操作

 

 

Binding和Command可以写在XAML中。

(3)  优势

(a)     ViewModule易于单元测试;

(b)     View没有MVC、MVP复杂的代码逻辑,让整个开发过程中的UI设计和后台的代码编写完全分开,设计者可以专注于使用Express Blend等去设计页面也就是View,而开发的可以完全通过Model来定义要操作的object,通过ViewModel来定义出来需要对这些model做哪些操作,最后使用Command来把Model和View完全联系到一起。

(4)    不足

(a)     编写Command的任务重;

(b)     由于Silverlight不能引用非Silverlight项目,许多界面层的逻辑也得放到后台(非ViewModel部分),如Command实现,必须通过WCF通信调用服务。

(5)    ICommand

(a)    编写每一个需要绑定的Command

(b)     网上有写好的基于WPF的Command模板,利用Prism特性重用这部分。

(c)     直接利用Prism的DelegateCommand和CompositeCommand类

          DelegateCommand接受Delegate参数

          CompositeCommand可以将多个Command组合在一起。

注:Prism 是微软最佳实践,可以简化建设WPF和Silverlight应用。

(6)     Command原理

 

(7)AttachBehavior

(a) 背景

假设我们有一个Button, 当该Button被点击的时候我们要完成一些操作, 很简单, 将该操作封装成一个Command并绑定到该Button上就可以了, 但如果我们要在Button被Load的时候执行另外一些操作呢?  由于Button没有直接被Load事件所触发的Command, 所以不能使用Command了. 不能直接将Load事件处理器写在Button所在的xaml所对应的CS文件里, 这和我们刚才对MVVM的设计是相矛盾的. 一个不太好的方案是继承一下Button, 并撰写一个由Load所触发的Command, 这可行, 但明显不好. 正如一个控件没有某个属性并且在不继承的情况下而采用AttachProperty一样, 我们可以采用AttachBehavior.    

五、MVVM具体应用

(1)     Command工作流程:

主要使用Prism的DelegeCommand、CompositeCommand类与订阅、发布原理。

(a)       ViewModel初始化命令(ICommand)

public DelegateCommand<object> AddAdministorCommand { get; private set; }

this.AddAdministorCommand = new DelegateCommand<object>(this.AddAdministor);

(b)     View UI元素绑定ViewModel Command属性

Command="{Binding Path=AddAdministorCommand}"

(c)     在ViewModel中定义一个事件(event),实现CompositePresentationEvent<object>接口(object-传递参数)。

public class AdministorRequestEvent: CompositePresentationEvent<AdministorViewModel>

{ }

(d)     使用TheAggregator订阅一个事件的执行者(Action)

this.TheAggregator.GetEvent<AdministorRequestEvent>().Subscribe(ShowAdministorVie        w);

        public void ShowAdministorView(AdministorViewModel administorVM)

{

            AdministorView administorV = new AdministorView();

            administorV.ViewDataContext = administorVM;

            administorV.Show();

           }

(e)    使用发布一个事件

private void AddAdministor(object obj)

{

this.TheAggregator.GetEvent<AdministorRequestEvent>().Publish( new AdministorViewModel(Administor.CreateNewAdministor(),_administorRepository));

}

posted @ 2011-06-10 07:19 linFen 阅读(265) 评论(0) 编辑

Model-View-ViewModel (MVVM) 设计模式描述了构建 WPF 或 Silverlight 应用程序的常用方法。它还是一款构建应用程序的强大工具,以及一种与开发人员讨论应用程序设计的通用语言。虽然 MVVM 确实很有用,但它发展时间不长,用户尚未形成正确的认识。在MVVM模式中,你需要一个为View量身定制的model,那么这个model实际上就是上图ViewModel。ViewModel包含所有UI所需要的接口和属性,这样只需要通过Binding使他们进行关联,就可以使二者之间达到松散耦合,所以这样一来,UI就可以由UI专业人员用design和blend来实现(当然很多效果还是需要用传统的制图软件),代码人员也可以专心写他的逻辑也业务代码,所以这样分工和协作变得更轻松、更愉快了,更漂亮的是View完全可以由(Unit/Automatic Test)所取代,所以单元测试也变得如此简单,这对于我们的开发人员和测试人员无疑是一个很好的解脱,同时也提高了系统的可测性、稳定性和维护性。数据绑定系统同时也提供了标准化的方式传输到视图的验证错误的输入的验证(但是个人觉得不是很好用,所以我们在实际的项目当中会写一套自己的验证框架)。

  当然是用这个模式的时候,我们还要注意很多细节,这个是我们必须面对的,比如我们怎么实现ViewModel之间的通信、怎样用Attached Behavior实现特定命令操作、validation的自定义设置、延迟加载、性能优化、与传统技术的交互等等问题。

  WPF项目

  前面讲了一些MVVM的基本概念,那么我们在WPF中究竟怎么使用呢?那么下面就是我们在项目当中的具体使用,第一幅图就是整个项目的结构图,第二副图是View和ViewModel以及一些常用的UI操作(当然这里也可以更加细节化,具体更加实际决定) 。

上图就是整个项目的结构图

上图是View和ViewModel以及一些常用的UI操作

  WinForm/ASP.NET上使用实践

  WinForm和ASP.NET在使用这个模式的时候颇为类似,所以我们今天就用WinForm进行演示,为了能够更好的表达今天的主题,我们尽量把UI和逻辑做简单一些,这样便于我们要阐述的思想也能大道至简,首先我们先建立一个WinForm的项目,然后加入两个TextBox,五个Label,一个Button,控件的摆放位置如下所示:

  然后我们新建三个文件夹,Common文件夹放一些公用的类,这里只放了ViewModelAttribute 类,其目的在于关联ViewModel和View的标示;View文件夹放置我们刚才新建的窗体,主要做UI的相关操作;ViewModel主要是放置一些ViewModel类(这些类是和View最好一一对应,提供一个定制化的Context).ClassDiagram1.cd是本应用程序的类图关系,Program.cs则承担了启动应用程序、关联View和ViewModel、处理整个应用程序的作用。

 

  第二步就是该建立我们的ViewModel了,我们在WPF中通常是一个View有一个定制的ViewModel相对应(或者多个View对应一个ViewModel),我们今天做的这个WinForm的例子也不例外,请看下面ViewModel类图:

正如我们上图看到的那样, View model 类包含了一个_form1的变量,这个变量的目的就是通过form1来操作它的子控件,然后我们就可以对它及它的子控件进行一些操作,已完成我们需要的业务,正如WPF中的ViewModel 一样,它的主要任务是提供给View的一个定制化的Model,所以我们要在里面实现业务的操作以及提供View所需要的属性和接口。代码很简单,我这里就不多费口水,代码如下:

using System;
using System.Windows.Forms;
namespace MVVMInWinForm
{    public class ViewModel  
{        //Form1的实例设置为View      
Form1 _form1;        
public ViewModel(Form1 form1)        {            _form1 = form1;      
     /*通过搜索整个页面的Controls,然后对各个控件的事件进行订阅 */        
foreach (Control item in _form1.Controls)      
{            
   if (item is Button)                {                    
(item as Button).Click+=new EventHandler(ViewModel_Click);          
     }          
  }        
   //启动窗体      
Application.Run(_form1);      
  }    
   /// <summary>  
     /// 具体的事件处理代码      
  /// </summary>      
  protected void ViewModel_Click(object sender, EventArgs args)      
  {        
   string result =string.Empty;      
      //具体操作        
   foreach (Control item in _form1.Controls)        
    {                
if (item is TextBox)                {                  
  result += (item as TextBox).Text;          
  
   }      
      }      
     foreach (Control item in _form1.Controls)            {            
   /* 显示操作后的结果*/            
if (item is Label && item.TabIndex ==5)            
    {                
   (item as Label).Text = result.ToString();              
  }        
   }      
  }    
}    
   }

 

  第三步是可选的步骤,你可以不用这个Attribute ,同样可以实现我们所要的功能,但加上它以后可以增强程序的灵活性。我们通过反射的形式来读取值,然后标示View是否被激活,如果是,则View和ViewModel进行关联,否则反之;

  第四步很重要,Winform中不像WPF和Silverlight那样灵活且没有那么多的延伸特性和附加功能,所以只能通过外界代码的方式手动绑定View和ViewModel

[global::System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]sealed class ViewModelAttribute : System.Attribute
{    
public ViewModelAttribute()
{
}  
public ViewModelAttribute(bool Activated)    {      
this.Activated = Activated;  
  }      
public bool Activated { get; set; }
}  

 

using System;using System.Reflection;using System.Windows.Forms;using MVVMInWinForm.Attribute;namespace MVVMInWinForm{    
static class Program    
{        
[STAThread]        
static void Main()        {          
Application.EnableVisualStyles();            Application.SetCompatibleTextRenderingDefault(false);          
  Initialize();      
}      
private static void Initialize()        {          
Type t = typeof(Form1);      
  //通过Type获取Attributes的值    
object[] attributes = t.GetCustomAttributes(typeof(ViewModelAttribute),
false);      
//如果ViewModelAttribute的Activated值为true,把相关的View和ViewModel关联起来,否则不关联            if (attributes.Length > 0 && (attributes[0] as ViewModelAttribute).Activated == true)          
  {              
Form1 form1 = new Form1();                ViewModel viewModel = new ViewModel(form1);        
    }          
  else        
   {              
Application.Run(new Form1());      
     }      
}        
}
}

 

using System;using System.Windows.Forms;
using MVVMInWinForm.Attribute;namespace MVVMInWinForm{    
//通过Attributes的方式来标记,true表示关联,false表示不关联,具体由主程序控制    
[ViewModel(true)]    
public partial class Form1 : Form    
{      
public Form1()      
  {          
InitializeComponent();      
}  
}
}

posted @ 2011-06-10 07:05 linFen 阅读(181) 评论(0) 编辑