WPF MVVM
MVVM=Model+View+ViewModel
- Model:现实世界中对象抽象的结果,也就是实体模型
- View:UI界面
- ViewModel:为UI界面服务的模型,可以理解为数据传输对象(DTO)
ViewModel和View的沟通有两个方面:数据和操作 - 传递数据–使用数据属性
- 传递操作–使用命令属性
很多人不理解MVVM和MVC的区别,我个人的理解是,MVC中的C可控范围更大,不仅可以控制View也能控制Model。而MVVM中,View是主动从ViewModel中获取数据,如果获取不到也不会导致程序崩溃,虽然VIewModel也可以去操作View,但是原则是View层主动获取数据,ViewModel是被动的提供数据。
案例1:
关注点:NotificationObject与数据属性
DelegateCommand与命令属性
命令的传递(单向)
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<object> ExecuteAction { get; set; }
public Func<object, bool> CanExecuteFunc { get; set; }
}
ViewModels
//通知Binding属性更新,供ViewModels使用的类
class NotificationObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
//也可以这样声明,自动获取方法调用方的方法名称或属性名称
//public void RaiseChanged([CallerMemberName]string proName=null)
if (this.PropertyChanged != null)
{
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
该案例只有一个MainWindow,所以创建一个MainWindowViewModel类
class MainWindowViewModel : NotificationObject
{
private double input1;
public double Input1
{
get { return input1; }
set
{
input1 = value;
this.RaisePropertyChanged("Input1");
}
}
private double input2;
public double Input2
{
get { return input2; }
set
{
input2 = value;
this.RaisePropertyChanged("Input2");
}
}
private double result;
public double Result
{
get { return result; }
set
{
result = value;
this.RaisePropertyChanged("Result");
}
}
public DelegateCommand AddCommand { get; set; }
public DelegateCommand SaveCommand { get; set; }
private void Add(object parameter)
{
this.Result = this.Input1 + this.Input2;
}
private void Save(object parameter)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.ShowDialog();
}
public MainWindowViewModel()
{
this.AddCommand = new DelegateCommand();
this.AddCommand.ExecuteAction = new Action<object>(this.Add);
this.SaveCommand = new DelegateCommand();
this.SaveCommand.ExecuteAction = new Action<object>(this.Save);
}
}
主界面
<Window x:Class="SimpleMvvmDemo.Client.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="_File">
<MenuItem Header="_Save" Command="{Binding SaveCommand}" />
</MenuItem>
</Menu>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Slider x:Name="slider1" Grid.Row="0" Background="LightBlue" Minimum="-100" Maximum="100" Margin="4" Value="{Binding Input1}" />
<Slider x:Name="slider2" Grid.Row="1" Background="LightBlue" Minimum="-100" Maximum="100" Margin="4" Value="{Binding Input2}" />
<Slider x:Name="slider3" Grid.Row="2" Background="LightBlue" Minimum="-100" Maximum="100" Margin="4" Value="{Binding Result}" />
<Button x:Name="addButton" Grid.Row="3" Content="Add" Width="120" Height="80" Command="{Binding AddCommand}"/>
</Grid>
</Grid>
</Window>
由于Binding不指定Source默认使用了控件的DataContext,所以只需要在MainWindow中增加DataContext,子代控件便可以共享这个DataContext。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
案例2:
项目使用了Microsoft.Practices.Prism,需要提前引用
- Models
要显示单品和饭店信息,所以定义Dish和Restaurant两个类
public class Dish : NotificationObject
{
public string Name { get; set; }
public string Category { get; set; }
public string Comment { get; set; }
public double Score { get; set; }
}
class Restaurant : NotificationObject
{
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
- Services
具有获得菜单和点单服务,先定义相关接口,再实现类
public interface IDataService
{
List<Dish> GetAllDishes();
}
class XmlDataService : IDataService
{
public List<Dish> GetAllDishes()
{
List<Dish> dishList = new List<Dish>();
string xmlFileName = System.IO.Path.Combine(Environment.CurrentDirectory, @"Data\Data.xml");
XDocument xDoc = XDocument.Load(xmlFileName);
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 = double.Parse(d.Element("Score").Value);
dishList.Add(dish);
}
return dishList;
}
}
interface IOrderService
{
void PlaceOrder(List<string> dishes);
}
class MockOrderService : IOrderService
{
public void PlaceOrder(List<string> dishes)
{
System.IO.File.WriteAllLines(@"C:\Users\cni23287938\Desktop\orders.txt", dishes.ToArray());
}
}
- ViewModels
因为菜单后面需要具有一个是否选择项,所以要新定义一个DishMenuItemViewModel类
class DishMenuItemViewModel : NotificationObject
{
public Dish Dish { get; set; }
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
this.RaisePropertyChanged("IsSelected");
}
}
}
MainWindowViewModel文章来源:https://www.toymoban.com/news/detail-603473.html
class MainWindowViewModel : NotificationObject
{
private int count;
public int Count
{
get { return count; }
set { count = value; this.RaisePropertyChanged("Count"); }
}
private Restaurant restaurant;
public Restaurant Restaurant
{
get { return restaurant; }
set { restaurant = value; this.RaisePropertyChanged("restaurant"); }
}
private List<DishMenuItemViewModel> dishMenu;
public List<DishMenuItemViewModel> DishMenu
{
get { return dishMenu; }
set { dishMenu = value; this.RaisePropertyChanged("DishMenu"); }
}
public DelegateCommand PlaceOrderCommand { get; set; }
public DelegateCommand SelectMenuItemCommand { get; set; }
public MainWindowViewModel()
{
LoadRestaurant();
LoadDishMenu();
PlaceOrderCommand = new DelegateCommand(this.PlaceOrderCommandExecute);
SelectMenuItemCommand = new DelegateCommand(this.SelectMenuItemExecute);
}
private void LoadRestaurant()
{
this.Restaurant = new Restaurant();
this.Restaurant.Name = "Crazy";
this.restaurant.Address = "北京";
this.restaurant.PhoneNumber = "1";
}
private void LoadDishMenu()
{
IDataService ds = new XmlDataService();
var dishes = ds.GetAllDishes();
this.dishMenu = new List<DishMenuItemViewModel>();
foreach (var dish in dishes)
{
DishMenuItemViewModel item = new DishMenuItemViewModel();
item.Dish = dish;
this.dishMenu.Add(item);
}
}
private void PlaceOrderCommandExecute()
{
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()
{
Count = DishMenu.Count(i => i.IsSelected == true);
}
}
xaml文章来源地址https://www.toymoban.com/news/detail-603473.html
<Window x:Class="WpfApp8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp8"
mc:Ignorable="d"
Title="{Binding Restaurant.Name}" Height="600" Width="800" WindowStartupLocation="CenterScreen">
<Grid x:Name="Root" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="欢迎"/>
<TextBlock Text="{Binding Restaurant.Name}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="电话"/>
<TextBlock Text="{Binding Restaurant.PhoneNumber}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="地址"/>
<TextBlock Text="{Binding Restaurant.Address}"/>
</StackPanel>
</StackPanel>
<DataGrid AutoGenerateColumns="False" CanUserDeleteRows="False" CanUserAddRows="False" Grid.Row="1" ItemsSource="{Binding DishMenu}">
<DataGrid.Columns>
<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>
<!--要获取每列数据,为每条数据赋值Command-->
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected,UpdateSourceTrigger=PropertyChanged}" Command="{Binding Path=DataContext.SelectMenuItemCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type DataGrid}}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2">
<TextBlock Text="共计:"/>
<TextBox IsReadOnly="True" Text="{Binding Count}"/>
<Button Content="Order" Command="{Binding PlaceOrderCommand}"/>
</StackPanel>
</Grid>
</Window>
到了这里,关于WPF MVVM的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!