WPF:MVVM解析探究

一 学习前提:

(1)Data Binding
(2)Dependency Property
(3)委托、事件、命令ICommand
上面三点内容,在学习MVVM之前要求简单了解并掌握使用。

MVVM介绍

MVC,Model - View - Controller的模式,页面和代码分离的写法,MVVM:Model - View - ViewModel,和WPF很好的进行结合,View负责界面,主要是写.xaml的文件,Model是一些实体类;ViewModel是联系两者的关键,并分离两者;
View需要什么,ViewModel就提供什么,如果将View理解为界面,Model和ViewModel以及Service等理解为后台的话,那么界面和后台是没有任何关系的,界面开发人员只要告诉后台人员需要哪些对象/属性,就可以进行开发了,二者之间的结合通过Binding操作进行绑定,解耦效果优于MVC,架构图如下:

WPF:MVVM解析探究_第1张图片
几个重要的概念:
1、属性
1)数据绑定源:CLR对象、动态对象、ADO.NET 对象、XML对象、DependencyObject对象
2)命令属性:ICommand、
3) 集合对象绑定:ObservableCollection
2、NotificationObject类
3、ICommand接口

    MVVM的难点和重点在于View以及MiewModel之间的绑定。

三 项目实战

效果如下:
按照上述架构图新建目录如下:

WPF:MVVM解析探究_第2张图片

按照从底层到显示层的策略:
(1)Data



  
    土豆泥底披萨
    披萨
    本店特色
    4.5
  
  
    烤囊底披萨
    披萨
    本店特色
    5
  
  
    水果披萨
    披萨
    
    4
  
  
    牛肉披萨
    披萨
    
    5
  

(2)Model

//定义Model,用于和xml文档中的节点属性匹配 
class Dish
{
    public string Name { get; set; }
    public string Category { get; set; }
    public string Comment { get; set; }
    public double Score { get; set; }
}
class Restaurant 
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string PhoneNumber { get; set; }
}

(3)Service
接口:

interface IDataService
{
    List GetAllDishes();
}
interface IOrderService
{
    void PlaceOrder(List dishes);
}

实现:

class XmlDataService:IDataService
{        
    public List GetAllDishes()
    {
        //读取Dish集合,用于接收数据并返回值
        List dishList = new List();
        //读取XML文件路径
        string xmlFileName = System.IO.Path.Combine(Environment.CurrentDirectory, @"Data\Data.xml");
        //加载xml文件
        XDocument xDoc = XDocument.Load(xmlFileName);
        //按照顺序返回集合下标签里的所有内容
        var dishes = xDoc.Descendants("Dish");
        //将xml筛选的集合里的属性与Model对象绑定
        foreach (var d in dishes)
        {
            Dish dish = new Dish();
            dish.Name = d.Element("Name").Value;
            dish.Category = d.Element("Category").Value;
            dish.Comment = d.Element("Comment").Value;
            dish.Score = double.Parse(d.Element("Score").Value);
            //添加到List集合
            dishList.Add(dish);
        }
        return dishList;
    }
}
class MockOrderService:IOrderService
{
    public void PlaceOrder(List dishes)
    {
        System.IO.File.WriteAllLines(@"C:\order.txt", dishes.ToArray());
    }
}

至此,静态的代码编写完毕,无论是使用什么类型的框架,这部分东西大同小异。
(4)ViewModels

class DishMenuItemViewModel:NotificationObject
{
    public Dish Dish { get; set; }
    //将IsSelected属性和Dish中的属性一起作为DishMenuItemViewModel里的属性
    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            //RaisePropertyChanged方法,源于引入属性更改通知类
            isSelected = value;
            this.RaisePropertyChanged("IsSeleted");//"IsSelected"属性值变化之后,自动通知使用该属性的方法,有点观察者模式的意思
        }
    }
}
class MainWindowViewModel : NotificationObject
{
    public DelegateCommand PlaceOrderCommand { get; set; }
    public DelegateCommand SelectMenuItemCommand { get; set; }
    public DelegateCommand RemoveItemCommand { get; set; }
    private double count;
    //数据属性-Count
    public double Count
    {
        get { return count; }
        set
        {
            count = value;
            this.RaisePropertyChanged("Count");
        }
    }

    private Restaurant restaurant;
    //数据属性-Restaurant
    public Restaurant Restaurant
    {
        get { return restaurant; }
        set
        {
            restaurant = value;
            this.RaisePropertyChanged("Restaurant");
        }
    }

    private ObservableCollection dishMenu;

    public ObservableCollection DishMenu
    {
        get { return dishMenu; }
        set
        {
            dishMenu = value;
            this.RaisePropertyChanged("DishMenu");
        }
    }

    public MainWindowViewModel()
    {
        this.LoadRestaurant();
        this.LoadDishMenu();
        //单向"命令属性"的加载过程,通过实例化委托的形式
        this.PlaceOrderCommand = new DelegateCommand();
        this.PlaceOrderCommand.ExecuteAction = new Action(this.PlaceOrderCommandExecute);
        this.SelectMenuItemCommand = new DelegateCommand();
        this.SelectMenuItemCommand.ExecuteAction = new Action(this.SelectMenuItemExecute);
        this.RemoveItemCommand = new DelegateCommand();
        this.RemoveItemCommand.ExecuteAction = new Action(this.RemoveItem);
    }


    private void LoadRestaurant()
    {
        this.Restaurant = new Restaurant();
        this.Restaurant.Name = "茶馆餐厅";
        this.restaurant.Address = "北京市乐客灵境科技有限公司";
        this.Restaurant.PhoneNumber = "12345678900";
    }

    private void LoadDishMenu()
    {
        IDataService ds = new XmlDataService();
        var dishes = ds.GetAllDishes();
        //为数据属性赋值
        this.DishMenu = new ObservableCollection();
        foreach (var dish in dishes)
        {
            DishMenuItemViewModel item = new DishMenuItemViewModel();
            item.Dish = dish;
            item.IsSelected = false;
            this.DishMenu.Add(item);
        }
    }

    private void PlaceOrderCommandExecute(object parameter)
    {
        //lamad表达式的形式来选取所需要的数据
        var selectedDishes = this.DishMenu.Where(i => i.IsSelected == true).Select(i => i.Dish.Name).ToList();
        IOrderService orderService = new MockOrderService();
        orderService.PlaceOrder(selectedDishes);
        MessageBox.Show("订餐成功!");
    }
    private void SelectMenuItemExecute(object parameter)
    {
        this.Count = this.DishMenu.Count(i => i.IsSelected == true);
    }     

    private void RemoveItem(object parameter)
    {            
        this.DishMenu.RemoveAt(0);//移除第一项
        SelectMenuItemExecute(parameter);//更新Count值
    }        
} 
 

其中DelegateCommand就是继承ICommand而来,包括二个方法和一个事件:

class DelegateCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        if (this.CanExecuteFunc == null)
        {
            return true;
        }

        return this.CanExecuteFunc(parameter);
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (this.ExecuteAction == null)
        {
            return;
        }
        this.ExecuteAction(parameter);
    }

    public Action ExecuteAction { get; set; }
    public Func CanExecuteFunc { get; set; }
} 
 

其实会发现,在ViewModel当中并没有很强的业务逻辑,业务逻辑更多的是放到Service当中的,在ViewModel当中所存放的内容,更多的会是一些属性,包括命令属性、数据属性,这些用于和View进行绑定,通过Binding,发现,后台的数据改变了,直接就会在前台页面上更新,这就是MVVM + WPF的魅力之一。同时也要知道,View和ViewModel之间的绑定,也是使用这个框架的难点之一。
(5)View
通过对控件的属性、样式进行设置.
通过Binding和后台数据进行绑定.

         
        
            
                
                    
                    
                    
                
                订餐系统:
                
                    
                        
                        
                        
                        
                        
                            
                                
                                    
                                
                            
                        
                    
                
                
                                        
                    
                        
                        
                        
                        

此时会发现,在View的.cs代码里,没有类似于onClick(),这样的方法,都通过绑定实现自动更新了。
(6)设置View的数据来源

public MainWindow()
{
    InitializeComponent();
    this.DataContext = new MainWindowViewModel();
}

在View的.cs文件中,通过this.DataContext = new MainWindowViewModel();的方式,绑定该View的数据来自于哪个ViewModel。
ps:另外,主要参考文章http://blog.csdn.net/zzh92062...
我做了代码演示并进行一些扩展,主要在于把List绑定更改为ObservableCollection。就不会出现在List中移除一项,界面不自动更新的bug,各位可尝试,还可进行CanExecuteChanged的尝试。

  至此,MVVM框架的简单实用,就通过这个例子实现了。
  That's all.

你可能感兴趣的:(c#,visual-studio)