【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中的ExecuteCanExecute方法。这意味着,当你在UI中触发一个命令时,DelegateCommand会调用ViewModel中相应的方法。

2.2. 你可以通过DelegateCommand类的构造函数来指定这些委托。例如,下面的代码示例展示了如何通过指定委托到ViewModel中的SubmitCanSubmit方法来构造一个代表提交命令的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类是一个泛型类型。类型参数指定了传递给ExecuteCanExecute方法的命令参数的类型。在上面的例子中,命令参数是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避免使用值类型作为参数,如果需要使用值类型参数时,应该如何处理以避免潜在的问题:

  1. DelegateCommand避免使用值类型DelegateCommand这个类在设计时故意不使用值类型(如intdoublebool等)作为参数类型。这是因为ICommand接口接受的是一个对象类型的参数。
  2. 值类型参数可能导致意外行为:如果在XAML初始化时,命令绑定调用了CanExecute(null),并且T是一个值类型,那么可能会导致一些意料之外的行为。这是因为值类型在默认情况下会被初始化为它们的默认值(如int的默认值是0),这可能会导致命令的执行逻辑出现问题。
  3. 默认值问题:考虑过使用default(T)作为解决方案,但最终被拒绝了。原因是实现者将无法区分一个有效的值和一个默认值。例如,如果Tint,那么default(int)是0,但实现者可能无法确定0是一个有效的参数值还是一个默认值。
  4. 使用可空类型:如果你确实需要使用值类型作为参数,那么必须将其转换为可空类型(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.在命令中使用异步方法

在现代编程中,使用asyncawait调用异步方法是很常见的需求。虽然许多人的第一反应是需要一个AsyncCommand,但实际上这种假设是错误的。ICommand本质上是同步的,ExecuteCanExecute委托应该被视为事件。这意味着使用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)

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

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

相关文章

两部手机的IP地址:是否会相同?全面探讨

在数字化时代&#xff0c;手机已成为我们生活中不可或缺的一部分&#xff0c;而IP地址作为手机连接互联网的重要标识&#xff0c;扮演着举足轻重的角色。许多用户可能会好奇&#xff0c;两部手机在连接网络时&#xff0c;它们的IP地址是否会相同&#xff1f;这个问题看似简单&a…

【代码随想录day31】【C++复健】56. 合并区间;738.单调递增的数字

56. 合并区间 遇到了三个问题&#xff0c;一一说来&#xff1a; 1 比较应该按左区间排序&#xff0c;我却写了右区间。由于本题是合并区间&#xff0c;判断是否连续显然是用下一个的左区间与前一个的右区间比较&#xff0c;属于没想清楚了。 2 在写for循环时写成了如下的代码…

uniapp: 微信小程序包体积超过2M的优化方法

一、问题描述 在使用uniapp进行微信小程序开发时&#xff0c;经常会遇到包体积超过2M而无法上传&#xff1a; 二、解决方案 目前关于微信小程序分包大小有以下限制&#xff1a; 整个小程序所有分包大小不超过 30M&#xff08;服务商代开发的小程序不超过 20M&#xff09; 单个…

MySQL Online DDL

文章目录 1. 在线DDL的优势2. 支持的DDL操作3. 在线DDL的原理4. Online DDL的操作流程1. 准备阶段&#xff08;Prepare phase&#xff09;2. 拷贝阶段&#xff08;Copy phase&#xff09;3. 应用阶段&#xff08;Apply phase&#xff09;4. 替换阶段&#xff08;Swap phase&…

【freertos】FreeRTOS时间管理

FreeRTOS时间管理 一、睡眠延时函数1、vTaskDelay2、vTaskDelayUntil3、相对延时与绝对延时对比 二、自定义延时函数1、微秒延时2、毫秒延时 一、睡眠延时函数 1、vTaskDelay \quad 在UCOSIII 中延时函数OSTimeDly()可以设置为三种模式:相对模式、周期模式和绝对模式。在FreeR…

栈相关算法题1|通过栈判断链表是否对称|共享栈入栈出栈|括号匹配|多种括号配对|递归求序列最大值(C)

通过栈判断链表是否对称 设单链表的表头指针为L&#xff0c;data域为字符型&#xff0c;判断该链表的全部n个字符是否中心对称 xyx&#xff0c;xyyx 算法思想 使用栈来判断链表中的数据是否中心对称&#xff0c;让链表的前一半元素依次进栈 在处理链表的后一半元素时&#x…

datawhale11月组队学习 模型压缩技术3:2:4结构稀疏化BERT模型

文章目录 一、 半结构化稀疏性简介二、 代码实践2.1 定义辅助函数2.2 加载模型、tokenizer和数据集2.3 测试baseline模型指标2.4 对BERT-base模型进行半结构稀疏化 《datawhale2411组队学习之模型压缩技术1&#xff1a;模型剪枝&#xff08;上&#xff09;》&#xff1a;介绍模…

Qt中实现旋转动画效果

使用QPropertyAnimation类绑定对应的属性后 就可以给这个属性设置对应的动画 //比如自定义了属性 Q_PROPERTY(int rotation READ rotation WRITE setRotation)//给这个属性加动画效果 //参数1&#xff1a;谁要加动画效果 //参数2&#xff1a;哪个属性加动画效果 //参数3&…

视频流媒体播放器EasyPlayer.js RTSP播放器视频颜色变灰色/渲染发绿的原因分析

EasyPlayer.js RTSP播放器属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0…

SpringBoot+Vue3开发会议管理系统

1 项目介绍 会议管理系统&#xff0c;简化公司内会议方面的流程&#xff0c;提供便捷。实现对会议室的管理、会议的管理、会议预约的管理&#xff0c;三大主流程模块。 系统分为三种角色&#xff0c;分别是员工、管理员和超级管理员。 员工角色功能&#xff1a;查看会议室占…

前端 JS 实用操作总结

目录 1、重构解构 1、数组解构 2、对象解构 3、...展开 2、箭头函数 1、简写 2、this指向 3、没有arguments 4、普通函数this的指向 3、数组实用方法 1、map和filter 2、find 3、reduce 1、重构解构 1、数组解构 const arr ["唐僧", "孙悟空&quo…

Clip结合Faiss+Flask简易版文搜图服务

一、实现 使用目录结构&#xff1a; templates ---upload.html faiss_app.py 前端代码&#xff1a;upload.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&quo…

【鸿蒙开发】第十一章 Stage模型应用组件-任务Mission

目录 1 任务(Mission)管理场景 2 任务&#xff08;Mission&#xff09;与启动模式 2.1 singleton单实例模式 2.2 multiton多实例模式 2.3 specified指定实例模式 3 页面栈及任务链 3.1 页面栈 3.2 任务链 4 设置任务快照的图标和名称 4.1 设置任务快照的图标&#xf…

探索 HTML 和 CSS 实现的模拟时钟

效果演示 这段代码是一个模拟时钟的 HTML 和 CSS 代码。它创建了一个简单的数字时钟界面&#xff0c;包括时针、分针和秒针。 HTML <div class"face"><p class"v-index">II</p><p class"h-index">II</p><d…

CSS预编译器:让样式编写更高效的秘密武器(6)

在现代前端开发中&#xff0c;CSS 预编译器是一种非常有用的工具&#xff0c;它通过扩展 CSS 语言的功能&#xff0c;帮助开发者更高效地编写和维护样式代码。本文将介绍 CSS 预编译器的基本原理&#xff0c;并重点讲解 LESS 的安装和使用方法。 1. 基本原理 编写 CSS 时&…

Vue3中实现插槽使用

目录 一、前言 二、插槽类型 三、示例 四、插槽的分类实现 1. 基本插槽 2. 命名插槽 3. 默认插槽内容 4. 作用域插槽&#xff08;Scoped Slots&#xff09; 5. 多插槽与具名插槽组合 一、前言 在 Vue 3 中&#xff0c;插槽&#xff08;Slot&#xff09;用于实现组件的内…

爬虫——Requests库的使用

在爬虫开发中&#xff0c;HTTP请求是与服务器进行交互的关键操作。通过发送HTTP请求&#xff0c;爬虫可以获取目标网页或接口的数据&#xff0c;而有效地处理请求和响应是爬虫能够高效且稳定运行的基础。Requests库作为Python中最常用的HTTP请求库&#xff0c;因其简洁、易用和…

如何使用EasyExcel生成多列表组合填充的复杂Excel示例

作者&#xff1a;Funky_oaNiu 一、&#xff08;需求&#xff09;生成的表格效果&#xff1a;二、搞一个模板文件三、建立对应的表格实体类四、开始填充五、Vue3前端发起请求下载六、官方文档及AI问答 一、&#xff08;需求&#xff09;生成的表格效果&#xff1a; 其中只有顶部…

三、计算机视觉_02计算机视觉领域的四大基本任务

0、前言 计算机视觉是人工智能领域的一个重要分支&#xff0c;它是一个跨学科的领域&#xff0c;涉及计算机科学、人工智能、机器学习、图像处理、神经科学等多个学科的知识 计算机视觉使用计算机技术来模拟人类视觉系统的功能&#xff0c;使计算机能够从图像或多维数据中提取…

Docker: ubuntu系统下Docker的安装

安装依赖 操作系统版本 Ubuntu Kinetic 22.10Ubuntu Jammy 24.04 (LTS)Ubuntu Jammy 22.04 (LTS)Ubuntu Focal 20.04 (LTS)Ubuntu Bionic 18.04 (LTS) CPU架构支持 ARMx86_64 查看我们的系统版本信息 uname -a通过该命令查得cpu架构是x86_64的&#xff1b; cat /etc/*re…