【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(…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...