本文主要是翻译Rachel Lim的一篇有关MVVM模式介绍的博文 A Simple MVVM Example

并具体给出了一个简单的Demo(原文是以WPF开发的,对于我自己添加或修改的一部分会用红色标注)

现在开始:

在我看来,如果你使用的是WPF或Sliverlight来开发程序就应该使用MVVM设计模式。它是你的代码清晰明了并易于维护。

可问题是网上有很多有关MVVM模式的资源都有自己强大的实现方式。这里我将介绍最基础的MVVM设计模式的实现方法。

MVVM  (是Model-View-ViewModel的缩写)

Model: 保存数据的简单类对象,它只能包含属性和属性验证(应该就是验证属性值是否正确),而不负责存储数据、事件点击、复杂运算、业务规则和其他操作。

View: 呈现给用户的数据界面,很多情况下,他是以数据模板(DataTemplates)的方式告诉应用如何呈现类中内容的。

      如果代码内容只跟View有关(比如社交焦点和执行动画),可以将代码写在View的后台。

ViewModel:用来处理逻辑。你的后台代码(数据访问、点击事件、复杂运算、业务规则验证等)都写在这里。这里面的代码View的反应。

比如,View中有一个ListBox对象、选中的对象、保存按钮。ViewModel中就要包含ObservableCollection<Model>集合、

Mode类型的SelectedObject和命令ICommand SaveCommand.

下面就通过一个简单的例子看看这三者之间是如何相互联系的。你会发现除了属性和方法名,任意一者是不需要访问另外两者的。

一旦接口被定义了,每一层可以完全独立于其他运行。

此例中,我使用的是Product Model,这个类中只含有属性和属性更改通知(INotifyPropertyChanged)

1.Model

public class ProductModel : ObservableObject
{
//字段
private int _productId;
private string _productName;
private decimal _unitPrice; //属性
public int ProductId
{
get { return _productId; }
set
{
SetProperty(ref this._productId, value);
}
} public string ProductName
{
get { return _productName; }
set
{
SetProperty(ref this._productName, value);
}
} public decimal UnitPrice
{
get { return _unitPrice; }
set
{
SetProperty(ref this._unitPrice, value);
}
} public ProductModel()  //这里的构造函数只是为了后面TextBlock能够以此方法绑定显示:Text="{Binding CurrentProduct.ProductId}"
{
this.ProductName = "Lumia 930";
this.ProductId = 123;
this.UnitPrice = 2799;
}
}

ProductModel继承了ObservableObject类,而ObservableObject实现了INotifyPropertyChanged接口

(ObservableObject和原文不一样,只是简化了一下,具体可以查看 INotifyPropertyChanged接口的实现,介绍的很详细)

public class ObservableObject: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; public bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
} protected void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (null != eventHandler)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

注意:属性更改通知(INotifyPropertyChanged):当Model中属性更改时,会通知View实时的更新View页面。

有人建议将这个放入ViewModel而不是Model中。虽然两种方式都是有效的,但是我发现放入ViewModel更加复杂,

还需要更多的代码。而放入Model中更简单些。[的确,很多例子都是有ViewModel来继承的...不知道为什么这里要Model继承。。有更加复杂么?]

2.ViewModel
由于在创建View之前需要ViewModel,接下来我们就来创建ViewModel。它要包括用户操作所有的交互。

现在这里包括4个属性:CurrentProduct当前产品, 产品获取命令GetProductCommand,保存命令SaveProductCommand.用来查找某个产品的ProductId

public class ProductViewModel : ObservableObject
{
    private int _productId;  
    private ProductModel _currentProduct;
    private ICommand _getProductCommand;
    private ICommand _saveProductCommand;
    
    public int ProductId
    {
      get { return _productId; }
      set
      {
        SetProperty(ref this._productId, value);
      }
    }
    public ProductViewModel()
    {
            CurrentProduct = new ProductModel();
    }
public ProductModel CurrentProduct
{
get { return _currentProduct; }
set
{
if (null == _currentProduct)
_currentProduct = new ProductModel();
_currentProduct = value;
}
}
public ICommand SaveProductCommand
{
get
{
if (_saveProductCommand == null)
{
_saveProductCommand = new RelayCommand(SaveProduct);  //实例化构造时和原文参数不一样
}
return _saveProductCommand;
}
} public ICommand GetProductCommand
{
get
{
if (_getProductCommand == null)
_getProductCommand = new RelayCommand(GetProduct, IsEnable);  //实例化构造时和原文参数不一样
return _getProductCommand;
}
}
public bool IsEnable()  //此控件是否可点击
{
return true;
} public void SaveProduct()  //执行命令
{
await new MessageDialog("保存").ShowAsync();
}      public void GetProduct(object parameter)  
        {
            if (parameter.ToString() == string.Empty)  //多做了一个判断
                return;
            ProductId = int.Parse(parameter.ToString());
            ProductModel product = new ProductModel();
            product.ProductName = "Test Product";
            product.ProductId = ProductId;
            product.UnitPrice = 10.00M;
            CurrentProduct = product;
        }
}

这里出现了一个新类RelayCommand,MVVM的正常使用必不可少。这个命令表示的是由其他类调用委托来实现此类中的代码

[在建立非空项目的时候Command文件夹会自动生成此类,但是本身只定义了不带参数的方法,需要进行扩展---注释部分]

public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
private readonly Action<object> _executeParam;  //新增了一个带参数有返回值的方法 public event EventHandler CanExecuteChanged; public RelayCommand(Action execute)
: this(execute, null)
{
} public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
} public RelayCommand(Action<object> executeParam)  //新增重载构造函数
{
if(executeParam == null)
throw new ArgumentNullException("executeParam");
_executeParam = executeParam;
_canExecute = () => true;
} public RelayCommand(Action<object> executeParam, Func<bool> canExecute)  //新增重载构造函数
{
if (executeParam == null)
throw new ArgumentNullException("executeParam");
_executeParam = executeParam;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)  //新增判断
{
if (parameter == null)
_execute();
else
_executeParam(parameter);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}

3.View(可以认为是MainPage.xaml    和原文不一样)

<StackPanel>
  <StackPanel x:Name="stackpaenl1">
    <TextBlock Text="{Binding CurrentProduct.ProductId}" Foreground="Yellow"/>
    <TextBlock Text="{Binding CurrentProduct.ProductName}"/>
    <TextBlock Text="{Binding CurrentProduct.UnitPrice}"/>         <TextBlock Text="Enter Product Id"/>
    <TextBox x:Name="Input"/>
    <TextBlock Text="{Binding ProductId}" Foreground="Yellow"/>
    <Button Content="Get Product" Command="{Binding GetProductCommand}" CommandParameter="{Binding ElementName=Input, Path=Text}"/>
    <Button Content="Save Product" Command="{Binding SaveProductCommand}"/>
  </StackPanel>
</StackPanel>

最后在View.cs添加代码

  ProductViewModel product = new ProductViewModel();
  product.ProductId = ;
  this.DataContext = product;  //绑定错了的话 COMMAND是不起作用的

以上就是简单的MVVM程序。

PS:下篇文章会接着本文

1.将添加Product集合,绑定到列表

2.给点击ListBox的添加选项改变时的事件(要附加依赖属性,和Button点击事件不同)

3.通过自定义类以JSON获取保存数据到存储空间

MVVM开发模式简单实例MVVM Demo的更多相关文章

  1. MVVM开发模式简单实例MVVM Demo【续】

    本文将接着上篇文章,介绍一下三点:(Universal App) 1.将添加Product集合,绑定到列表 2.给点击ListBox的添加选项改变时的事件(要附加依赖属性,和Button点击事件不同) ...

  2. js架构设计模式——理解javascript中的MVVM开发模式

    理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...

  3. 3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)

    前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...

  4. 玩转Android之MVVM开发模式实战,炫酷的DataBinding!

    C# 很早就有了MVVM的开发模式,Android手机中的MVVM一直到去年Google的I\O大会上才推出,姗姗来迟.MVVM这中开发模式的优点自不必多说,可以实现视图和逻辑代码的解耦,而且,按照G ...

  5. (Hibernate进阶)Hibernate搭建开发环境+简单实例(二)

    hibernate是非常典型的持久层框架,持久化的思想是非常值得我们学习和研究的.这篇博文,我们主要以实例的形式学习Hibernate,不深究Hibernate的思想和原理,否则,一味追求,苦学思想和 ...

  6. 【SSH进阶之路】Hibernate搭建开发环境+简单实例(二)

    Hibernate是很典型的持久层框架,持久化的思想是很值得我们学习和研究的.这篇博文,我们主要以实例的形式学习Hibernate,不深究Hibernate的思想和原理,否则,一味追求,苦学思想和原理 ...

  7. Android之MVVM开发模式

    MVVM 模式简介 MVVM模式是指Model-View-ViewModel.相信看过笔者关于MVP的文章的读者也会发现,无论如何抽象化,在我们的View层中是无法避免的要处理一部分逻辑的.而MVVM ...

  8. 精通MVC网站、MVVM开发模式、Razor语法

    http://www.cnblogs.com/powertoolsteam/p/MVC_one.html ASP.NET MVC (一)——深入理解ASP.NET MVC 以下是ASP.NET MVC ...

  9. 从MVC -> MVVM ? 开发模式

    MVVM 到底是什么? view :由 MVC 中的 view 和 controller 组成,负责 UI 的展示,绑定 viewModel 中的属性,触发 viewModel 中的命令: viewM ...

随机推荐

  1. 【小白的CFD之旅】04 任务

    和老蓝见面之后的很长一段时间里,小白都没有接到任何老蓝的消息,再加上课比较多,小白也慢慢适应了白天上课,晚上窝在宿舍打游戏,偶尔也去图书馆看看书的生活,这样宁静的生活持续了差不多两个月.就在老蓝的影子 ...

  2. Android项目部署时,发生AndroidRuntime:android.view.InflateException: Binary XML file line #168: Error inflating class错误

    这个错误也是让我纠结了一天,当时写的项目在安卓虚拟机上运行都很正常,于是当我部署到安卓手机上时,点击登陆按钮跳转到用户主界面的时候直接结束运行返回登陆界面.    当时,我仔细检查了一下自己的代码,并 ...

  3. 系统集成方案(一).NET集成方案

    NET系统集成有自己独立的登录验证方式.比如,跟报表集成时,不需要再使用报表内置的登录界面,只需要将报表默认的参数用户名fr_username和密码fr_password发送给报表系统,触发一下报表验 ...

  4. 报表工具如何实现多次导入Excel

    很多人在开发报表的时候会遇到将多张表样相同的excel导入到模板,然后提交至数据库中.但问题是很多情况,在线导入不支持一次性选择多个excel,一次只能选择一个excel,也不能将多个excel中的数 ...

  5. Counting Bits -leetcode

    introduction: Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num  ...

  6. 基于SuperSocket的IIS主动推送消息给android客户端

    在上一篇文章<基于mina框架的GPS设备与服务器之间的交互>中,提到之前一直使用superwebsocket框架做为IIS和APP通信的媒介,经常出现无法通信的问题,必须一天几次的手动回 ...

  7. Windows phone应用开发[18]-下拉刷新

    在windows phone 中采用数据列表时为了保证用户体验常遇到加载数据的问题.这个问题普遍到只要你用到数据列表就要早晚面对这个问题. 很多人会说这个问题已经有解决方案. 其实真正问题并不在于如何 ...

  8. 利用CSS计数函数counter()实现计数

    要实现li列表计数比较简单,直接设置list-style-type即可,但是要实现非li列表计数该怎么办呢,counter()可以轻松实现 body{counter-reset:section 0 s ...

  9. 创建一个新的Activity

    1.创建一个类继承Activity类,并创建对应的布局文件,在onCreate方法中加载该布局. 2.在AndroidManifest.xml声明该组件 注:如果想配置一个activity在桌面上有该 ...

  10. ECharts学习(2)--饼状图之南丁格尔图

    1.上一篇中讲了如何绘制一个简单的柱状图,这次要画的是饼图,饼图主要是通过扇形的弧度表现不同类目的数据在总和中的占比,它的数据格式比柱状图更简单,只有一维的数值,不需要给类目.因为不在直角坐标系上,所 ...