wpf dockpanel,WPF命令

 2023-10-06 阅读 21 评论 0

摘要:? WPF的命令是經常使用的,在MVVM中,RelayCommand更是用得非常多,但是命令的本質究竟是什么,有了事件為什么還要命令,命令與事件的區別是什么呢?MVVM里面是如何包裝命令的呢?命令為什么能夠觸發呢?帶著這些疑問ÿ

?

WPF的命令是經常使用的,在MVVM中,RelayCommand更是用得非常多,但是命令的本質究竟是什么,有了事件為什么還要命令,命令與事件的區別是什么呢?MVVM里面是如何包裝命令的呢?命令為什么能夠觸發呢?帶著這些疑問,我們深入講解下命令:

首先看看命令系統的幾個基本元素:

1) 命令(Command):實現了ICommand接口的類,用得最多的是RoutedCommand.

  ICommand的成員:

  event EventHandler CanExecuteChanged;

  bool CanExecute(object parameter);確定此命令能否執行的方法

  void Execute(object parameter);執行命令調用的方法

?

2)?命令源(Command Source):即命令的發送者,是實現了ICommandSource接口的類,很多界面元素都實現了這個接口,其中包括Button, MenuItem, ListBoxItem等。

  ICommandSource成員:

  ?????? ICommand Command{get;}???獲取將在調用命令源時執行的命令

    object CommandParameter{get; }?命令參數。

??????????? IInputElement CommandTarget{get;}?將在其上執行命令的對象。

?

3)命令目標(Command Target):即命令講發送給誰,或者說命令將作用在誰身上。命令目標必須是實現了IInputElement接口的類。

4)命令關聯(Command Binding):負責把一些外圍邏輯與命令關聯起來,比如執行之前對命令是否可以執行進行判斷、命令執行之后還有哪些后續工作。

  CommandBinding的成員:

  ? public ICommand Command{get; set;}?與這個CommandBinding關聯的ICommand。

   public event CanExecuteRoutedEventHandler CanExecute;

???????public event ExecutedRoutedEventHandler Executed;

?????? public event CanExecuteRoutedEventHandler PreviewCanExecute;

?????? public event ExecuteRoutedEventHandler PreviewExecuted;

?下面先看看一個命令方面的例子:

代碼:

 1   private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(MainWindow));
 2         private void InitializeCommand()
 3         {
 4             this.button1.Command = clearCmd;
 5             this.clearCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
 6             this.button1.CommandTarget = this.textBoxA;
 7 
 8             CommandBinding cb = new CommandBinding();
 9             cb.Command = this.clearCmd;
10             cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExcecute);
11             cb.Executed += new ExecutedRoutedEventHandler(cb_Executed);
12 
13             this.stackPanel.CommandBindings.Add(cb);
14 
15         }
16 
17         void cb_CanExcecute(object sender, CanExecuteRoutedEventArgs e)
18         {
19             if (string.IsNullOrEmpty(this.textBoxA.Text))
20             {
21                 e.CanExecute = false;
22             }
23             else
24             {
25                 e.CanExecute = true;
26             }
27             e.Handled = true;
28         }
29 
30         void cb_Executed(object sender, ExecutedRoutedEventArgs e)
31         {
32             this.textBoxA.Clear();
33             e.Handled = true;
34         }
View Code

UI上一個按鈕,一個文本框,上級UI是StackPanel。
實現的功能是當文本框里面沒有內容的時候,按鈕是不是能的,當有內容的時候,按鈕使能。

我們來對照著命令的幾大要素,來分析下這個例子:

首先一個clearCmd的路由命令,RoutedCommand,實現了ICommand接口。

然后就是命令源,這里是Button,它實現了ICommandSource接口,在這里把clearCmd路由命令賦值給了其成員Command,把文本框賦值給了命令的目標。

然后命令目標就是文本框,實現了IInputElement接口

最后就是CommandBinding,這里給StackPanel的CommandBindings賦值,其中CommandBinding的Command賦值為clearCmd,并且定義兩個事件驅動程序,用來處理CanExecute,Execute。

另外還有快捷鍵的設置方式,這不是重點,我們重點看看命令的執行模式到底如何?工作原理是什么?

通過調查,我發現是這樣的:

首先命令源會一直查詢其Command,如果有就執行命令,然后就連接到了命令目標,命令目標激發路由事件,然后在外圍的控件的CommandBinding監控下捕捉相關的路由事件,然后就會調用相關的事件處理程序。

對應這個例子,是這樣的,button作為命令源,賦值了clearCmd命令,然后通過查詢并執行這個命令,命令連接到命令目標文本框,然后文本框激發出路由事件clearCmd,然后安裝在StackPanel的CommandBinding監控下,如果是找到的命令的能否執行命令,就執行能否執行命令的事件處理函數,這里的能否執行返回的值直接決定了命令源是否可用。如果找到的命令式執行命令,就執行執行命令的處理函數,這里執行就把文本框清空。為了提高效率,一般都要e.Handled?=?True.

我們可以看出,真正起作用的是CommandBinding,命令源的目的是告訴命令目標發了命令,還有讓命令目標激光路由事件,命令目標的目的就是發生路由事件,CommandBinding賦值監聽命令,執行命令。

用通俗的話說,命令源就相當于火炮,命令相當于炮彈,命令目標相當于跑到要打的目標,命令關聯就詳單與偵察兵,在打炮彈之前的觀察敵情,以及打掃戰場等事情。

命令源會不斷的像命令目標投石問路,命令目標就會不斷的發送路由事件PreviewCanExcecute和CanExcecute附加事件,命令被發送出來并達到命令目標,命令目標就會發送PreviewExecuted和Executed附加事件。命令關聯捕捉到后,就會執行一些任務了。

?

我們再來看看另外一個例子,我們自定義命令的例子:

 1   /// <summary>
 2     /// CustomRoutedCommand.xaml 的交互邏輯
 3     /// </summary>
 4     public partial class CustomRoutedCommand : UserControl,IView
 5     {
 6         public CustomRoutedCommand()
 7         {
 8             InitializeComponent();
 9         }
10 
11         public bool IsChanged { get; set; }
12         public void SetBinding() { }
13         public void Refresh(){}
14         public void Save() { }
15         public void Clear()
16         {
17             this.textBox1.Clear();
18             this.textBox2.Clear();
19             this.textBox3.Clear();
20             this.textBox4.Clear();
21         }
22     }
23 
24     public interface IView
25     {
26         bool IsChanged { get; set; }
27         void SetBinding();
28         void Refresh();
29         void Clear();
30         void Save();
31     }
32 
33     public class ClearCommand : ICommand
34     {
35         public bool CanExecute(object parameter)
36         {
37             throw new NotImplementedException();
38         }
39 
40         public event EventHandler CanExecuteChanged;
41 
42         public void Execute(object parameter)
43         {
44             IView view = parameter as IView;
45             if (view != null)
46             {
47                 view.Clear();
48             }           
49             //throw new NotImplementedException();
50         }
51     }
52 
53     public class MyCommandSource : UserControl, ICommandSource
54     {
55         public ICommand Command { get; set; }
56         public object CommandParameter { get; set; }
57         public IInputElement CommandTarget { get; set; }
58 
59         protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
60         {
61             base.OnMouseLeftButtonDown(e);
62             if (CommandTarget != null)
63             {
64                 this.Command.Execute(CommandTarget);
65             }
66         }
67       
68     }
View Code

也是點擊一個按鈕把4個文本框全部清空。
同樣的我們來分析下各個要素:

命令:ClearCommand,?實現了ICommand接口,這里的命令執行方法,是執行命令參數的清除方法。

命令源:MyCommandSource,這是一個用戶控件,實現了ICommandSource接口,左鍵按鈕里面如果有命令目標,就執行命令方法。

命令目標是一個窗體,他實現了IView接口,接口里面有個清除方法。

這里沒有CommandBinding。實際執行的代碼就是命令目標的IView接口的方法。

當我們點擊按鈕的時候,觸發了命令的執行,命令執行調用IView接口的方法。

這個自定義的過程要比上面那個例子要好理解一點。

上面的那個例子存在命令源,路由命令和CommandBinding三者之間的關系,命令并不真正執行邏輯代碼,是靠CommandBinding來實現邏輯的,當我們真正自定義的命令的時候,如果想簡單的使用命令,我們可以把邏輯放到命令里面去,這樣便于管理,以上的自定義命令的例子就是這樣。

至于MVVM里面的RelayCommand命令,一樣也是實現的ICommand接口。當我們把命令通過binding賦值給命令源的Command后,當命令源的命令觸發的時候,就執行RelayCommand的方法,這個方法是一個委托方法,這樣我們就可以通過Binding來把聯系了控件和控件要執行的行為。

命令跟事件可能否是在一起被觸發的,比如在ButtonBase的OnClick方法里面是這樣的:

protected virtual void OnClick()
{
??????? RoutedEventArgs e = new RoutedEventArgs(ButtonBase.ClickEvent, this);
?????? base.RaiseEvent(e);
?????? CommandHelpers.ExecuteCommandSource(this);
}

可以看出在Click里面,先激發路由事件,再執行命令。

總結:對于命令而言,我們說的幾大要素,命令,命令源,命令目標,命令關聯,在路由命令中,一般都是存在的,命令在命令源的激發下,到命令目標,有可能沒有命令目標,通過命令關聯來監控并執行邏輯方面的事情。但是我們一般使用比如MVVM里面的RelayCommand,一般邏輯代碼都是放在命令里面的,一般沒有命令目標及命令關聯。

到目前為止,雖然大致對命令有了個了解,但是對于WPF預定義的一些命令沒有完全理解,以及對于命令的好處也沒有完全理解,以后隨著使用越來越多,再去總結吧。

http://files.cnblogs.com/files/monkeyZhong/RoutedCommandEg.zip

?

轉載于:https://www.cnblogs.com/monkeyZhong/p/4600293.html

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/119870.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息