使用Prism提供的类实现WPF MVVM点餐Demo

由于公司开发的技术需求,近期在学习MVVM模式开发WPF应用程序。进过一段时间的学习,感受到:学习MVVM模式,最好的方法就是用MVVM做几个Demo,因为编程里面的东西还是原来的WPF的相关知识。最近学习的资料来源大多为CodePlex、CodeProject和MSDN,以及博客园MS的MVP刘铁锰的一些资料。

前面几篇博文DebugLZQ写了,如何来写MVVM,以及Prism框架的安装等等。

本篇在前面的基础上,通过一个相对复杂一点的Demo,来学习Prism中的一些类的使用。

首先来介绍下今天这个Demo要实现的功能,今天开启的系统是XP,所以下面各位看到的将是XP风格的界面。:

使用Prism提供的类实现WPF MVVM点餐Demo

·界面上方TextBlock显示餐馆的信息(粉红色字),该信息保存在一个ViewModel的一个餐馆的属性中。

·DataGrid显示菜品信息,从一个模拟的Service中读出;并在最后添加一个CheckBox Binding一个命令用来选择菜品

·下面的TextBox显示选中了几个菜,Button则Binding一个Command实现点菜(象征性的存入本地磁盘)

下面来实现它:

//---------------------

最终的项目的文件结构如下:

使用Prism提供的类实现WPF MVVM点餐Demo

前面说过,可以直接饮用Prism,只引入相关的程序集也可以(虽然是一回事),这次我们就这么干!

1.新建一个WpfPrism的WPF项目,添加Prism dll引用,(使用NotificationObject、DelegateCommand)如下:

使用Prism提供的类实现WPF MVVM点餐Demo

 2.在项目中添加一个Data文件夹,放入Data.XML文件,文件如下:

View Code
<?xml version="1.0" encoding="utf-8"?>

<Dishes>

    <Dish>

        <Name>土豆泥底披萨</Name>

        <Category>披萨</Category>

        <Comment>本店特色</Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>烤囊底披萨</Name>

        <Category>披萨</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>水果披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>4</Score>

    </Dish>

    <Dish>

        <Name>牛肉披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>培根披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>什锦披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>金枪鱼披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>海鲜披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>川香披萨</Name>

        <Category>披萨</Category>

        <Comment></Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>黑椒鸡腿扒</Name>

        <Category>特色主食</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>肉酱意面</Name>

        <Category>特色主食</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>寂寞小章鱼</Name>

        <Category>风味小吃</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>照烧鸡软骨</Name>

        <Category>风味小吃</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>芝士青贝</Name>

        <Category>风味小吃</Category>

        <Comment></Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>奥尔良烤翅</Name>

        <Category>风味小吃</Category>

        <Comment>秒杀KFC</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>双酱煎泥肠</Name>

        <Category>风味小吃</Category>

        <Comment></Comment>

        <Score>4</Score>

    </Dish>

    <Dish>

        <Name>香酥鱿鱼圈</Name>

        <Category>风味小吃</Category>

        <Comment>本店特色</Comment>

        <Score>4.5</Score>

    </Dish>

    <Dish>

        <Name>黄金蝴蝶虾</Name>

        <Category>风味小吃</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>金枪鱼沙拉</Name>

        <Category>沙拉</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>日式素沙拉</Name>

        <Category>沙拉</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>冰糖洛神</Name>

        <Category>饮料</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>玫瑰特饮</Name>

        <Category>饮料</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>清新芦荟</Name>

        <Category>饮料</Category>

        <Comment></Comment>

        <Score>5</Score>

    </Dish>

    <Dish>

        <Name>薄荷汽水</Name>

        <Category>饮料</Category>

        <Comment>本店特色</Comment>

        <Score>5</Score>

    </Dish>

</Dishes>

3.在项目中添加Model文件夹。添加两个Model Dish和Restaurant,分别如下:

View Code
namespace WpfPrism.Models

{

    class Dish

    {

        public string Name { get; set; }



        public string Category { get; set; }



        public string  Comment { get; set; }



        public string  Score { get; set; }

    }

}
View Code
namespace WpfPrism.Models

{

    class Restaurant

    {

        public string Name { get; set; }



        public string Address { get; set; }



        public string  PhoneNumber { get; set; }

    }

}

4.在项目中添加Services文件夹,其中IDataService、XMLDataService用来定义和实现:获取菜品信息功能。IOrderService和MockOrderService用来定义和实现:点菜功能。之所以使用接口,是为了定义和实现相分离!
其代码依次如下:

View Code
using System.Collections.Generic;

using WpfPrism.Models;



namespace WpfPrism.Services

{

    interface IDataService

    {

        List<Dish> GetAllDishes();

    }

}
View Code
using System;

using System.Collections.Generic;

using WpfPrism.Models;

using System.IO;

using System.Xml.Linq;





namespace WpfPrism.Services

{

    class XMLDataService:IDataService//接口:定义和实现相分离

    {

        #region IDataService 成员



        public List<Models.Dish> GetAllDishes()

        {

            List<Dish> dishList = new List<Dish>();



            string xmlFile = Path.Combine(Environment.CurrentDirectory, @"Data/Data.xml");



            XDocument xDoc = XDocument.Load(xmlFile);

            var dishes = xDoc.Descendants("Dish");

            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 = d.Element("Score").Value;

                dishList.Add(dish);

            }



            return dishList;

        }



        #endregion

    }

}
View Code
using System.Collections.Generic;



namespace WpfPrism.Services

{

    interface IOrderService

    {

        void PlaceOrder(List<string> dishes);

    }

}
View Code
using System.Collections.Generic;

using System.IO;



namespace WpfPrism.Services

{

    class MockOrderService:IOrderService//接口:实现定义和实现相分离

    {

        #region IOrderService 成员



        public void PlaceOrder(List<string> dishes)

        {

            File.WriteAllLines(@"D:/order.txt", dishes.ToArray());

        }



        #endregion

    }

}

5.在项目中添加一个ViewModels文件夹,并添加两个Model:DishMenuItemViewModel和MianWindowViewModel。

稍微解释一下:MianWindowViewModel中的一个属性是List<MianWindowViewModel>类型的。两者代码分别如下:

using Microsoft.Practices.Prism.ViewModel;

using WpfPrism.Models;



namespace WpfPrism.ViewModels

{

    class DishMenuItemViewModel:NotificationObject

    {

        public Dish Dish { get; set; }



        private bool isSelected;

        public bool IsSelected//这个地方刚开始写错了,废了太大的劲才找出来(注意拼写!)

        {

            get { return isSelected; }

            set 

            {

                isSelected = value;

                RaisePropertyChanged("IsSelected");

            }

        }

    }

}
using System;

using System.Collections.Generic;

using System.Linq;

using Microsoft.Practices.Prism.ViewModel;

using WpfPrism.Models;

using WpfPrism.Services;

using Microsoft.Practices.Prism.Commands;

using System.Windows;



namespace WpfPrism.ViewModels

{

    class MianWindowViewModel:NotificationObject

    {

        private Restaurant restaurant;

        public Restaurant Restaurant

        {

            get { return restaurant; }

            set

            {

                restaurant = value;

                RaisePropertyChanged("Restaurant");

            }

        }



        //外加的一个属性,点菜的数量

        private int count;

        public int Count

        {

            get { return count; }

            set

            {

                count = value;

                RaisePropertyChanged("Count");

            }

        }



        private List<DishMenuItemViewModel> dishMenu;

        public List<DishMenuItemViewModel> DishMenu

        {

            get { return dishMenu; }

            set

            {

                dishMenu = value;

                RaisePropertyChanged("DishMenu");

            }

        }



        public MianWindowViewModel()

        {

            LoadRestuarant();//赋值Restaurant属性

            LoadDishMenu();//赋值DishMenu属性



            //初始化两个命令属性

            PlaceOrderCommand = new DelegateCommand(new Action(PlaceOrderCommandExecute));

            SelectMenuItemCommand = new DelegateCommand(new Action(SelectMenuItemCommandExecute));

        }



        private void LoadRestuarant()

        {

            Restaurant = new Restaurant() {Name="百年苏韵", Address="江苏大学", PhoneNumber="0511-12345678"};           

        }



        private void LoadDishMenu()

        {

            DishMenu = new List<DishMenuItemViewModel>();



            IDataService ds = new XMLDataService();

            var dishes = ds.GetAllDishes();

            foreach (var d in dishes)

            {

                DishMenuItemViewModel item = new DishMenuItemViewModel() {  Dish=d};

                DishMenu.Add(item);

            }

        }



        //两个命令属性

        public DelegateCommand PlaceOrderCommand { get; set; }

        public DelegateCommand SelectMenuItemCommand { get; set; }



        private void PlaceOrderCommandExecute()

        {

            //获取点菜单

            var selectedDishes = dishMenu.Where(d => d.IsSelected == true).Select(d => d.Dish.Name).ToList();



            //仅保存到本地磁盘--可以写一些有意义的代码

            IOrderService orderService = new MockOrderService();

            orderService.PlaceOrder(selectedDishes );



            MessageBox.Show("订餐成功!");

        }



        private void SelectMenuItemCommandExecute()

        {

            Count = DishMenu.Count(n=>n.IsSelected==true);

        }

        

    }

}

注意NotificationObject是ViewModel的基类。

最后,为View添加Binding:

<Window x:Class="WpfPrism.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="MainWindow" Height="350" Width="590">

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="*"/>

            <RowDefinition Height="Auto"/>

        </Grid.RowDefinitions>

        <!-- 餐馆信息-->

        <StackPanel Grid.Row="0">

            <StackPanel Orientation="Horizontal" >

                <TextBlock Text="欢迎光临-" FontSize="40"/>

                <TextBlock Text="{Binding Restaurant.Name}" FontSize="40"  Foreground="HotPink" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" >

                <TextBlock Text="地址:" FontSize="40"/>

                <TextBlock Text="{Binding Restaurant.Address}" FontSize="40" Foreground="HotPink" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" >

                <TextBlock Text="电话:" FontSize="40"/>

                <TextBlock Text="{Binding Restaurant.PhoneNumber}" FontSize="40" Foreground="HotPink" />

            </StackPanel>

        </StackPanel>

        <!--菜品信息,选菜-->

        <DataGrid Grid.Row="1" ItemsSource="{Binding DishMenu}" AutoGenerateColumns="False" GridLinesVisibility="All" CanUserDeleteRows="False" CanUserAddRows="False" >

            <DataGrid.Columns>

                <!-- 这4个来自(ViewModel )Dish属性,UI上一次读出,不会变-->

                <DataGridTextColumn Header="菜名"  Binding="{Binding Dish.Name}" Width="120"/>

                <DataGridTextColumn Header="种类"  Binding="{Binding Dish.Category}" Width="120"/>

                <DataGridTextColumn Header="点评"  Binding="{Binding Dish.Comment}" Width="120"/>

                <DataGridTextColumn Header="推荐指数"  Binding="{Binding Dish.Score}" Width="120"/>

                <!--注意这个属性-->

                <DataGridTemplateColumn Header="选中" SortMemberPath="IsSelected" Width="120">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate >

                            <CheckBox IsChecked="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged}"

                                      VerticalAlignment="Center" HorizontalAlignment="Center" 

                                      Command="{Binding Path=DataContext.SelectMenuItemCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}"/>

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

            </DataGrid.Columns>

        </DataGrid>

        <!--所点菜品个数,点菜-->

        <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" >

            <TextBlock Text="点了几个菜?" TextAlignment="Center"  />

            <TextBox IsReadOnly="True" Text="{Binding Count}" Width="120" TextAlignment="Center" />

            <Button Content="点菜" Command="{Binding PlaceOrderCommand}"/>

        </StackPanel>

    </Grid>

</Window>
using System.Windows;

using WpfPrism.ViewModels;



namespace WpfPrism

{

    /// <summary>

    /// MainWindow.xaml 的交互逻辑

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();



            this.DataContext = new MianWindowViewModel();

        }

    }

}

程序运行如下:

使用Prism提供的类实现WPF MVVM点餐Demo

可以在D盘找到如下的txt文件:

使用Prism提供的类实现WPF MVVM点餐Demo

也请参考CodeProject:WPF Master Details MVVM Application

说明:本文使用Prism框架中的几个类,来简化MVVM的编写。并未所见标准Prism的Bootstrapper、Shell、Region、Module、Unity/MEF...

关于Prism框架的知识,请关注DebugLZQ后续博文:

Prism框架-Hello Prism Using Unity

Hello Prism Using MEF

没什么高端的知识,老鸟绕过轻拍~

希望对你有帮助~

 

你可能感兴趣的:(demo)