• WPF MVVM


    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; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    该案例只有一个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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    主界面

    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    由于Binding不指定Source默认使用了控件的DataContext,所以只需要在MainWindow中增加DataContext,子代控件便可以共享这个DataContext。

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    案例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; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 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");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    MainWindowViewModel

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    xaml

    <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>
                            
                            <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  • 相关阅读:
    DirectX 3D C++ 圆柱体的渲染(源代码)
    golang 对不同结构体中数据进行相互转换的几种常用方法
    .net core MVC不用端口号访问
    下载工程resources目录下的模板excel文件
    过滤表filter达式cql相互转化
    js返回的promise对象有值,为什么返回里面的值就为““了呢
    HTML设计一个简单的奥迪RS汽车主题网站( web网页制作期末大作业)
    07 Linux补充|秋招刷题|9月6日
    京东二面:Sychronized的锁升级过程是怎样的
    0.前期准备-后台管理系统
  • 原文地址:https://blog.csdn.net/weixin_44064908/article/details/127924489