【WPF】Prism学习(二)
Prism Commands

1.命令(Commanding)
1.1. ViewModel的作用:
- ViewModel不仅提供在视图中显示或编辑的数据,还可能定义一个或多个用户可以执行的动作或操作。
- 这些用户可以通过用户界面(UI)执行的动作或操作通常被定义为命令(Commands)。
1.2. 命令(Commands)的作用:
- 命令提供了一种方便的方式来表示可以轻松绑定到UI控件的动作或操作。
- 它们封装了实现动作或操作的实际代码,并有助于将其与视图中的实际视觉表示分离。
1.3. 命令的触发方式:
- 用户与视图交互时,可以通过多种方式视觉上表示和调用命令。
- 大多数情况下,命令是由鼠标点击触发的,但它们也可以由快捷键按下、触摸手势或其他输入事件触发。
- 视图中的控件与ViewModel的命令数据绑定,因此用户可以使用控件定义的任何输入事件或手势来调用它们。
- 视图中的UI控件与命令之间的交互可以是双向的,即命令可以在用户与UI交互时被调用,UI也可以根据底层命令的启用或禁用状态自动启用或禁用。
1.4. 命令对象的实现:
- ViewModel可以将命令实现为命令对象(一个实现了
ICommand接口的对象)。 - 视图与命令的交互可以声明性地定义,而不需要在视图的代码后台文件中编写复杂的事件处理代码。
- 例如,某些控件天生支持命令,并提供了一个可以与ViewModel提供的
ICommand对象数据绑定的Command属性。 - 在其他情况下,可以使用命令行为将控件与ViewModel提供的命令方法或命令对象关联起来。
1.5. ICommand接口的实现:
- 实现
ICommand接口是直接的。 - Prism框架提供了
DelegateCommand这个ICommand接口的实现,你可以在你的应用程序中直接使用它。
DelegateCommand可以在Prism.Core Nuget包的Prism.Commands命名空间中找到。
2.创建一个委托命令对象
如何在MVVM模式中使用
DelegateCommand来实现命令,以及如何将这些命令绑定到UI控件上。
2.1. DelegateCommand类封装了两个委托,这两个委托分别对应于ViewModel中的Execute和CanExecute方法。这意味着,当你在UI中触发一个命令时,DelegateCommand会调用ViewModel中相应的方法。
2.2. 你可以通过DelegateCommand类的构造函数来指定这些委托。例如,下面的代码示例展示了如何通过指定委托到ViewModel中的Submit和CanSubmit方法来构造一个代表提交命令的DelegateCommand实例。然后,这个命令通过一个只读属性暴露给视图,该属性返回对DelegateCommand的引用。
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand<object>(Submit, CanSubmit);}void Submit(object parameter){// 实现逻辑}bool CanSubmit(object parameter){return true;}
}
2.3. 当DelegateCommand对象的Execute方法被调用时,它会通过你在构造函数中指定的委托将调用转发到ViewModel类中的方法。同样,当CanExecute方法被调用时,会调用ViewModel类中的对应方法。CanExecute方法的委托在构造函数中是可选的。如果没有指定,DelegateCommand将始终返回true作为CanExecute的结果。
2.4. DelegateCommand类是一个泛型类型。类型参数指定了传递给Execute和CanExecute方法的命令参数的类型。在上面的例子中,命令参数是object类型。Prism还提供了一个非泛型的DelegateCommand类,用于不需要命令参数的情况,定义如下:
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit, CanSubmit);}void Submit(){// 实现逻辑}bool CanSubmit(){return true;}
}
DelegateCommand避免使用值类型作为参数,如果需要使用值类型参数时,应该如何处理以避免潜在的问题:
- DelegateCommand避免使用值类型:
DelegateCommand这个类在设计时故意不使用值类型(如int、double、bool等)作为参数类型。这是因为ICommand接口接受的是一个对象类型的参数。- 值类型参数可能导致意外行为:如果在XAML初始化时,命令绑定调用了
CanExecute(null),并且T是一个值类型,那么可能会导致一些意料之外的行为。这是因为值类型在默认情况下会被初始化为它们的默认值(如int的默认值是0),这可能会导致命令的执行逻辑出现问题。- 默认值问题:考虑过使用
default(T)作为解决方案,但最终被拒绝了。原因是实现者将无法区分一个有效的值和一个默认值。例如,如果T是int,那么default(int)是0,但实现者可能无法确定0是一个有效的参数值还是一个默认值。- 使用可空类型:如果你确实需要使用值类型作为参数,那么必须将其转换为可空类型(nullable)。这可以通过使用
DelegateCommand<Nullable<int>>或者使用简写形式?语法(DelegateCommand<int?>)来实现。这样,就可以区分一个有效的值和一个未赋值(null)的情况。
3.在视图中调用 ViewModel 提供的 DelegateCommands
3.1. 视图与命令对象的关联:在视图中,可以通过 Command 属性将控件与 ViewModel 提供的命令对象关联起来。这适用于 WPF、Xamarin.Forms 和 UWP 控件,它们可以通过数据绑定轻松地与命令对象关联。
3.2. 数据绑定示例:提供了一个按钮的示例代码,展示了如何将按钮的 Command 属性绑定到 ViewModel 中的 SubmitCommand 命令上。代码如下:
<Button Command="{Binding SubmitCommand}" CommandParameter="OrderId"/>
这里,{Binding SubmitCommand} 表示将按钮的 Command 属性绑定到 ViewModel 的 SubmitCommand 属性上,CommandParameter="OrderId" 表示传递一个参数 “OrderId” 给命令的 Execute 方法。
3.3. 命令参数:可以通过 CommandParameter 属性可选地定义一个命令参数。预期的参数类型在 DelegateCommand 的泛型声明中指定。当用户与控件交互时,控件会自动调用目标命令,如果提供了命令参数,它将作为参数传递给命令的 Execute 方法。
3.4. 自动调用命令:在上述示例中,当按钮被点击时,它会自动调用 ViewModel 中的 SubmitCommand。此外,如果指定了 CanExecute 委托,按钮会在 CanExecute 返回 false 时自动禁用,在返回 true 时启用。
4.向UI发送变化通知
4.1. RaiseCanExecuteChanged方法:
当你需要手动更新绑定到UI元素的状态时,可以使用这个方法。例如,当IsEnabled属性的值发生变化时,在属性的setter中调用RaiseCanExecuteChanged来通知UI状态变化。
private bool _isEnabled;
public bool IsEnabled
{get { return _isEnabled; }set{SetProperty(ref _isEnabled, value);SubmitCommand.RaiseCanExecuteChanged();}
}
4.2. ObservesProperty方法:
当命令需要在属性值变化时发送通知时,可以使用这个方法。使用ObservesProperty方法时,每当提供的属性值变化,DelegateCommand会自动调用RaiseCanExecuteChanged来通知UI状态变化。
public class ArticleViewModel : BindableBase
{private bool _isEnabled;public bool IsEnabled{get { return _isEnabled; }set { SetProperty(ref _isEnabled, value); }}public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit, CanSubmit).ObservesProperty(() => IsEnabled);}void Submit(){// 实现逻辑}bool CanSubmit(){return IsEnabled;}
}
注意:使用
ObservesProperty方法时,可以链式注册多个属性进行观察。例如:ObservesProperty(() => IsEnabled).ObservesProperty(() => CanSave)。
4.3. ObservesCanExecute方法:
如果你的CanExecute是简单布尔属性的结果,你可以消除声明CanExecute委托的需要,改用ObservesCanExecute方法。ObservesCanExecute不仅会在注册的属性值变化时向UI发送通知,还会使用同一个属性作为实际的CanExecute委托。
public class ArticleViewModel : BindableBase
{private bool _isEnabled;public bool IsEnabled{get { return _isEnabled; }set { SetProperty(ref _isEnabled, value); }}public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit).ObservesCanExecute(() => IsEnabled);}void Submit(){// implement logic}
}
警告:不要尝试链式注册
ObservesCanExecute方法。只能为CanExecute委托观察一个属性。
5.在命令中使用异步方法
在现代编程中,使用async和await调用异步方法是很常见的需求。虽然许多人的第一反应是需要一个AsyncCommand,但实际上这种假设是错误的。ICommand本质上是同步的,Execute和CanExecute委托应该被视为事件。这意味着使用async void作为命令的语法是完全有效的。文中提供了两种使用DelegateCommand与异步方法的方法。
选项 1:
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(Submit);}async void Submit(){await SomeAsyncMethod();}
}
在这个选项中,Submit方法被声明为async void,这意味着它可以包含await语句来调用异步方法。DelegateCommand在执行时会调用这个Submit方法。
选项 2:
public class ArticleViewModel
{public DelegateCommand SubmitCommand { get; private set; }public ArticleViewModel(){SubmitCommand = new DelegateCommand(async () => await Submit());}Task Submit(){return SomeAsyncMethod();}
}
在这个选项中,Submit方法被声明为返回Task的普通方法,而DelegateCommand的构造函数中使用了lambda表达式async () => await Submit()来包装这个方法调用。这样,当DelegateCommand被执行时,它会调用这个lambda表达式,该表达式会异步地调用Submit方法。
这两种方法都允许你在DelegateCommand中使用异步方法,同时保持ICommand的同步特性。选择哪种方法取决于你的具体需求和偏好。
相关链接
- 介绍(Introduction)
- 命令(Commands)
-
- 命令(Commanding)
-
- 复合命令(Composite Commands)
相关文章:
【WPF】Prism学习(二)
Prism Commands 1.命令(Commanding) 1.1. ViewModel的作用: ViewModel不仅提供在视图中显示或编辑的数据,还可能定义一个或多个用户可以执行的动作或操作。这些用户可以通过用户界面(UI)执行的动作或操作…...
【鸿蒙开发】第二十一章 Location位置服务
目录 1 简介 1.1 Location Kit简介 1.2 运作机制 1.3 约束与限制 2 位置服务开发 2.1 申请位置权限开发指导 2.1.1 场景概述 2.2 获取设备的位置信息开发指导 2.2.1 场景概述 2.2.2 接口说明 2.2.3 开发步骤 2.3(逆)地理编码转化开发指导 2.…...
《目标检测》R-CNN网络基础(RCNN,Fast-RCNN)
文章目录 1.Overfeat模型2.RCNN网络2.1 算法流程2.1.1 候选区域的生成(了解,已经不再使用了)2.1.2 CNN网络提取特征2.1.3 目标分类(SVM)2.1.4 目标回归(线性回归修正坐标)2.1.5 预测过程 2.2 算…...
iOS中的定位实现(逆地理编码)及Info.plist位置权限详解
引言 在现代移动应用开发中,位置服务已经成为不可或缺的一部分。无论是地图导航、社交分享,还是基于位置的个性化推荐,位置数据都为用户提供了更加丰富和智能的体验。然而,随着用户隐私保护的不断加强,iOS对位置权限的…...
【从零开始的LeetCode-算法】3270. 求出数字答案
给你三个 正 整数 num1 ,num2 和 num3 。 数字 num1 ,num2 和 num3 的数字答案 key 是一个四位数,定义如下: 一开始,如果有数字 少于 四位数,给它补 前导 0 。答案 key 的第 i 个数位(1 < …...
Web认证机制 Cookie、Token、Session、JWT、OAuth2 解析
标题 一、Cookie二、Session三、Token四、JWTSSO(单点登录) 五、OAuth2如何设计权限系统区别总结 Cookie、Token、Session 和 JWT 都是在 Web 开发中常用的身份验证和授权技术,它们各有优缺点,适用于不同的场景。 Cookie 简单易用…...
Docker 基础命令介绍和常见报错解决
介绍一些 docker 可能用到的基础命令,并解决三个常见报错: 权限被拒绝(Permission Denied)无法连接到 Docker 仓库(Timeout Exceeded)磁盘空间不足(No Space Left on Device) 命令以…...
如何轻松导出所有 WordPress URL 为纯文本格式
作为一名多年的 WordPress 使用者,我深知管理一个网站的复杂性。从迁移网站、设置重定向到整理内容结构,每一步都需要精细处理。而拥有所有 URL 的清单,不仅能让这些工作变得更加简单,还能为后续的管理提供极大的便利。其实&#…...
【进程概念精讲】
Susan,在那命运月台前面,再上车,春天开始落叶.................................................................. 文章目录 前言 一、【认识进程】 1、【进程基本概念引入】 2、【进程的描述与组织——进程控制块(PCB)与进程…...
帽子矩阵--记录
帽子矩阵 H是一个重要的统计工具,用于评估数据点对模型拟合结果的影响。通过计算帽子矩阵的对角线元素(杠杆值),我们可以识别出高杠杆点,这些点对模型的影响较大,可能需要特别关注。...
MySQL深入:B+树的演化、索引和索引结构
提示:内容是读《MySQL技术内幕:InnoDB存储引擎》,笔记摘要 文章目录 二叉查找树平衡二叉树(AVL) B树(BTree)B树(BTree)InnoDB B树索引索引结构(InnoDB B树)B树存放的数据量 二叉查找树 在二叉查找树中,左子…...
axios 实现 无感刷新方案
实现思路 首次登录前端通过接口获取到两个 token;分别是 accessToken、refreshToken; accessToken:正常请求需要传递的 token ;refreshToken:当某个请求 401 ,就可以通过 refreshToken 获取到新的 accessToken 特殊场…...
Python 三种方式实现自动化任务
在这篇文章中,我们将介绍一些用Python实现机器人过程自动化的包。机器人流程自动化(Robotic process automation,简称RPA)是指将鼠标点击和键盘按压自动化的过程,即模拟人类用户的操作。RPA用于各种应用程序࿰…...
新型创业模式:退休创业。没有工资,不用投资,有时间就干,不强制做,赚钱按贡献分。
这种“退休创业”的创业模式具有独特的吸引力和灵活性,适合那些已退休但希望继续贡献社会价值、赚取额外收入且无需承担太多责任的群体。以下是一个详细的设计思路: 模式概述 目标人群:退休人员,具有一定技能或经验,但…...
Android 项目依赖库无法找到的解决方案
目录 错误信息解析 解决方案 1. 检查依赖版本 2. 检查 Maven 仓库配置 3. 强制刷新 Gradle 缓存 4. 检查网络连接 5. 手动下载依赖 总结 相关推荐 最近,我在编译一个 Android 老项目时遇到了一个问题,错误信息显示无法找到 com.gyf.immersionba…...
在Node.js中如何使用TypeScript
第一步:创建一个Node.js项目的package.json文件 npm init -y第二步:添加TypeScript、添加node.d.ts npm install typescript -D npm install types/node -D第三步:初始化一个tsconfig.json文件 npx tsc --init --rootDir src --outDir lib…...
链表两数加python
一、问题描述 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外,这两个…...
免费的可以薅羊毛的cloudflare反向代理教程
cloudflare-reverse-proxy 项目代码: https://github.com/EASTCATV/cloudflare-reverse-proxy 本项目是cloudflare反向代理。在cloudflare网站中新建worker,把worker.js文件中的内容复制进去即可使用。 使用方法为在任意url前面加上https://你的域名/proxy/ 即可…...
【每日刷题】Day155
【每日刷题】Day155 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. LCR 108. 单词接龙 - 力扣(LeetCode) 2. 675. 为高尔夫球比赛砍树 - 力扣(…...
EXCEL延迟退休公式
如图: A B为手工输入 C2EOMONTH(A2,B2*12) D2EOMONTH(C2,IF(C2>DATEVALUE("2025-1-1"),INT((DATEDIF(DATEVALUE("2025-1-1"),C2,"m")4)/4),0)) E2EOMONTH(A2,B2*12IF(EOMONTH(A2,B2*12)>DATEVALUE("2025-1-1"),INT(…...
终极免费窗口强制调整工具:如何突破Windows尺寸限制
终极免费窗口强制调整工具:如何突破Windows尺寸限制 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些无法拖拽的"顽固窗口"而烦恼吗?Wi…...
终极指南:如何解锁光猫全部性能?RTL960x开源方案深度解析
终极指南:如何解锁光猫全部性能?RTL960x开源方案深度解析 【免费下载链接】RTL960x Hacking & Reverse Engineering RTL960x-based xPON ONTs to suit your OLT 项目地址: https://gitcode.com/gh_mirrors/rt/RTL960x RTL960x开源光猫固件是基…...
别再焊错线了!51单片机+L298N驱动小车底盘,保姆级接线避坑指南
51单片机L298N驱动小车底盘:从零避坑到一次点亮 当你第一次把51单片机、L298N电机驱动模块、红外传感器和电源组装在一起时,是否曾被那些密密麻麻的杜邦线弄得晕头转向?每个初学者都可能经历过接错线导致芯片冒烟的惨痛教训。本文将用实战经验…...
Kubernetes 监控与可观测性深度解析:Prometheus + Grafana + Loki
Kubernetes 监控与可观测性深度解析:Prometheus Grafana Loki 引言 在云原生环境中,监控与可观测性是保障系统稳定运行的关键。Kubernetes 生态提供了丰富的监控工具,其中 Prometheus、Grafana 和 Loki 组成了完整的可观测性栈。本文将深…...
网络工程师避坑指南:eNSP中配置Eth-Trunk链路聚合的5个常见错误与排查方法
网络工程师避坑指南:eNSP中配置Eth-Trunk链路聚合的5个常见错误与排查方法 在华为eNSP模拟器中配置Eth-Trunk链路聚合时,许多网络工程师都会遇到各种"翻车"现场。明明按照教程一步步操作,却发现带宽没有叠加、端口状态异常…...
国产电池包传感监测芯片:从AFE设计到BMS系统实战解析
1. 项目概述:从“芯”守护,让每一度电都安全在电动汽车的心脏——动力电池包里,温度、电压、电流这些关键参数哪怕出现一丝一毫的异常,都可能从量变引发质变,最终导致热失控等严重安全事故。因此,对电池包内…...
定义即生产力:Perplexity定义查询功能的7种高阶用法(含论文写作/术语溯源/跨语言校验)
更多请点击: https://kaifayun.com 第一章:定义即生产力:Perplexity定义查询功能的底层逻辑与设计哲学 Perplexity 的定义查询功能并非简单地匹配关键词,而是将用户输入的自然语言问题映射为可执行的语义图谱查询。其核心在于“定…...
告别开机黑屏:搞懂UEFI、CSM和Secure Boot的‘三角关系’,装机不求人
现代计算机启动架构解密:UEFI、CSM与Secure Boot的协同与冲突 开机黑屏是许多DIY装机用户和技术爱好者常遇到的棘手问题。当新硬件遇上旧设备,或是现代系统需要兼容传统软件时,计算机的启动过程往往成为第一道技术壁垒。要真正理解这些兼容性…...
MySQL切换服务器数据迁移记录
老服务器性能不足,计划迁移数据至新服务器 一、查询 MySQL 数据库 / 表数据量大小 1、查询所有数据库的总大小 SELECTCONCAT(ROUND(SUM(data_length index_length) / 1024 / 1024, 2), MB) AS total_database_size,CONCAT(ROUND(SUM(data_length index_length) /…...
别再死记硬背公式了!用大白话和动图拆解Transformer的注意力机制
用生活场景拆解Transformer:注意力机制就像一场高效会议 想象你正在主持一场跨国团队会议,成员们用不同语言讨论项目进展。作为主持人,你需要快速捕捉每个人的发言重点,判断谁的意见最关键,并协调不同观点之间的关系—…...
