【WPF】Prism学习(三)
Prism Commands
1.复合命令(Composite Commanding)
这段内容主要介绍了在应用程序中如何使用复合命令(Composite Commands)来实现多个视图模型(ViewModels)上的命令。以下是对这段内容的解释:
1.1. 复合命令的概念:
- 在许多情况下,ViewModel中定义的命令会绑定到相关视图中的控件上,这样用户就可以直接在视图中调用这些命令。
- 然而,在某些情况下,你可能希望从一个父视图中的控件调用一个或多个ViewModel上的命令。
1.2. 复合命令的应用场景:
- 例如,如果你的应用程序允许用户同时编辑多个项目,你可能希望允许用户使用应用程序工具栏或功能区中的单个按钮来保存所有项目。在这种情况下,"保存全部"命令将调用每个项目的ViewModel实例实现的"保存"命令。
1.3. Prism框架对复合命令的支持:
- Prism框架通过
CompositeCommand
类支持这种场景。 CompositeCommand
类表示由多个子命令组成的命令。当复合命令被调用时,会依次调用每个子命令。- 它适用于需要在UI中将一组命令表示为单个命令的情况,或者当你想要调用多个命令以实现一个逻辑命令时。
1.4. CompositeCommand
类的工作原理:
CompositeCommand
类维护一个子命令列表(DelegateCommand
实例)。CompositeCommand
类的Execute
方法简单地依次调用每个子命令的Execute
方法。CanExecute
方法类似地调用每个子命令的CanExecute
方法,但如果任何一个子命令不能执行,CanExecute
方法将返回false
。换句话说,默认情况下,只有当所有子命令都可以执行时,CompositeCommand
才能被执行。
1.5. CompositeCommand
类的位置:
CompositeCommand
可以在Prism.Commands
命名空间中找到,该命名空间位于Prism.Core
NuGet包中。
2.创建一个复合命令
复合命令是由多个子命令组成的命令,当复合命令被触发时,它的每个子命令会依次被执行。这在用户界面(UI)中表示一组命令为单个命令或者想要执行多个命令以实现一个逻辑命令时非常有用。
具体来说,创建复合命令的步骤:
- 实例化一个
CompositeCommand
对象。 - 将这个
CompositeCommand
对象作为一个属性暴露出来,这个属性可以是ICommand
或者CompositeCommand
类型。
下面是具体的代码示例:
public class ApplicationCommands
{// 创建一个私有的 CompositeCommand 实例private CompositeCommand _saveCommand = new CompositeCommand();// 将这个实例作为一个公共属性暴露出来,允许外部访问和使用这个复合命令public CompositeCommand SaveCommand{get => _saveCommand;}
}
在这个例子中,ApplicationCommands
类包含了一个名为 SaveCommand
的属性,这个属性是一个 CompositeCommand
类型的实例。这样,你就可以在应用程序的任何地方通过 ApplicationCommands.SaveCommand
来访问和使用这个复合命令,例如,将其绑定到用户界面的按钮上,当按钮被点击时,就会触发这个复合命令及其所有的子命令。
3. 全局使用复合命令(CompositeCommand)
3.1.使用依赖注入(DI)来全局使用复合命令
-
定义接口:首先,你需要定义一个接口
IApplicationCommands
,该接口包含一个SaveCommand
属性,它是一个CompositeCommand
实例。public interface IApplicationCommands {CompositeCommand SaveCommand { get; } }
-
实现接口:然后,创建一个类
ApplicationCommands
来实现这个接口,并在类中定义_saveCommand
作为CompositeCommand
的实例。public class ApplicationCommands : IApplicationCommands {private CompositeCommand _saveCommand = new CompositeCommand();public CompositeCommand SaveCommand{get => _saveCommand;} }
-
注册为单例:在你的应用程序中,需要将
ApplicationCommands
类注册为单例,这样在整个应用程序中使用的都是同一个CompositeCommand
实例。public partial class App : PrismApplication {protected override void RegisterTypes(IContainerRegistry containerRegistry){containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();} }
-
在ViewModel中注册子命令:在ViewModel的构造函数中,请求
IApplicationCommands
接口,并使用SaveCommand
来注册你的DelegateCommand
。public DelegateCommand UpdateCommand { get; private set; }public TabViewModel(IApplicationCommands applicationCommands) {UpdateCommand = new DelegateCommand(Update);applicationCommands.SaveCommand.RegisterCommand(UpdateCommand); }
3.2.使用静态类来全局使用复合命令
-
创建静态类:创建一个静态类
ApplicationCommands
,并在其中定义一个静态的SaveCommand
属性,它是一个CompositeCommand
实例。public static class ApplicationCommands {public static CompositeCommand SaveCommand = new CompositeCommand(); }
-
在ViewModel中关联子命令:在ViewModel中,将你的
DelegateCommand
与静态的ApplicationCommands
类关联起来。public DelegateCommand UpdateCommand { get; private set; }public TabViewModel() {UpdateCommand = new DelegateCommand(Update);ApplicationCommands.SaveCommand.RegisterCommand(UpdateCommand); }
为了提高代码的可维护性和可测试性,推荐使用依赖注入的方式而不是静态类。
4.绑定全局可用的复合命令(CompositeCommands)
4.1.使用依赖注入(Dependency Injection)
-
暴露IApplicationCommands:在使用依赖注入(DI)时,你需要在视图模型(ViewModel)中暴露
IApplicationCommands
接口,以便将其绑定到视图(View)。 -
设置属性:在视图模型的构造函数中请求
IApplicationCommands
实例,并设置一个类型为IApplicationCommands
的属性。public class MainWindowViewModel : BindableBase {private IApplicationCommands _applicationCommands;public IApplicationCommands ApplicationCommands{get => _applicationCommands;set => SetProperty(ref _applicationCommands, value);}public MainWindowViewModel(IApplicationCommands applicationCommands){ApplicationCommands = applicationCommands;} }
在这个例子中,
MainWindowViewModel
类有一个ApplicationCommands
属性,它在构造函数中被设置为传入的IApplicationCommands
实例。 -
在视图中绑定按钮:在XAML视图中,将按钮的
Command
属性绑定到ApplicationCommands.SaveCommand
属性。SaveCommand
是在ApplicationCommands
类中定义的。<Button Content="Save" Command="{Binding ApplicationCommands.SaveCommand}"/>
这里,按钮的
Command
属性被绑定到视图模型中的SaveCommand
属性,当按钮被点击时,会触发SaveCommand
。
4.2.使用静态类(Static Class)
-
绑定到静态ApplicationCommands类:如果你使用的是静态类方法,以下代码示例展示了如何在WPF中将按钮绑定到静态的
ApplicationCommands
类。<Button Content="Save" Command="{x:Static local:ApplicationCommands.SaveCommand}" />
在这个例子中,按钮的
Command
属性直接绑定到ApplicationCommands
类中的静态SaveCommand
属性。
5.从CompositeCommand
中注销子命令
在编程中,特别是在使用命令模式(Command Pattern)时,我们可能会创建一些命令,并将它们注册到一个复合命令(CompositeCommand
)中。这样做的好处是可以将多个命令作为一个单一的命令来处理,简化了用户界面(UI)的操作。
然而,当你的视图(View)或视图模型(ViewModel)不再需要时,比如它们即将被垃圾回收器(Garbage Collector, GC)回收,你应该从CompositeCommand
中注销这些子命令。这是因为如果这些子命令仍然被CompositeCommand
持有,它们将不会被垃圾回收,从而导致内存泄漏。内存泄漏是指程序中已分配的内存空间由于某种原因未被正确释放,导致随着时间的推移,可用内存越来越少,最终可能影响程序的性能。
public void Destroy(){_applicationCommands.UnregisterCommand(UpdateCommand);}
在这段内容中,提供了一个Destroy
方法的示例,该方法使用CompositeCommand.UnregisterCommand
方法来注销一个名为UpdateCommand
的子命令。这样做可以确保当View
或ViewModel
不再需要时,相关的命令可以被正确地从CompositeCommand
中移除,从而允许垃圾回收器回收这些对象,避免内存泄漏。
6.执行活跃视图(Active Views)上的命令
6.1. 在父视图级别协调子视图命令的执行:
- 在某些情况下,你可能希望执行所有显示视图上的命令,例如前面提到的“保存全部”(Save All)命令。
- 在其他情况下,你可能只希望在当前活跃的视图上执行命令。在这种情况下,组合命令(CompositeCommand)只会在被认为是活跃的视图上执行子命令,而不活跃的视图上的子命令则不会执行。例如,你可能想在应用程序的工具栏上实现一个“缩放”(Zoom)命令,这个命令只会导致当前活跃的项目被缩放。
6.2. IActiveAware接口:
- 为了支持上述场景,Prism提供了
IActiveAware
接口。该接口定义了一个IsActive
属性,当实现者处于活跃状态时返回true
,以及一个IsActiveChanged
事件,每当活跃状态改变时触发。
6.3. 在视图或视图模型上实现IActiveAware接口:
- 这个接口主要用于跟踪视图的活跃状态。一个视图是否活跃是由特定控件内的视图决定的。例如,在Tab控件中,有一个适配器将当前选中的标签页中的视图设置为活跃状态。
6.4. DelegateCommand类实现IActiveAware接口:
CompositeCommand
可以通过在构造函数中指定monitorCommandActivity
参数为true
来配置,以评估子DelegateCommand
的活跃状态(除了CanExecute
状态)。当这个参数设置为true
时,CompositeCommand
类在确定CanExecute
方法的返回值以及在Execute
方法中执行子命令时,会考虑每个子DelegateCommand
的活跃状态。
public class ApplicationCommands : IApplicationCommands{private CompositeCommand _saveCommand = new CompositeCommand(true);public CompositeCommand SaveCommand{get => _saveCommand;}}
6.5. CompositeCommand的行为:
- 当
monitorCommandActivity
参数为true
时,CompositeCommand
类表现出以下行为:CanExecute
:只有在所有活跃的命令都可以执行时才返回true
。不活跃的子命令将完全不被考虑。Execute
:执行所有活跃的命令。不活跃的子命令将完全不被考虑。
6.6. 在ViewModels上实现IActiveAware接口:
- 通过在ViewModels上实现
IActiveAware
接口,当视图变为活跃或不活跃时,你将得到通知。当视图的活跃状态改变时,你可以更新子命令的活跃状态。然后,当用户调用组合命令时,活跃子视图上的命令将被调用。
6.7. 示例代码:
public class TabViewModel : BindableBase, IActiveAware{private bool _isActive;public bool IsActive{get { return _isActive; }set => SetProperty(ref _isActive, OnIsActiveChanged);}public event EventHandler IsActiveChanged;public DelegateCommand UpdateCommand { get; private set; }public TabViewModel(IApplicationCommands applicationCommands){UpdateCommand = new DelegateCommand(Update);applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);}private void Update(){//实现逻辑}private void OnIsActiveChanged(){UpdateCommand.IsActive = IsActive; //set the command as activeIsActiveChanged?.Invoke(this, new EventArgs()); //invoke the event for all listeners}}
- 提供了一个
TabViewModel
的示例,展示了如何实现IActiveAware
接口,并在视图的活跃状态改变时更新子命令的活跃状态。
相关链接
- 介绍(Introduction)
- 命令(Commands)
- 命令(Commanding)
- 复合命令(Composite Commands)
相关文章:

【WPF】Prism学习(三)
Prism Commands 1.复合命令(Composite Commanding) 这段内容主要介绍了在应用程序中如何使用复合命令(Composite Commands)来实现多个视图模型(ViewModels)上的命令。以下是对这段内容的解释: …...

1+X应急响应(网络)系统加固:
系统加固: 数据库的重要性: 数据库面临的风险: 数据库加固: 业务系统加固: 安全设备加固: 网络设备加固:...

使用 Grafana api 查询 Datasource 数据
一、使用grafana 的api 接口 官方API 二、生成Api key 点击 Administration -》Users and accss -》Service accounts 进入页面 点击Add service account 创建 service account 点击Add service account token 点击 Generate token , 就可以生成 api key 了 三、进入grafana…...

【电子设计】按键LED控制与FreeRTOS
1. 安装Keilv5 打开野火资料,寻找软件包 解压后得到的信息 百度网盘 请输入提取码 提取码:gfpp 安装526或者533版本都可以 下载需要的 F1、F4、F7、H7 名字的 DFP pack 芯片包 安装完 keil 后直接双击安装 注册操作,解压注册文件夹后根据里面的图示步骤操作 打开说明 STM…...

JMeter中添加请求头
在JMeter中添加请求头的步骤如下: 1.打开HTTP信息头管理器 : 首先,你需要进入JMeter的HTTP请求组件。这可以通过在HTTP请求测试元素上右键点击,然后选择“添加 > 配置元件 > HTTP信息头管理器”来完成。 2.添加新的请求头…...

VMD + CEEMDAN 二次分解,CNN-LSTM预测模型
往期精彩内容: 时序预测:LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享! EMD变体分解效果最好算法——CEEMDAN(五)-CSDN博客 拒绝信息泄露!VMD滚动分…...

【Linux系统编程】第四十六弹---线程同步与生产消费模型深度解析
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、Linux线程同步 1.1、同步概念与竞态条件 1.2、条件变量 1.2.1、认识条件变量接口 1.2.2、举例子认识条件变量 1.2.3、…...
VoIP是什么?
IP 语音 (VoIP)(Voice over Internet Protocol) 是一种通过互联网拨打电话的方法。与旧的固定电话系统不同,互联网并非设计用于在连接的人之间实时传输音频信号。必须构建专门的技术和协议才能使之成为可能,这些技术和协议构成了 …...

MySQL 中的集群部署方案
文章目录 MySQL 中的集群部署方案MySQL ReplicationMySQL Group ReplicationInnoDB ClusterInnoDB ClusterSetInnoDB ReplicaSetMMMMHAGalera ClusterMySQL ClusterMySQL Fabric 总结参考 MySQL 中的集群部署方案 MySQL Replication MySQL Replication 是官方提供的主从同步方…...

《设计模式》创建型模式总结
目录 创建型模式概述 Factory Method: 唯一的类创建型模式 Abstract Factory Builder模式 Prototype模式 Singleton模式 最近在参与一个量化交易系统的项目,里面涉及到用java来重构部分vnpy的开源框架,因为是框架的搭建,所以会涉及到像…...

Conda安装与使用中的若干问题记录
Conda安装与使用中的若干问题记录 1.Anaconda 安装失败1.1.问题复述1.2.问题解决(安装建议) 2.虚拟环境pip install未安装至本虚拟环境2.1.问题复述2.2.问题解决 3.待补充 最近由于工作上的原因,要使用到Conda进行虚拟环境的管理,…...

人力资源招聘系统的革新之路:从传统到智能的转变
在全球化与数字化交织的今天,企业间的竞争日益激烈,而人才作为企业发展的核心驱动力,其重要性不言而喻。传统的人力资源招聘方式,如依赖纸质简历、人工筛选、面对面面试等,不仅效率低下,且难以精准匹配企业…...

Python网络爬虫与数据采集实战——网络协议与HTTP
目录 1. HTTP协议简介 2. 常见的请求方法 3. 状态码含义 实际应用中的HTTP协议 1. 如何在爬虫中使用HTTP协议 2. 模拟浏览器请求与爬虫反爬虫技术 3. 高级HTTP请求 实现爬虫时HTTP协议的优化与常见问题 总结 1. HTTP协议简介 HTTP的定义与作用 HTTP(超文本…...

从零开始的c++之旅——二叉搜索树
1、二叉搜索树概念 1. ⼆叉搜索树的概念 ⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树: • 若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值 • 若它的右⼦树不为空,则右⼦树上所有结…...

CSS回顾-基础知识详解
一、引言 在前端开发领域,CSS 曾是构建网页视觉效果的关键,与 HTML、JavaScript 一起打造精彩的网络世界。但随着组件库的大量涌现,我们亲手书写 CSS 样式的情况越来越少,CSS 基础知识也逐渐被我们遗忘。 现在,这种遗…...

Elasticsearch 查询时 term、match、match_phrase、match_phrase_prefix 的区别
Elasticsearch 查询时 term、match、match_phrase、match_phrase_prefix 的区别 keyword 与 text 区别term 查询match 查询match_phrase 查询match_phrase_prefix 查询写在最后 在讲述 es 查询时 term、match、match_phrase、match_phrase_prefix 的区别之前,先来了…...
低代码平台:跨数据库处理的重要性与实现方式
一、低代码平台概述 低代码平台作为一种创新的软件开发工具,为开发者带来了极大的便利。它具备可视化编程工具和大量预构建组件,这使得开发者无需编写大量代码就能创建应用程序,显著降低了软件开发的技术门槛。无论是专业开发人员还是业务人员…...
【jvm】如何破坏双亲委派机制
目录 1.说明2.重写ClassLoader的loadClass方法2.1 原理2.2 实现步骤2.3 注意事项 3.使用线程上下文类加载器3.1 原理3.2 实现步骤3.3 应用场景 4.利用SPI机制4.1 原理4.2 实现步骤4.3 应用场景 5.Tomcat等容器的自定义类加载器5.1 原理5.2 实现方式5.3 应用场景 1.说明 1.双亲委…...

ReactPress与WordPress:一场内容管理系统的较量
ReactPress Github项目地址:https://github.com/fecommunity/reactpress WordPress官网:https://wordpress.org/ ReactPress与WordPress:一场内容管理系统的较量 在当今数字化时代,内容管理系统(CMS)已成为…...

网络安全练习之 ctfshow_web
文章目录 VIP题目限免(即:信息泄露题)源码泄露前台JS绕过协议头信息泄露robots后台泄露phps源码泄露源码压缩包泄露版本控制泄露源码(git)版本控制泄露源码2(svn)vim临时文件泄露cookie泄露域名txt记录泄露敏感信息公布内部技术文档泄露编辑器…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...