1. 程式人生 > >WPF命令

WPF命令

article 添加 將在 通過 cti window htm 可能 單獨

原文:WPF命令

“有了路由事件為什麽還需要命令系統呢?”。事件的作用是發布、傳播一些消息,消息傳達到了接收者,事件的指令也就算完成了,至於如何響應事件送來的消息事件並不做任何限制,每個接收者可已用自己的行為來響應事件。也就是說,事件不具有約束力。命令和事件的區別就在於命令具有約束力。

命令是為了解除UI和交互邏輯代碼間耦合,以前winform的時候,界面控件通過事件綁定代碼,這樣界面和代碼就無法拆開。

1、WPF命令模型。

WPF命令模型是由許多可變的部分組成,但主要的部分有以下4個重要元素。

1.1)、命令。

命令表示應用程序任務,並且跟蹤任務是否能夠被執行,然而,命令實際上不包含執行應用程序任務的代碼。

1.2)、命令綁定。

每個命令綁定針對用戶界面的具體區域,將命令連接到相關的應用程序邏輯。

1.3)、命令源。

命令源觸發命令,例如MenuItem和Button都是源對象,單擊他們都會執行綁定命令。

1.4)、命令目標。

命令目標是在其中執行命令的元素。

2、ICommand接口。

WPF命令模型的核心是System.WIndows.Input.ICommand接口,該接口中包含了兩個方法和一個事件。

public interface ICommand
{  
    event EventHandler CanExecuteChanged; //當命令狀態改變時,引發該事件。
bool CanExecute(object parameter); //此方法返回命令狀態,如果命令可用則返回true,不可用則返回false。 void Execute(object parameter); //定義在調用此命令時要調用的方法。 }

3、RoutedCommand類。

當創建自己的命令時,不會直接實現ICommand接口,而是使用System.Windows.Input.RoutedCommand類,該類自動實現了ICommand接口,RoutedEvent類是WPF中唯一實現了ICommand接口的類,換句話說,所有WPF命令都是RoutedCommand類及其派生類的實例。為支持路由事件,RoutedCommand類私有地實現了ICommand接口,並添加了ICommand接口方法的一些不同版本,最大的變化就是Excute()方法和CanExcute()方法使用了一個額外的參數

public bool CanExecute(object parameter, IInputElement target); //如果可以對當前命令目標執行此命令,則為 true;否則為 false。
        
public void Execute(object parameter, IInputElement target); //定義在調用此命令時要調用的方法。

4、RouteCommand類。

public class RoutedCommand : ICommand
{    
    public RoutedCommand();
       
    public RoutedCommand(string name, Type ownerType);
        
    public RoutedCommand(string name, Type ownerType, InputGestureCollection inputGestures);

    public InputGestureCollection InputGestures { get; }
    
    //除了Excute()和CanExecute()修改外,還引入了三個屬性:Name、OwnerType和觸發命令的鼠標操作。
        
    public string Name { get; } // 獲取命令的名稱。
       
    public Type OwnerType { get; } //獲取使用命令註冊的類型。
        
    public event EventHandler CanExecuteChanged;    
    //參數Target是處理事件的元素,事件從Target元素開始,然後冒泡至最高層的容器,知道應用程序為了執行合適的任務而處理了事件(為了處理Executed事件,元素還需要借助於另一個類Commanding類的對象)。    
    public bool CanExecute(object parameter, IInputElement target);
       
     public void Execute(object parameter, IInputElement target);
}

5、RoutedUICommand類。

在程序中處理的大部分命令不是RoutedCommand對象,而是RoutedUICommand類的實例,RoutedUICommand類繼承自RoutedCOmmand類,RoutedUIElement類只增加了Text屬性,該屬性是為命令顯示的文本。

6、命令庫。

每個應用程序可能有大量命令,所有基於文檔的應用程序都有他們自己的版本的 New、Open以及Save命令,WPF提供了基本的命令庫,而這些命令庫通過5個專門的靜態類的靜態屬性提供。

a)、ApplicationCommands:該類提供了通用命令,包括剪貼板命令(如Copy、Open、New、Delete、SelectAll、Stop)。

b)、NavigationCommands:該類提供了用於導航的命令。

c)、EditingCommands:該類提供了許多重要的文檔編輯命令。

d)、ComponentCommands:該類提供了由用戶界面組建使用的命令。

e)、MediaCommands:該類提供了一組用於處理多媒體的命令。

7、執行命令。

RoutedUICommands類沒有任何硬編碼的功能,而只是表示命令,為觸發命令,需要有命令源,為響應命令,需要有命令綁定,命令綁定將執行轉發給普通的事件處理程序。

7.1)命令源。

命令庫中的命令始終可用,觸發他們的最簡單方法是將他們關聯到實現了ICommandSource接口的控件,其中包括繼承自ButtonBase類的控件(如Button、CheckBox)、單獨的ListBoxItem對象和MenuItem。

ICommandSource接口的屬性

名稱

說明

Command

指向連接的命令,這是唯一必須的細節

CommandParameter

提供其他希望隨命令發送的數據

CommandTarget

確定將在其中執行命令的元素

<!--使用Command屬性連接到ApplicationCommands.New命令-->
<Button Margin="50" Command="ApplicationCommands.New">命令</Button>

7.2)、命令綁定。

<!--使用Command屬性連接到ApplicationCommands.New命令-->
<Button Margin="50" Command="ApplicationCommands.New">命令</Button>

當將命令關聯到命令源時,會看到一些有趣的現象,命令源會被自動禁用。這是因為按鈕已經查詢了命令的狀態,而且由於命令還沒有與其關聯的綁定,所以按鈕被認為是禁用的。

為改變這種狀態,需要明確做以下三件事:

a)、當命令被觸發時執行什麽操作。

b)、如何確定命令是否能夠被執行 (這是可選的,只要提供了關聯的事件處理程序,命令總是可用的)。

c)、命令在何處起作用。

Xaml代碼:

<!--使用Command屬性連接到ApplicationCommands.New命令-->
<Button Name="btn1" Margin="50" Command="ApplicationCommands.New">命令測試</Button>

後臺代碼:

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    //CommandBinding binding = new CommandBinding(ApplicationCommands.New);
    //CommandBinding:綁定到實現該命令的事件處理程序。
    CommandBinding binding = new CommandBinding();
    //Command屬性:關聯到ICommand。
    binding.Command = ApplicationCommands.New;
    //關聯事件處理程序。
    binding.Executed += Binding_Executed; 
    //將創建的CommandBinding對象添加到包含窗口的CommandBindings集合中,當單機按鈕時,CommandBinding.Executed事件從按鈕冒泡到包含元素。
    this.CommandBindings.Add(binding);
}
        
private void Binding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    MessageBox.Show(e.Source.ToString());
}

單機按鈕,就會觸發Excuted事件,該事件冒泡至窗口,並被上面給出的NewCommand()事件處理程序處理。

在Xaml中創建命令,綁定處理程序:

Xaml代碼:

<Window x:Class="命令測試2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:命令測試2"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"></CommandBinding>
    </Window.CommandBindings>

    <StackPanel Margin="10">
        <Button Command="ApplicationCommands.New">Xaml測試</Button>
    </StackPanel>
</Window>

後臺代碼:

private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    MessageBox.Show(e.Source.ToString());
}

8、使用多命令源。

<Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"></CommandBinding>
</Window.CommandBindings>    
   
<StackPanel Margin="10">        
    <Menu>
        <MenuItem Header="File">
            <MenuItem Command="New"></MenuItem>
        </MenuItem>
    </Menu>        
</StackPanel>

技術分享圖片

MenuItem類足夠智能,如果沒有設置Header屬性,他將從命令中提取文本,如上面Xaml中提取了"新建",MenuItem類還會自動提取Commad.InputBindings集合中的第一個快捷鍵(如果存在快捷鍵的話)。

9、微調命令文本。

說白了就是提取命令中的字體。

 <Button Margin="5" Command="New" Content="{Binding RelativeSource={RelativeSource Self},Path=Command.Text}" Height="40"></Button>

技術分享圖片

10、直接調用命令。

並且只能使用實現了ICommandSource接口的類來觸發執行的命令,也可以使用Execute()方法直接調用來自任何事件處理程序的方法,這時需要傳遞參數值(或null引用),和目標元素的引用。

語法:ApplicationCommands.New.Execute(null, targetElement);

Xaml代碼:

<Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed"></CommandBinding>
 </Window.CommandBindings>

<StackPanel Margin="10">
    <Button Name="btn2" Height="50">Hello,Test</Button>
</StackPanel>

後臺代碼:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(Command_Test);
    }

    private void Command_Test(object sender, RoutedEventArgs e)
    {
        //也可在關聯的CommandBinding對象中調用Excute()方法,這種情況下不需要提供目標元素,
        //會自動將公開正在使用CommandBinding集合的元素設置為目標元素。
        this.CommandBindings[0].Command.Execute(null); 
        
         ApplicationCommands.New.Execute(null, btn2); //目標元素是WPF開始查找命令的地方(實際引發事件的元素)
    }

    private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show(e.Source.ToString());
    }
}

11、禁用命令(通過設置CanExecute屬性來更改按鈕狀態)。

如果想要創建狀態在啟動和禁用之間變化的命令,將會體會到命令模型的真正優勢。

Xaml代碼:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <TextBox TextChanged="textBox1_TextChanged" Name="textBox1" Margin="10"></TextBox>
    <Button Command="Save" Grid.Row="1" Width="250" Height="60">當TextBox值改變時,此按鈕才可以點擊</Button>        
</Grid>

後臺代碼:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        //創建命令對象。
        CommandBinding binding = new CommandBinding();
        //設置關聯的命令。
        binding.Command = ApplicationCommands.Save;
        //命令處理程序。
        binding.Executed += Binding_Executed;
        //檢查是否可用,true:可用,false:不可用。
        binding.CanExecute += Binding_CanExecute;
        //將命令加入到CommandBindings集合中。
        this.CommandBindings.Add(binding);
    }

    //設置狀態標誌。
    bool drag = false;
    private void Binding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = drag;
    }

    private void Binding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
        MessageBox.Show("保存成功!");   
    }

    private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
    {
        //如果文本框中的值改變了,就設置為可用狀態。
        drag = true;
    }
}

12、具有內置命令的控件(主要用於Menu控件或ToolBar控件)。

<TextBox  Name="textBox1" Margin="10"></TextBox>
<ToolBar Grid.Row="1" Margin="10,-23,-9.6,22">
    <Button Command="Copy">復制</Button>
    <Button Command="Paste">粘貼</Button>
    <Button Command="Cut">剪切</Button>
</ToolBar>

如果不是Menu控件和ToolBar控件,需要借助FocusManager.IsFocusScope屬性。

<TextBox  Name="textBox1" Margin="10"></TextBox>
<StackPanel Grid.Row="1" FocusManager.IsFocusScope="True">
    <Button  Command="Copy" Height="30">復制</Button>
    <Button  Command="Paste" Height="30">粘貼</Button>
    <Button  Command="Cut" Height="30">剪切</Button>
</StackPanel>

WPF命令