WPF——初识MVVM(二)

说明:该案例与前一个案例的来源一样,同样讲述的是MVVM,算是MVVM的进阶吧。在该案例中,涉及到数据访问,Prism等更为复杂的问题。首先来看界面设计,我在作者的基础上稍微做了改动,大致如下:

 

1.首先还是来一个解决方案的截图:

2.实现步骤

2.1)从OO的角度来说,首先是完成基类的书写,该案例主要有两个对象,是Dish类(菜品类)和Restaurant(餐馆类),实现起来都较为简单,具体如下:

Restaurant Class
 1  public class Restaurant
 2     {
 3         /// <summary>
 4         /// 餐馆名称
 5         /// </summary>
 6         public string Name
 7         {
 8             get;
 9             set;
10         }
11 
12         /// <summary>
13         /// 餐馆地址
14         /// </summary>
15         public string Address
16         {
17             get;
18             set;
19         }
20 
21         /// <summary>
22         /// 订餐电话
23         /// </summary>
24         public string PhoneNumber
25         {
26             get;
27             set;
28         }
29     }

 

Dish Class Code
 1 /// <summary>
 2     ///菜品类
 3     /// </summary>
 4     class Dish
 5     {
 6         //1.Data Property
 7         /// <summary>
 8         /// 菜品名称
 9         /// </summary>
10         public string Name { get; set; }
11         /// <summary>
12         /// 菜品类别
13         /// </summary>
14         public string Category { get; set; }
15         /// <summary>
16         /// 菜品评论
17         /// </summary>
18         public string Comment { get; set; }
19         /// <summary>
20         /// 菜品评分
21         /// </summary>
22         public double Score { get; set; }
23     }



2.2)接口的定义和实现

无论是从技术上还是从团队的角度来谈,使用接口的好处是显而易见的。这里做个简单的架设,该项目中的数据源是xml,如果涉及到不同的数据源就可以用不同的方法来实现接口来获取数据。

首先定义接口IDataService和IOrderService:

1 interface IDataService
2     {
3        List<Dish> GetAllDishes();
4     }

 

IOrderService
1  interface IOrderService
2     {
3         void PlaceOrder(List<string> dishes);
4     }


接口的实现:

XmlDataService
 1  class XmlDataService :IDataService
 2     {
 3         public List<Dish> GetAllDishes()
 4         {
 5             List<Dish> dishList = new List<Dish>();
 6             //System.IO.Path.Combine将两个字符串组合成一个路径(http://msdn.microsoft.com/zh-cn/library/z8te35sa(v=vs.80).aspx)
 7             string xmlFileName = System.IO.Path.Combine(Environment.CurrentDirectory, @"Data\Data.xml");
 8             //XDocument.Load(string uri)
 9             XDocument xDoc = XDocument.Load(xmlFileName);
10             //按文档顺序返回此文档或元素的经过筛选的子代元素集合。集合中只包括具有匹配 XName 的元素。
11             //XName即Data.xml中的 <Dish></Dish>节点
12             var dishes = xDoc.Descendants("Dish");
13             //IEnumerable dishes = xDoc.Descendants("Dish");
14 
15             foreach (var d in dishes)
16             {
17                 Dish dish = new Dish();
18                 dish.Name = d.Element("Name").Value;
19                 dish.Category = d.Element("Category").Value;
20                 dish.Comment = d.Element("Comment").Value;
21                 dish.Score = double.Parse(d.Element("Score").Value);
22                 dishList.Add(dish);
23             }
24             return dishList;
25         }
26         
27     }

 

MockOrderService
1  class MockOrderService :IOrderService
2     {
3         public void PlaceOrder(List<string> dishes)
4         {
5             System.IO.File.WriteAllLines(@"C:\order.txt", dishes.ToArray());
6         }
7     }

说明:该XmlDataService的作用是从xml文件中读取数据,MockOrderService则是简单的将订单信息存储在本地磁盘。

2.3)主要的业务逻辑实现

这里涉及到了Prism,Prism已经为我们实现了INotifyPropertyChangedICommand接口,我们只需添加引用集,添加命名空间和使相关类继承NotificationObject。

 1  using Microsoft.Practices.Prism.ViewModel;
 2  using MVVMDemo2.Models;
 3 
 4 class DishMenuItemViewModel : NotificationObject
 5     {
 6         public Dish Dish { get; set; }//(有一个关系)
 7 
 8         private bool isSelected;
 9 
10         public bool IsSelected
11         {
12             get { return isSelected; }
13             set
14             {
15                 isSelected = value;
16                 this.RaisePropertyChanged("IsSelected");
17             }
18         }
19 
20     }

 

在引用了Microsoft.Practices.Prism.ViewModel命名空间,并继承NotificationObject后就可以使用RasisePropertyChanged()方法了,NotificationObject实际上派生自INotifyPropertyChanged。

 

 

  1 using Microsoft.Practices.Prism.ViewModel;
  2 using Microsoft.Practices.Prism.Commands;
  3 using MVVMDemo2.Services;
  4 using System.Windows;
  5 
  6 namespace MVVMDemo2.ViewModels
  7 {
  8     class MainWindowViewModel : NotificationObject
  9     {
 10         #region 定义命令属性和数据属性
 11         //Command property
 12         public DelegateCommand PlaceOrderCommand { get; set; }
 13         public DelegateCommand SelectMenuItemCommand { get; set; }
 14 
 15         //Data Property
 16         /// <summary>
 17         /// 选择个数
 18         /// </summary>
 19         private int count;
 20 
 21         public int Count
 22         {
 23             get { return count; }
 24             set
 25             {
 26                 count = value;
 27                 //若不继承NotificationObject,这里将会报错
 28                 this.RaisePropertyChanged("Count");
 29             }
 30         }
 31 
 32         /// <summary>
 33         /// 餐馆信息
 34         /// </summary>
 35         private Restaurant restaurant;
 36 
 37         public Restaurant Restaurant
 38         {
 39             get { return restaurant; }
 40             set
 41             {
 42                 restaurant = value;
 43                 this.RaisePropertyChanged("Restaurant");
 44             }
 45         }
 46 
 47         /// <summary>
 48         /// 餐馆菜品
 49         /// </summary>
 50         private List<DishMenuItemViewModel> dishMenu;
 51 
 52         public List<DishMenuItemViewModel> DishMenu
 53         {
 54             get { return dishMenu; }
 55             set
 56             {
 57                 dishMenu = value;
 58                 this.RaisePropertyChanged("DishMenu");
 59             }
 60         }
 61         #endregion
 62 
 63 
 64         public MainWindowViewModel()
 65         {
 66             this.LoadRestaurant();
 67             this.LoadDishMenu();
 68 
 69             this.PlaceOrderCommand = new DelegateCommand(new Action(this.PlaceOrderCommandExecute));
 70             this.SelectMenuItemCommand = new DelegateCommand(new Action(this.SelectMenuItemExecute));
 71 
 72         }
 73 
 74         /// <summary>
 75         /// 加载餐馆信息
 76         /// </summary>
 77         private void LoadRestaurant()
 78         {
 79             this.Restaurant = new Restaurant();
 80 
 81             this.Restaurant.Name = "乔治伊诺";
 82             this.Restaurant.Address = "小花园立交桥西口233号";
 83             this.Restaurant.PhoneNumber = "0932313520";
 84 
 85         }
 86 
 87         /// <summary>
 88         /// 加载菜品信息
 89         /// </summary>
 90         private void LoadDishMenu()
 91         {
 92             IDataService ds = new XmlDataService();
 93             var dishes = ds.GetAllDishes();
 94             this.DishMenu = new List<DishMenuItemViewModel>();
 95             foreach (var dish in dishes)
 96             {
 97                 DishMenuItemViewModel item = new DishMenuItemViewModel();
 98                 item.Dish = dish;
 99                 this.DishMenu.Add(item );
100                    
101             }
102 
103         }
104 
105         /// <summary>
106         /// 执行订餐操作
107         /// </summary>
108         private void PlaceOrderCommandExecute()
109         {
110             var selectedDishes = this.DishMenu.Where(i => i.IsSelected == true).Select(i => i.Dish.Name).ToList();
111            
112             IOrderService orderService = new MockOrderService();
113             orderService.PlaceOrder(selectedDishes);
114             //var unSelected = this.DishMenu.Where(i => i.IsSelected == false).Select(i => i.Dish.Name).ToList();
115             // orderService.PlaceOrder(unSelected);
116             MessageBox.Show("订餐成功!");
117         }
118 
119         /// <summary>
120         /// 执行操作
121         /// </summary>
122         private void SelectMenuItemExecute()
123         {
124             this.Count = this.DishMenu.Count(i=>i.IsSelected==true);
125         }
126 
127     }
128 }

说明:添加了 Microsoft.Practices.Prism.Commands命名空间,就可以使用DelegateCommand,如上代码所示。其继承层次结构为:DelegateCommand>DelegateCommandBae>ICommand, IActiveAware。

 

最后通过在CS中添加 this.DataContext = new MainWindowViewModel();并在XAML代码中进行绑定即可:

 

View XAML
 1 <Window x:Class="MVVMDemo2.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="{Binding Restaruant.Name,StringFormat=\{0\}-在线订餐}"  Height="600" Width="1000" WindowStartupLocation="CenterScreen">
 5     <Border BorderThickness="10" Name="borderWrap" Background="Orange" BorderBrush="GreenYellow" CornerRadius="10">
 6         <Border BorderBrush="Orange" BorderThickness="3" CornerRadius="6" Background="Yellow">
 7             <Grid x:Name="Root" Margin="4">
 8                 <Grid.RowDefinitions>
 9                     <RowDefinition Height="Auto" />
10                     <RowDefinition Height="*" />
11                     <RowDefinition Height="Auto" />
12                 </Grid.RowDefinitions>
13                 <Border BorderBrush="Orange" BorderThickness="2" CornerRadius="6" Padding="4">
14                     <StackPanel>
15                         <StackPanel Orientation="Horizontal">
16                             <StackPanel.Effect>
17                                 <DropShadowEffect Color="LightGray" />
18                             </StackPanel.Effect>
19                             <TextBlock Text="欢迎光临-" FontSize="60" FontFamily="KaiTi" />
20                             <TextBlock Text="{Binding Restaurant.Name}" FontSize="60" FontFamily="LiShu" />
21                         </StackPanel>
22                         <StackPanel Orientation="Horizontal">
23                             <TextBlock Text="餐厅地址:" FontSize="24" FontFamily="LiShu" />
24                             <TextBlock Text="{Binding Restaurant.Address}" FontSize="24" FontFamily="LiShu" />
25                         </StackPanel>
26                         <StackPanel Orientation="Horizontal">
27                             <TextBlock Text="订餐电话:" FontSize="24" FontFamily="LiShu" />
28                             <TextBlock Text="{Binding Restaurant.PhoneNumber}" FontSize="24" FontFamily="LiShu" />
29                         </StackPanel>
30                     </StackPanel>
31                 </Border>
32                 <DataGrid AutoGenerateColumns="False" GridLinesVisibility="None" CanUserDeleteRows="False"
33                     CanUserAddRows="False" Margin="0,4" Grid.Row="1" FontSize="16" ItemsSource="{Binding DishMenu}">
34                     <DataGrid.Columns>
35                         <DataGridTextColumn Header="菜品" Binding="{Binding Dish.Name}" Width="120" />
36                         <DataGridTextColumn Header="种类" Binding="{Binding Dish.Category}" Width="120" />
37                         <DataGridTextColumn Header="点评" Binding="{Binding Dish.Comment}" Width="120" />
38                         <DataGridTextColumn Header="推荐分数" Binding="{Binding Dish.Score}" Width="120" />
39                         <DataGridTemplateColumn Header="选中" SortMemberPath="IsSelected" Width="120">
40                             <DataGridTemplateColumn.CellTemplate>
41                                 <DataTemplate>
42                                     <CheckBox IsChecked="{Binding Path=IsSelected,  UpdateSourceTrigger=PropertyChanged}"
43                                         VerticalAlignment="Center" HorizontalAlignment="Center"
44                                         Command="{Binding Path=DataContext.SelectMenuItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}" />
45                                 </DataTemplate>
46                             </DataGridTemplateColumn.CellTemplate>
47                         </DataGridTemplateColumn>
48                     </DataGrid.Columns>
49                 </DataGrid>
50                 <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="2">
51                     <TextBlock Text="共计" VerticalAlignment="Center" />
52                     <TextBox IsReadOnly="True" TextAlignment="Center" Width="120" Text="{Binding Count}" Margin="4,0" />
53                     <Button Content="我要订餐" Height="24" Width="120" Command="{Binding PlaceOrderCommand}" />
54                 </StackPanel>
55             </Grid>
56         </Border>
57     </Border>
58 </Window>


Form:深入浅出WPF MVVM入门与提高(刘铁锰 wpfgeek@live.com)

posted @ 2012-04-10 11:32  January  阅读(2525)  评论(1编辑  收藏  举报