WPF MVVM

news2025/7/11 14:54:15

WPF MVVM

MVVM=Model+View+ViewModel

  • Model:现实世界中对象抽象的结果,也就是实体模型
  • View:UI界面
  • ViewModel:为UI界面服务的模型,可以理解为数据传输对象(DTO)
    ViewModel和View的沟通有两个方面:数据和操作
  • 传递数据–使用数据属性
  • 传递操作–使用命令属性
    在这里插入图片描述

案例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)
    {
        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

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

<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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17998.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“综合”web项目编写------手把手0基础教学(二)

上一节介绍了编写综合项目的基本流程 “综合”web项目编写------手把手0基础教学&#xff08;一&#xff09; 这里将继续介绍项目的编写&#xff0c;一个一个功能挨个实现。 目录 实现用户的登录功能 一 . 编写工具包---封装数据库链接 二 . 构建数据模型 三 . 构建功能…

ASEMI代理艾赛斯IGBT管IXYB82N120C3H1

编辑-Z 艾赛斯IGBT管IXYB82N120C3H1参数&#xff1a; 型号&#xff1a;IXYB82N120C3H1 漏极-源极电压&#xff08;VCES&#xff09;&#xff1a;1200V 连续漏电流&#xff08;IC&#xff09;&#xff1a;82A 功耗&#xff08;PC&#xff09;&#xff1a;1040W 工作结温度…

spring cloud在bootstrap.properties配置spring.profiles.active无效

bootstrap.properties 配置 bootstrap.properties spring.profiles.activeprofiles.active bootstrap-dev.properties / bootstrap-test.properties #服务器地址 spring.cloud.nacos.config.server-addr127.0.0.1:8848 #项目的命名空间的ID spring.cloud.nacos.config.name…

m基于FPGA和MATLAB的数字CIC滤波器设计和实现

目录 1.算法概述 2.仿真效果预览 3.MATLAB/FPGA部分代码预览 4.完整MATLAB/FPGA程序 1.算法概述 CIC滤波器由一对或多对积分-梳状滤波器组成&#xff0c;在抽取CIC中&#xff0c;输入信号依次经过积分&#xff0c;降采样&#xff0c;以及与积分环节数目相同的梳状滤波器。在…

Facebook账号复审的问题。

Facebook账号复审主要指的就是&#xff1a; Facebook个人广告账号&#xff0c;或者是你的企业bn广告系统当中的 Facebook投手号。在投放广告的过程当中&#xff0c;因为违规。或者其他原因。 Facebook需要审核这个Facebook账号的主体。简单的说就是他的合法性。 所以会要求你。…

基于Peer-Group(对等体组)的BGP和 BGP水平分割路由反射器

目录 一、简介 二、基于Peer-Group的IBGP配置 三、基于Peer-Group的EBGP配置 四、IBGP的水平分割与路由反射器 1.水平分割概念 2.现象 【1】R3收到的路由信息如下&#xff1a; 【2】R2收到的路由信息如下&#xff1a; 这就是IBGP水平分割导致的现象&#xff01; 五、…

策略验证_卖出口诀_三种图线交叉点卖出股票需抢先

写在前面&#xff1a; 1. 本文中提到的“股票策略校验工具”的具体使用操作请查看该博文&#xff1b; 2. 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 3. 本系列文章是用来学习技法&#xff0c;文中所得内容都仅仅只是作为演示功能使用 目录 解说 策略代码 结果 解…

m低信噪比下GPS信号的捕获算法研究,分别使用matlab和FPGA对算法进行仿真和硬件实现

目录 1.算法概述 2.仿真效果预览 3.MATLAB/FPGA部分代码预览 4.完整MATLAB/FPGA程序 1.算法概述 GPS卫星发送的信号一般由3个分量组成&#xff1a;载波、伪码和导航电文&#xff0c;其中伪码和导航电文采用BPSK技术去调制载波。GPS使用的两个L波段两种载频&#xff1a; 为了…

react学习随笔

创建react脚手架项目 方法一&#xff1a;在安装好nodejs的情况下 npm install -g create-react-app 全局安装react脚手架 create-react-app 项目名 创建一个新的react项目 方法二&#xff1a;直接安装 npx create-react-app 项目名 npx create-react-app 项目名 --template ty…

org.activiti.engine

org.activiti.engine目录概述需求&#xff1a;设计思路实现思路分析1.TaskService2.RuntimeService3.ProcessEngines4.ProcessEngineLifecycleListener5.ProcessEngines参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,…

详解Unity中的新版Nav Mesh|导航寻路系统 (三)

前言 通过前面两篇的介绍与讲解我们已经对新版NavMesh有了一个基础的了解和使用&#xff0c;本篇我们继续讲解剩余的两个组件NavMeshModifier、NavMeshModifierVolume&#xff0c;这两个组件是主要是用于影响导航网格的生成的。所以内容不是很主要&#xff0c;但也非常重要。 …

LQ0209 颠倒的价牌【枚举+进制】

题目来源&#xff1a;蓝桥杯2013初赛 C A组D题 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小李的店里专卖其它店中下架的样品电视机&#xff0c;可称为&#xff1a;样品电视专卖店。 其标价都是 4 位数字&am…

Pulsar bundle数目的选择

看到今年Pulsar 峰会上挺多人分享负载均衡的内容&#xff0c;这里也整理分享一下相关的内容。 实践中&#xff0c;我们都会 关闭 auto bundle split&#xff0c;保证系统稳定 Pulsar bundle split 是一个比较耗费资源的操作&#xff0c;会造成连接到这个 bundle 上的所有 pr…

SAP 物料分类账配置详解Part 1( 基于SAP S/4HANA1909 版本)

本文将详细介绍&#xff1a;SAP 物料分类账配置要点 &#xff0c;本系统使用的版本是&#xff1a; SAP S/4HANA1909 版本目录 1 物料分类账的后台配置 1.1 激活物料分类帐的评估区域 1.2 分配货币类型并定义物料分类帐类型 1.3 分配物料分类帐类型给评估范围 1.5 定义…

m基于MATLAB的通信系统仿真,包括信号源,载波信号,放大器,带宽滤波器,接收端包括放大器,带宽滤波器,载波解调,低通滤波器等

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 Interference : 200KHz Signal source: 需要在给出的一个excel 文档里调用&#xff0c;我对应的信号是第二竖栏&#xff0c;就是从B1到B60 里面所有的filter(滤波器)都是自己来选值&am…

驱动 私有数据传参点灯

1.在串口工具进行输入&#xff1a; echo 1 > /dev/myled0 ---->led1灯点亮 echo 0 > /dev/myled0 ---->led1灯熄灭 echo 1 > /dev/myled1 ---->led1灯点亮 echo 0 > /dev/myled1 ---->led1灯熄灭 echo 1 > /dev/myled2 ----…

243 h160 相交链表

题解 本题关键: acbbca // 243 h160 相交链表public ListNode getIntersectionNode(ListNode headA, ListNode headB) {ListNode pointAheadA,pointBheadB;int rount2;while (rount>0){if (pointApointB){return pointA;}pointApointA.next;pointBpointB.next;if (pointAnul…

【树莓派不吃灰】命令篇⑥ 了解树莓派Boot分区,学习Linux启动流程

目录1. Linux启动过程1.1 内核引导1.2 运行init初始化进程 —— 初始化系统环境1.3 运行级别 —— runlevel1.4 系统初始化 —— 加载开机启动程序1.5 用户登录1.5.1 方式1&#xff1a;命令行登录1.5.2 方式2&#xff1a;ssh登录&#xff08;常用&#xff09;1.5.3 方式3&#…

[附源码]计算机毕业设计JAVA恒星学院网络计费系统

[附源码]计算机毕业设计JAVA恒星学院网络计费系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

Lingo软硬件划分 实例

文章目录一、SM2 加密算法软硬件划分1.1 实验目标1.2 实验过程&#xff08;1&#xff09; 综合考虑使得系统整体性能最&#xff08;2&#xff09;只考虑硬面积&#xff0c;即系统硬件面积最小&#xff08;3&#xff09;只考虑功耗&#xff0c;即系统功耗最小&#xff08;4&…