WPF之命令

命令的定义:

命令与事件的区别:命令是具有约束性的。

命令还可以控制接收者"先做校验,再保存,再关闭"。

命令:WPF的命令,实际上就是实现了ICommand接口的类,平时使用最多的是RoutedCommand类,还可以自定义命令。

如何使用自定义命令:

1、命令源:

即命令的发送者,是实现了TCommandSource接口的类,很多界面元素都实现了这个接口。

2、命令目标:

CommandTarget,即命令发送给谁,将作用在谁的身上,命令目标必须是实现了IInputElement接口的类。

3、命令关联:

CommandBinding,负责把一些外围逻辑和命令关联起来,不如执行之前对命令是否可以执行进行判断,命令执行之后还有哪些后续工作等。

基本元素之间的关系:

1、创建命令类:即获得一个实现ICommand接口的类,如果命令与具体业务逻辑无关,则使用WPF类库中的RoutedCommand类即可,如果想得到与业务逻辑相关的专有命令,则需要创建RoutedCommand的派生类。

2、声明命令实例:使用命令时需要创建命令类的实例。这里有个技巧,一般情况下程序中某种操作只需要一个命令实例与之对应即可。比如对应“保存”这个操作,你可以拿同一个实例去命令每个组件执行其保存功能,因此程序中的命令会使用单件模式(Singleton Pattern)以减少代码的复杂度。

3、指定命令的源:即指定由谁来发送这个命令。如果把命令看作炮弹,那么命令源就相当于火炮。同一个命令可以有多个源。比如保存命令,既可以由菜单中的保存项来发送,也可以由工具栏中的保存图标来发送。需要注意的是,一旦把命令指派给命令源,那么命令源就会受命令的影响,当命令不能被执行的时候作为命令源的控件将处在不可用状态。看来命令这种炮弹很智能,当不满足发射条件时还会给用来发射它的火炮上一道保险。避免走火。还需要注意,各种控件发送命令的方法不尽相同,比如 Button 和 MenuItem 是在单击时发送命令,而 ListBoxItem 单击时表示被选中所以双击时才发送命令。

4、指定命令目标:命令目标并不是命令的属性而是命令源的属性,指定命令目标是告诉命令源向哪个组件发送命令,无论这个组件是否拥有焦点它都会收到这个命令。如果没有为命令源指定命令目标,则 WPF 系统认为当前拥有焦点的对象就是命令目标。这个步骤有点像为火炮指定目标。

5、设置命令关联:炮兵是不能单独战斗的,就像炮兵需要侦查兵在射击前观察敌情、判断发射时机,在射击后观测射击效果、帮助修正一样,WPF 命令需要 CommandBinding 在执行前来帮助判断是不是可以执行、在执行后做一些事件来“打扫战场”。

命令实战:

<Grid><StackPanel x:Name="stackPanel"><Button Content="Send Command" x:Name="btn_1" Margin="5"/><TextBox x:Name="txt_1" Margin="5" Height="100"/></StackPanel></Grid>

 

 /// <summary>/// MainWindow.xaml 的交互逻辑/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();InitialCommand();}//第一个参数   Clear  只是一个对命令的命名,用以区分别的命令//第二个参数  是这个命令归属于哪个类private RoutedCommand clearCmd = new RoutedCommand("Clear",typeof(MainWindow));public void InitialCommand(){//把命令赋值给命令源(发送者)并指定快捷键this.btn_1.Command = this.clearCmd;//添加快捷键启动命令this.clearCmd.InputGestures.Add(new KeyGesture(Key.C,ModifierKeys.Alt));//指定命令目标this.btn_1.CommandTarget = this.txt_1;//创建命令关联 CommandBinding 是用来定义命令的执行逻辑(Executed 事件)和能否执行的逻辑(CanExecute 事件)//当一个命令被触发时(例如通过按钮点击或快捷键),WPF 会沿着元素的逻辑树向上查找是否有与该命令相关的 CommandBinding。如果找到,则会触发对应的事件。CommandBinding cb = new CommandBinding();cb.Command = this.clearCmd;//只关注与clearCmd相关的事件cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);//添加是否可以执行的事件cb.Executed += new ExecutedRoutedEventHandler(cb_Executed);//添加执行的事件//把命令关联安置在外围控件上?为什么呢?//如果将 CommandBinding 放在按钮(btn_1)上,那么只有当按钮本身直接处理命令时才有效。但如果将命令绑定放在外围控件(如 stackPanel)上,那么整个 stackPanel 内的任何元素(包括按钮、快捷键等)都可以触发该命令,并且会找到这个 CommandBinding。这样可以确保命令的逻辑在整个布局范围内生效。//代码中,命令可以通过按钮点击触发,也可以通过快捷键(Alt + C)触发。如果将 CommandBinding 放在按钮上,那么快捷键触发时可能无法找到对应的绑定逻辑。而将命令绑定放在外围控件上,无论是按钮点击还是快捷键,都可以正确找到并执行命令。this.stackPanel.CommandBindings.Add(cb);}//当探测命令是否可以执行时,此方法被调用void cb_CanExecute(object sender,CanExecuteRoutedEventArgs e){if (string.IsNullOrEmpty(this.txt_1.Text)){e.CanExecute = false;}else{e.CanExecute = true;}//避免继续向上传递降低程序性能e.Handled = true;}void cb_Executed(object sender,ExecutedRoutedEventArgs e){this.txt_1.Clear();//避免继续向上传递降低程序性能e.Handled = true;}}

对于上面代码有几点需要注意的地方:

第一,使用命令可以避免自己写代码判断 Button 是否可用以及添加快捷键。 第二,RoutedCommand 是一个与业务逻辑无关的类,只负责在程序中“跑腿”而并不对命令目标做任何操作,TextBox 并不是由它清空的。那么对 TextBox 的清空操作是谁做的呢?答案是 CommandBinding。因为无论是探测命令是否执行还是命令送达目标,都会激发命令目标发送路由事件,这些路由事件会沿着 UI 元素树向上传递并最终被 CommandBinding 所捕捉。本例中 CommandBinding 被安装在外围的 StackPanel 上,CommandBinding “站在高处”起一个侦听器的作用,而且专门针对 clearCmd 命令捕捉与其相关的路由事件。本例中,当 CommandBinding 捕捉到 CanExecute 事件就会调用 cb_CanExecute 方法(判断命令执行的条件是否满足,并反馈给命令供其影响命令源的状态);当捕捉到的是 Executed 事件(表示命令的 Execute 方法已经执行了,或说命令已经作用在了命令目标上,RoutedCommand 的 Execute 方法不包含业务逻辑,只负责让命令目标激发 Executed),则调用 cb_Executed 方法。 第三,因为 CanExecute 事件的激发频率比较高,为了避免降低性能,在处理完后建议把 e.Handled 设为 true。 第四,CommandBinding 一定要设置在命令目标的外围控件上,不然无法捕捉到 CanExecute 和 Executed 等路由事件。 

WPF 的命令库

命令具有“一处声明、处处使用”的特点,比如 Save 命令,在程序的任何地方它都表示要求命令目标保存数据。因此,微软在 WPF 类库里准备了一些便捷的命令库,这些命令库包括:

  • ApplicationCommands

  • ComponentCommands

  • NavigationCommands

  • MediaCommands

  • EditingCommands

 命令参数:

前面提到命令库里有很多 WPF 预制的命令,如 New、Open、Copy、Cut、Paste 等。这些命令都是 ApplicationCommands 类的静态属性,所以它们的实例永远只有一个,这就引出一个问题:如果界面上有两个按钮,一个用来新建 Teacher 的档案,另一个用来新建 Student 的档案,都使用 New 命令的话,程序应该如何区别新建的是什么档案呢? 答案是使用 CommandParameter。命令源一定是实现了 ICommandSource 接口的对象,而ICommandSource 有一个属性就是CommandParameter,如果把命令看作飞向目标的炮弹,那么 CommandParameter 就相当于装载在炮弹肚子里的“消息”。下面是程序的实现代码。

<Window x:Class="CommandParameterSample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Command Parameter"Height="240" Width="360" Background="LightBlue" WindowStyle="ToolWindow"><Grid Margin="6"><Grid.RowDefinitions><RowDefinition Height="24" /><RowDefinition Height="4" /><RowDefinition Height="24" /><RowDefinition Height="4" /><RowDefinition Height="24" /><RowDefinition Height="4" /><RowDefinition Height="*" /></Grid.RowDefinitions><!--命令和命令参数--><TextBlock Text="Name:" VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Row="0" /><TextBox x:Name="nameTextBox" Margin="60, 0,0,0" Grid.Row="0" /><Button Content="New Teacher" Command="New" CommandParameter="Teacher" Grid.Row="2" /><Button Content="New Student" Command="New" CommandParameter="Student" Grid.Row="4" /><ListBox x:Name="listBoxNewItems" Grid.Row="6" /></Grid><!--为窗体添加 CommandBinding--><Window.CommandBindings><CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed" /></Window.CommandBindings>
</Window>

注意:

代码有两个值得注意的地方: 两个按钮都使用 New 命令,但分别使用字符串 Teacher 和 Student 作为参数。 这次是使用 XAML 代码为窗体添加 CommandBinding,CommandBinding 的 CanExecute 和 Executed 事件处理器写在后台 C#代码里。

private void New_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{if (string.IsNullOrEmpty(this.nameTextBox.Text)){e.CanExecute = false;}else{e.CanExecute = true;}
}private void New_Executed(object sender, ExecutedRoutedEventArgs e)
{string name = this.nameTextBox.Text;if(e.Parameter.ToString() == "Teacher"){this.listBoxNewItems.Items.Add(string.Format("New Teacher: {0}, 学而不厌、诲人不倦。", name));}if(e.Parameter.ToString() == "Student"){this.listBoxNewItems.Items.Add(string.Format("New Student: {0}, 好好学习、天天向上。", name));}
}

 

 命令与 Binding 的结合

初试命令,你可能会想到这样一个问题:控件有很多事件,可以让我们进行各种各样不同的操
作,可控件只有一个 Command 属性,而命令库中却有数十种命令,这样怎么可能使用这个唯一的
Command 属性来调用那么多种命令呢?答案是:使用 Binding。前面已经说过,Binding 作为一种间接的、不固定的赋值手段,可以让你有机会选择在某个条件下为目标赋特定的值(有时候需要借助 Converter)。
例如,如果一个 Button 所关联命令有可能根据某些条件而改变,我们可以把代码写成这样:

<Button x:Name="dynamicCmdBtn" Command="{Binding Path=ppp, Source=sss}" Content="Command" />
  • Command:这是一个附加属性,它告诉WPF按钮应该执行一个命令。

  • {Binding ...}:这是一个绑定表达式,用于将按钮的 Command 属性与视图模型中的命令属性进行连接。

  • Path=ppp:这里的 ppp 是一个占位符,代表视图模型中命令属性的名称。例如,如果你的视图模型中有一个名为 SaveCommand 的命令属性,那么这里的 ppp 应该替换为 SaveCommand

  • Source=sss:这里的 sss 也是一个占位符,代表数据上下文(DataContext)的名称。在WPF中,数据上下文是绑定的起点,它通常是包含数据和命令的对象。如果你的视图模型实例被设置为某个控件(如窗口或页面)的 DataContext,那么这里的 sss 应该替换为该控件的XAML名称,或者是 RelativeSource 绑定,指向包含数据上下文的父级控件。

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

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

相关文章

百度文心一言开源大模型ERNIE-4.5-0.3B-PT深度测评

号外号外&#xff01;6月30号&#xff0c;百度文心一言官宣开源ERNIE 4.5大模型&#xff01;&#xff01;&#xff01; 一收到这个消息&#xff0c;博主就立马从GitCode拉了个模型&#xff0c;本地私有化部署体验了一下&#xff0c;一个字&#xff0c;酷&#xff01; 鉴于绝大…

零基础,使用Idea工具写一个邮件报警程序

打开idea&#xff0c;创建一个project打开文件目录下的pom.xml文件&#xff0c;添加下面的内容安装依赖&#xff0c;等待下载完成<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId> &…

字体 Unicode 区块字符展示 PDF 生成器

Unicode 字体字符集可视化工具 - 代码介绍 项目概述 这个工具是一个用于分析和可视化字体文件中包含的 Unicode 字符的实用程序&#xff0c;能够扫描指定字体文件&#xff0c;提取其中包含的所有 Unicode 字符&#xff0c;并按 Unicode 区块分类生成 PDF 文档&#xff0c;直观展…

第4章:实战项目一 打造你的第一个AI知识库问答机器人 (RAG)

各位老铁&#xff0c;欢迎来到我们专栏的第一个实战项目。 在过去的三个章节里&#xff0c;我们已经完成了所有的理论储备和环境搭建。我们理解了LLM的本质&#xff0c;掌握了Prompt Engineering的要领&#xff0c;洞悉了Embedding和向量数据库的魔力&#xff0c;并且熟悉了La…

身份证识别api-便捷生活与安全社会的双重保障

身份证识别技术是人工智能和图像处理领域的杰出产物之一&#xff0c;正逐步渗透到我们生活的方方面面。而最直观的作用就是简化身份证验证流程。现如今&#xff0c;无论是银行开户、酒店入住还是政务办理、线上支付&#xff0c;都需要输入 身份证信息进行身份验证&#xff0c;传…

跨国企业进入中国市场:如何利用亚马逊云科技文档 MCP 服务器解决区域差异问题

业务场景 想象一下&#xff0c;您是一家美国科技公司的 IT 架构师&#xff0c;公司刚刚决定将业务扩展到中国市场。作为技术负责人&#xff0c;您需要规划如何将现有的基于亚马逊云科技的应用迁移到中国区域。然而&#xff0c;您很快发现中国区的云服务环境与您熟悉的全球区域…

WPF使用WebBrowser 解决href标签target=_blank在浏览器窗口打开新链接而非窗体内部打开的问题

前言 最近在WPF中使用WebBrowser控件显示网页的时候遇到一个问题,由于网页里面有大规模的连接标签使用了target=_blank的属性,导致打开的网页不是在我们的程序内部,而是调用系统浏览器打开了我们的网页内容,这种情况非常的影响用户体验。于是就有了这篇文章内容。本文将详细…

制作MikTex本地包可用于离线安装包

MikTex安装包版本是basic-miktex-24.1-x64.exe。注&#xff1a;basic版本表示只安装MikTex基本包&#xff0c;不安装全部包。在能够联网的电脑上安装MikTex软件后&#xff0c;可以按以下步骤制作本地包库。一、制作本地包库1、新建一个文件夹&#xff0c;比如在D盘新建miktex-l…

Redis基础的介绍与使用(一)(Redis简介以及Redis下载和安装)

0 引言 本系列用于和大伙儿一起入门Redis&#xff0c;主要包括Redis的下载&#xff0c;分别在终端&#xff0c;图形显示界面以及JAVA代码中进行使用&#xff0c;适合给需要快速了解Redis是什么以及上手使用的朋友们&#xff0c;希望我用最简单的语言来讲清楚相关内容&#xff…

七牛云C++开发面试题及参考答案

智能指针的原理及应用场景是什么&#xff1f; 智能指针是 C 中用于管理动态分配内存的工具&#xff0c;其核心原理是通过 RAII&#xff08;资源获取即初始化&#xff09;技术&#xff0c;将堆内存的生命周期与对象的生命周期绑定&#xff0c;从而避免手动管理内存带来的内存泄…

【Python办公】Excel横板表头转竖版通用工具(GUI版本)横向到纵向的数据重构

目录 专栏导读前言项目概述功能特性技术栈核心代码解析1. 类结构设计2. 界面布局设计3. 滚动列表实现4. 数据转换核心逻辑5. 预览功能实现设计亮点1. 用户体验优化2. 技术实现优势3. 代码结构优势使用场景扩展建议总结完整代码结尾专栏导读 🌸 欢迎来到Python办公自动化专栏—…

C#项目 在Vue/React前端项目中 使用使用wkeWebBrowser引用并且内部使用iframe网页外链 页面部分白屏

如果是使用wkeWebBrowser的引用方式 非常有可能是版本问题导致的 问题分析 1. wkeWebBrowser 的局限性 不支持或不完全支持 ES6 语法&#xff08;如 let, const, Promise, async/await&#xff09; 缺少对现代 Web API 的支持&#xff08;如 Intl, fetch, WebSocket&#xff0…

系统架构设计师论文分享-论微服务架构

我的软考历程 摘要 2023年2月&#xff0c;我所在的公司通过了研发纱线MES系统的立项&#xff0c;该系统为国内纱线工厂提供SAAS服务&#xff0c;旨在提高纱线工厂的数字化和智能化水平。我在该项目中担任系统架构设计师一职&#xff0c;负责该项目的架构设计工作。本文结合我…

The History of Big Data

数据洪流悄然重塑世界的进程中&#xff0c;大数据的历史是技术迭代与需求驱动的交响。从 2003 年分布式系统雏形初现&#xff0c;到 Hadoop 掀起开源浪潮&#xff0c;再到 Spark、容器化技术与深度学习的接力革新&#xff0c;以及 Hadoop 生态的兴衰起落&#xff0c;大数据发展…

【JS逆向基础】数据分析之正则表达式

前言&#xff1a;前面介绍了关于JS逆向所需的基本知识&#xff0c;比如前端三件套等&#xff0c;从这里开始就要进入到数据分析的范围内了&#xff0c;当然对于一些小白而言一些基本的知识还是需要知道的&#xff0c;比如正则&#xff0c;XPATNY与BS4&#xff1b;三个内容用三篇…

Mac mini 高性价比扩容 + Crossover 游戏实测 全流程手册

Mac mini 高性价比扩容 Crossover 游戏实测 全流程手册 本文将图文并茂地指导你如何&#xff1a; 为 M4 Mac mini 外置扩容&#xff08;绿联 USB4 硬盘盒 致态 TiPlus7100&#xff09;安装并配置 Crossover/Whisky 运行 Windows 应用实测游戏运行性能、诊断常见异常一、准备工…

【PyTorch】PyTorch中torch.nn模块的卷积层

PyTorch深度学习总结 第七章 PyTorch中torch.nn模块的卷积层 文章目录PyTorch深度学习总结前言一、torch.nn模块1. 模块的基本组成部分1.1 层&#xff08;Layers&#xff09;1.2 损失函数&#xff08;Loss Functions&#xff09;1.3 激活函数&#xff08;Activation Functions…

Rust简洁控制流:if let与let else高效编程指南

文章目录Rust简洁控制流&#xff1a;if let与let else高效编程指南&#x1f3af; if let&#xff1a;专注单一匹配场景&#x1f4a1; if let核心优势&#xff1a;&#x1f504; if let与else搭配使用&#x1f680; let else&#xff1a;错误处理与提前返回&#x1f48e; let el…

upload-labs靶场通关详解:第19关 条件竞争(二)

一、分析源代码//index.php // 初始化变量&#xff1a;标记上传状态和错误消息 $is_upload false; $msg null;// 检查是否通过POST方式提交了表单 if (isset($_POST[submit])) {// 引入自定义上传类require_once("./myupload.php");// 生成基于时间戳的文件名&…

一天两道力扣(3)

解法一&#xff1a;class Solution(object):def invertTree(self, root):if not root:return Noneroot.left, root.right root.right, root.leftself.invertTree(root.right)self.invertTree(root.left)return root解析&#xff1a;递归解法二&#xff1a;class Solution(obje…