WPF 命令(RoutedCommand自定義命令,實現 ICommand 介面自定義命令)。推薦使用實現 ICommand 介面自定義命令
一、命令基本元素及關係
我們已經知道WPF裡已經有了路由事件,可以釋出及傳播一些訊息,那為什麼還需要命令呢?這是因為事件指負責傳送訊息,對訊息如何處理則不管,而命令是有約束力,每個接收者對命令執行統一的行為,比如選單上的儲存,工具欄上的儲存都必須是執行同樣的儲存。在WPF中,命令(Commanding)被分割成了四個部分,分別是ICommand,ICommandSource,CommandTarget和CommandBinding。下面我們來分別探討這四個部分。
命令(Command):實現了ICommand介面的類,經常使用的有RoutedCommand類
(
命令源:是命令的傳送者,是實現了ICommandSource介面的類,大部分介面的控制元件都實現了這個介面,Button, MenuItem 等等。
(為按鈕設定呼叫的命令 如:按鈕.Command=)
命令目標:命令的接收者,命令目標是視線了IInputElement介面的類。
(設定引發指定命令的元素 如:按鈕.CommandTarget=
命令關聯:負責一些邏輯與命令關聯起來,比如判斷命令是否可以執行,以及執行完畢後做一些處理。
1、CommandBinding :將RoutedCommand 繫結到事件處理程式。
(
//CommandBinding 將命令繫結到事件處理程式
CommandBinding cb = new CommandBinding();
//命令
cb.Command = 命令物件;
//事件處理程式
cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);
cb.Executed += new ExecutedRoutedEventHandler(cb_Executed);
)
2、把命令關聯安置在外圍控制元件上(將命令CommandBinding新增到命令集合CommandBindings 中)
佈局控制元件.CommandBindings.Add(CommandBinding物件);
四個命令元素之間的關係:
例項一:RoutedCommand自定義命令:
與業務邏輯無關的命令,使用 RoutedCommand,業務邏輯要依靠外圍的CommandBinding來實現。這樣一來,如果對CommandBinding管理不善就可能造成程式碼混亂無章,畢竟一個CommandBinding要牽扯到誰是它的宿主以及它的兩的事件處理器。
使用 Button 來發送這個命令,當命令送達到 TextBox 時,TextBox被清空(如果TextBox沒有文字,則命令不可以被髮送)
效果:
程式碼:
<StackPanel x:Name="stackpanel">
<Button x:Name="button1" Content="傳送命令" Margin="5"/>
<TextBox x:Name="textboxA" Margin="5,0" Height="100"/>
</StackPanel>
注意:
RoutedCommand 只負責跑腿,並不對命名目標做任何操作
CommandBinding 對命名目標做操作using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApplication
{
/// <summary>
/// Window9.xaml 的互動邏輯
/// </summary>
public partial class Window9 : Window
{
public Window9()
{
InitializeComponent();
InitializeCommand();
}
//第一步:宣告並定義命令(命令名稱是Clear 所有者型別是Window9)
//(RoutedCommand 只負責跑腿,並不對命名目標做任何操作)
private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(Window9));
private void InitializeCommand()
{
//第二步:把命令賦值給命令源(傳送者),並指定快捷建
this.button1.Command = this.clearCmd;
this.clearCmd.InputGestures.Add(new KeyGesture(Key.C,ModifierKeys.Alt));
//第三步:指定命令的目標
this.button1.CommandTarget = this.textboxA;
//第四步:建立命令關聯(CommandBinding 對命名目標做操作)
CommandBinding cb = new CommandBinding();
cb.Command = this.clearCmd;
cb.CanExecute+=new CanExecuteRoutedEventHandler(cb_CanExecute);
cb.Executed+=new ExecutedRoutedEventHandler(cb_Executed);
//第五步:把命令關聯安置在外圍控制元件上(將命令繫結新增到命令集合中)
this.stackpanel.CommandBindings.Add(cb);
}
//當探測命名是否可以執行時,此方法被呼叫
public void cb_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.textboxA.Text))
{
e.CanExecute = false;
}
else
{
e.CanExecute = true;
}
//避免繼續向上傳而降低程式效能
e.Handled = true;
}
//命令送達目標後,此方法被呼叫
public void cb_Executed(object sender, ExecutedRoutedEventArgs e)
{
this.textboxA.Clear();
//避免繼續向上傳而降低程式效能
e.Handled = true;
}
}
}
例項二:WPF命令庫,比如:複製,貼上。。。
命令庫包括:
ApplicationCommands
ComponentCommands
NavigationCommands
MediaCommands
EditingCommands
它們都是靜態類
例項三:命令中的引數傳遞(同個命令區分,需要引數)
程式碼:
<Window x:Class="WpfApplication.Window10"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window10" Height="300" Width="300">
<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"/>
<!--兩個按鈕都使用了New命令,使用Teacher,Student字串作為引數-->
<Button Content="新老師" Command="New" CommandParameter="Teacher" Grid.Row="2"/>
<Button Content="新學生" Command="New" CommandParameter="Student" Grid.Row="4"/>
<ListBox x:Name="listboxnewItem" Grid.Row="6"/>
</Grid>
<Window.CommandBindings>
<CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed"/>
</Window.CommandBindings>
</Window>
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.listboxnewItem.Items.Add(string.Format("新老師:{0}",name));
}
if (e.Parameter.ToString() == "Student")
{
this.listboxnewItem.Items.Add(string.Format("新學生:{0}", name));
}
}
例項四:命令與Binding結合(Command 屬性呼叫多個命令)
<Button x:Name="dynamicCmdBtn" Command="{Binding Path=ppp,Source=sss}" Content="動態命令"/>
二、近觀命令
1、ICommand介面與RoutedCommand
2、自定義命令(實現 ICommand 介面)
(推薦)
命令:實現 ICommand 介面開始,定義自己的命令並且把某些業務邏輯包含到命令中,這才是真正意義上的自定義命令。
命令源: 實現 ICommandSource介面
命令目標:
第一步:定義介面 IView.cs,並且需要接受命令的控制元件都要實現這個介面,這樣就確保了命令可以成功對它執行操作。
public interface IView
{
bool IsChanged { get; set; }
void SetBinding();
void Refresh();
void Clear();
void Save();
}
第二步:建立命令 。ClearCommand.cs
//命令
//實現ICommand介面,並繼承了CanExecuteChanged事件,CanExecute方法和Execute方法
public class ClearCommand:ICommand
{
//當命令可執行狀態發生改變時,應當被啟用
public event EventHandler CanExecuteChanged;
//用於判斷命令是否可以執行(暫時不實現)
public bool CanExecute(object parameter)
{
throw new NotImplementedException();
}
public void Execute(object parameter)
{
//執行命令,帶與業務相關的Clear邏輯
IView view = parameter as IView;
if (view != null)
{
view.Clear();
}
}
}
執行使用者控制元件MiniView的Clear()方法第三步:命令源 MyCommandSource.cs
//命令源
public class MyCommandSource:UserControl1,ICommandSource
{
//繼承 ICommandSource 三個屬性
public ICommand Command{get;set;}
public object CommandParameter{get;set;}
public System.Windows.IInputElement CommandTarget{get;set;}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
//命令作用於命令目標
if (this.CommandTarget != null)
{
this.Command.Execute(CommandTarget);
}
}
}
CommandTarget 命令目標 如:miniView
Command.Execute 點選滑鼠左鍵,執行ClearCommand.cs中的Execute方法
第四步:命令目標
效果圖:
使用者控制元件:
<UserControl x:Class="WpfApplication.MiniView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="114" Width="200">
<Border CornerRadius="5" BorderBrush="LawnGreen" BorderThickness="2">
<StackPanel>
<TextBox x:Name="textbox1" Margin="5"/>
<TextBox x:Name="textbox2" Margin="5,0"/>
<TextBox x:Name="textbox3" Margin="5"/>
<TextBox x:Name="textbox4" Margin="5,0"/>
</StackPanel>
</Border>
</UserControl>
/// <summary>
/// 命令目標
/// </summary>
public partial class MiniView :UserControl,IView
{
public MiniView()
{
InitializeComponent();
}
public bool IsChanged{get;set;}
public void SetBinding()
{//方法體
}
public void Refresh()
{//方法體
}
public void Save()
{//方法體
}
//用於清除內容的業務邏輯
public void Clear()
{
this.textbox1.Clear();
this.textbox2.Clear();
this.textbox3.Clear();
this.textbox4.Clear();
}
}
第五步:將命令、命令源、命令目標整合起來
效果圖:
程式碼:
<Window x:Class="WpfApplication.Window11"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="Window11" Height="300" Width="300">
<StackPanel>
<local:MyCommandSource x:Name="ctrlClear" Margin="10">
<TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LightBlue" Width="80"/>
</local:MyCommandSource>
<local:MiniView x:Name="miniView"/>
</StackPanel>
</Window>
public Window11()
{
InitializeComponent();
//宣告命令
ClearCommand clearCommand = new ClearCommand();
//命令源命令
this.ctrlClear.Command = clearCommand;
//命令源目標
this.ctrlClear.CommandTarget = this.miniView;
}
//注意:正規方法,應該把命令宣告在靜態全域性的地方,供所有物件使用。
例項:MVVM為按鈕新增事件
DelegateCommand.cs
public class DelegateCommand : ICommand
{
public Action<object> ExecuteCommand = null;
public Func<object, bool> CanExecuteCommand = null;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
if (CanExecuteCommand != null)
{
return this.CanExecuteCommand(parameter);
}
else
{
return true;
}
}
public void Execute(object parameter)
{
if (this.ExecuteCommand != null) this.ExecuteCommand(parameter);
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
ViewModel.cs
public class ViewModel
{
public DelegateCommand BuildCommand { get; set; }
public ViewModel()
{
BuildCommand = new DelegateCommand();
BuildCommand.ExecuteCommand = new Action<object>(Build);
}
private void Build(object obj)
{
MessageBox.Show(obj.ToString());
}
}
View.xaml
<Button Content="生 成" Height="50" Width="100"
Command="{Binding BuildCommand}"
CommandParameter="1"/>