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


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

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

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

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

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

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

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

    下面来实现它:

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

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

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

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

     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();
            }
        }
    }

    程序运行如下:

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

    也请参考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

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

    希望对你有帮助~

  • 相关阅读:
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    svn 启动项目报错,项目被lock
    BigDecimal 用法详解
    MySQL 规范
    Tomcat 详解URL请求
    Tomcat Servlet工作原理
    Tomcat Context容器和Wrapper容器
    Tomcat 核心组件 Container容器相关
    Tomcat 核心组件 Connector
  • 原文地址:https://www.cnblogs.com/DebugLZQ/p/2821772.html
Copyright © 2020-2023  润新知