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
英文地址,請注明出處:http://en.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…