WPF学习——基于Mvvm模式下的餐馆订餐系统小示例
订餐小系统的界面主要内容为餐馆信息显示,菜品表格呈现。选择栏中按钮进行菜品的选择,可以对数量进行加减,并将订单信息实时显示在预购清单中。最后点击下单按钮,将订单信息保存到硬盘orders.txt文本中。界面呈现画面如下图所示。

- 数据库信息,我用的SQLServer。表格如下图所示。
![]()
![]()
- 新建Models文件夹,添加Dish与Restaurant类。
class Dish { public int Id { get; set; } public string Name { get; set; } public string Catagory { get; set; } public string Comment { get; set; } public double Score { get; set; } public int Count { get; set; } }
class Restaurant { public string Name { get; set; } public string Address { get; set; } public string PhoneNumber { get; set; } }
- 新增Db文件夹,添加localDb类。
class localDb { public localDb() { } private List<Dish> Dishes; private List<Restaurant> Restaurants; public List<Dish> GetDishes() { Dishes = new List<Dish>(); string connString = Properties.Settings.Default.Database; string sql = "SELECT [Id],[Name],[Catagory],[Comment],[Score],[Count] FROM [test].[dbo].[Dish]"; using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { Dish dish = new Dish() {Id=(int)reader["Id"], Name=(string)reader["Name"],Catagory=(string)reader["Catagory"], Comment=(string)reader["Comment"],Score=(double)reader["Score"],Count=(int)reader["Count"]}; Dishes.Add(dish); } return Dishes; } } public List<Restaurant> GetRestaurants() { Restaurants = new List<Restaurant>(); string connString = Properties.Settings.Default.Database; string sql = "SELECT TOP 1 [Name],[Address],[PhoneNumber] FROM [test].[dbo].[Restaurant]"; using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { Restaurant restaurant = new Restaurant() { Name = (string)reader["Name"], Address = (string)reader["Address"], PhoneNumber = (string)reader["PhoneNumber"] }; Restaurants.Add(restaurant); } return Restaurants; } } public string GetNameById(int? id) { localDb localDb = new localDb(); string name = localDb.GetDishes().FirstOrDefault(t => t.Id == id).Name; return name; } public void CountAddOne(int? id) { string connString = Properties.Settings.Default.Database; string sql = "update test.dbo.Dish set [Count]=@count where Id=@id"; localDb localDb = new localDb(); List<Dish> dishes = localDb.GetDishes(); var count = dishes.FirstOrDefault(t => t.Id == id).Count+1; using (SqlConnection conn=new SqlConnection(connString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.Add(new SqlParameter("@count",count)); cmd.Parameters.Add(new SqlParameter("@id",id)); cmd.ExecuteNonQuery(); } } public void CountSubOne(int? id) { string connString = Properties.Settings.Default.Database; string sql = "update test.dbo.Dish set [Count]=@count where Id=@id"; localDb localDb = new localDb(); List<Dish> dishes = localDb.GetDishes(); var count = dishes.FirstOrDefault(t => t.Id == id).Count - 1; if (count >= 0) { using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.Add(new SqlParameter("@count", count)); cmd.Parameters.Add(new SqlParameter("@id", id)); cmd.ExecuteNonQuery(); } } } public void SetCountZero() { string connString = Properties.Settings.Default.Database; string sql = "update test.dbo.Dish set [Count]=0"; using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sql, conn); cmd.ExecuteNonQuery(); } } }
localDb类中主要有6个方法:
GetDishes方法,用来获取数据库中Dish表内所有数据,返回一个List<Dish>。
GetRestaurant方法,用来获取数据库中Restaurant表内所有数据,返回一个List<Restaurant>。
GetNameById方法,根据Id搜索数据库中Dish表内对应的Name。用于在选择点击“+1”“-1”按钮时,通过按钮绑定的Id获取相应菜品的Name,并且附加到预购清单文本框中。
CountAddOne方法,用于在选择点击“+1”按钮时,将数据库表内对应Count属性值加一。
CountSubOne方法,用于在选择点击“-1”按钮时,将数据库表内对应Count属性值减一。
SetCountZero方法,用于在点击“下单”按钮时,将数据库表内所有Count属性值置零。 - 新增ViewModels文件夹,添加MainWindowViewModel类。
class MainWindowViewModel:BindableBase { public MainWindowViewModel() { localDb = new localDb(); DishInfo = localDb.GetDishes(); RestaurantInfo = localDb.GetRestaurants(); Name = RestaurantInfo.FirstOrDefault().Name; Address = RestaurantInfo.FirstOrDefault().Address; Phone = RestaurantInfo.FirstOrDefault().PhoneNumber; AddOneCommand = new DelegateCommand<int?>(t => AddOne(t)); SubOneCommand = new DelegateCommand<int?>(t=>SubOne(t)); OrderFinishCommand = new DelegateCommand(OrderFinish); } localDb localDb; private List<Dish> dishInfo; public List<Dish> DishInfo { get { return dishInfo; } set { dishInfo = value; RaisePropertyChanged("DishInfo"); } } private List<Restaurant> restaurantInfo; public List<Restaurant> RestaurantInfo { get { return restaurantInfo; } set { restaurantInfo = value; RaisePropertyChanged("RestaurantInfo"); } } private string name; public string Name { get { return name; } set { name = value; RaisePropertyChanged("Name"); } } private string address; public string Address { get { return address; } set { address = value; RaisePropertyChanged("Address"); } } private string phone; public string Phone { get { return phone; } set { phone = value; RaisePropertyChanged("Phone"); } } private string orderText; public string OrderText { get { return orderText; } set { orderText = value; RaisePropertyChanged("OrderText"); } } public DelegateCommand<int?> SubOneCommand { get; set; } public DelegateCommand<int?> AddOneCommand { get; set; } public DelegateCommand OrderFinishCommand { get; set; } public void SubOne(int? id) { localDb.CountSubOne(id); DishInfo = localDb.GetDishes(); string name = localDb.GetNameById(id); int n = OrderText.IndexOf(name); if (n != -1) { string s = OrderText.Remove(n, name.Length); OrderText = s; } } public void AddOne(int? id) { localDb.CountAddOne(id); DishInfo = localDb.GetDishes(); OrderText += localDb.GetNameById(id); } public void OrderFinish() { localDb.SetCountZero(); DishInfo = localDb.GetDishes(); System.IO.File.WriteAllText(@"D:\orders.txt", OrderText); OrderText = string.Empty; }
MainWindowViewModel类中有6各属性:
DishInfo接受List<Dish>,绑定主界面DataGrid的ItemsSource。
RestaurantInfo接受List<Restaurant>,将内部值再分给Name、Address、Phone属性,分别绑定到主界面的TextBlock中。
OrderText接受选择的菜品名称。
3个命令:
SubOneCommand,绑定DataGrid中选择栏中的“-1”按钮的Command。
AddOneCommand,绑定DataGrid中选择栏中的“+1”按钮的Command。
OrderFinishCommand,绑定“下单”按钮的Command。
3个方法:
SubOne,与SubOneCommand关联。SubOneCommand = new DelegateCommand<int?>(t=>SubOne(t));AddOne,与AddOneCommand关联。AddOneCommand = new DelegateCommand<int?>(t => AddOne(t));
OrderFinish,与OrderFinishCommand关联。OrderFinishCommand = new DelegateCommand(OrderFinish); - MainWindow.xaml.cs中给DataContext赋值。
public MainWindow() { InitializeComponent(); this.DataContext = new MainWindowViewModel(); }
- MainWindow.xaml中创建主画面布局。
<Border Background="LightBlue"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="auto"/> <RowDefinition Height="2*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal"> <TextBlock Text="{Binding Name}" FontSize="40" FontStyle="Italic" Foreground="Green" FontWeight="Bold" Margin="10,10,0,10" VerticalAlignment="Center"/> <TextBlock Text="---欢迎您!" FontSize="40" FontStyle="Italic" Foreground="Red" FontWeight="Bold" Margin="0,10,10,10" VerticalAlignment="Center"/> </StackPanel> <StackPanel Grid.Row="1" Orientation="Horizontal"> <TextBlock Text="地址:" FontSize="30" Margin="5,5,0,5" FontWeight="Black" Foreground="Brown" VerticalAlignment="Center"/> <TextBlock Text="{Binding Address}" FontSize="30" FontWeight="Black" Margin="0,5,5,5" Foreground="Orange" VerticalAlignment="Center" /> </StackPanel> <StackPanel Grid.Row="2" Orientation="Horizontal"> <TextBlock Text="电话:" FontSize="30" Margin="5,5,0,5" FontWeight="Black" Foreground="Blue" VerticalAlignment="Center"/> <TextBlock Text="{Binding Phone}" FontSize="30" FontWeight="Black" Margin="0,5,5,5" Foreground="Red" VerticalAlignment="Center" /> </StackPanel> <DataGrid Grid.Row="3" ColumnWidth="*" AutoGenerateColumns="False" ItemsSource="{Binding DishInfo}" CanUserAddRows="False" FontSize="25" BorderBrush="Black"> <DataGrid.Columns> <DataGridTextColumn Header="序号" Binding="{Binding Id}"></DataGridTextColumn> <DataGridTextColumn Header="菜品名称" Binding="{Binding Name}"></DataGridTextColumn> <DataGridTextColumn Header="菜品种类" Binding="{Binding Catagory}"></DataGridTextColumn> <DataGridTextColumn Header="客户评价" Binding="{Binding Comment}"></DataGridTextColumn> <DataGridTextColumn Header="评分" Binding="{Binding Score}"></DataGridTextColumn> <DataGridTemplateColumn Header="选择" > <DataGridTemplateColumn.CellTemplate> <DataTemplate> <DockPanel LastChildFill="True"> <Button Content="-1" Width="40" CommandParameter="{Binding Id}" Command="{Binding DataContext.SubOneCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}" Background="Pink" Margin="5,5,0,5" DockPanel.Dock="Left"/> <Button Content="+1" Width="40" CommandParameter="{Binding Id}" Command="{Binding DataContext.AddOneCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}" Background="Pink" Margin="5" DockPanel.Dock="Right"/> <TextBox Margin="5,5,0,5" BorderBrush="Black" Text="{Binding Count}"/> </DockPanel> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> <Grid Grid.Row="4" Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="auto"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="auto"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="预购清单:" FontSize="30" Margin="5" /> <Button Grid.Row="0" Grid.Column="1" Content="下单" Width="100" Command="{Binding OrderFinishCommand}" FontSize="30" Margin="5" Height="45" Background="Blue" Foreground="Red"/> <TextBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" FontSize="20" TextWrapping="Wrap" BorderBrush="Black" Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Text="{Binding OrderText}"/> </Grid> </Grid> </Border>
我做的有点花里胡哨,肯定还是可以优化的。我觉得很关键的一点就是选择栏中的“+1”“-1”按钮的属性CommandParameter=“{Binding Id}”。
在我们触发SubOneCommand<int?>传进来的参数就是CommandParameter。 - 这样就大功告成了。代码方面应该还是会有一些小bug,一些愚蠢的操作网友也就见笑了,总之一起讨论进步。
源代码我就发百度网盘了。
链接:https://pan.baidu.com/s/1xwepaXFXZT-ziHB17z0-cQ
提取码:b1ft



浙公网安备 33010602011771号