1. 程式人生 > >WPF 命令(RoutedCommand自定義命令,實現 ICommand 介面自定義命令)。推薦使用實現 ICommand 介面自定義命令

WPF 命令(RoutedCommand自定義命令,實現 ICommand 介面自定義命令)。推薦使用實現 ICommand 介面自定義命令

一、命令基本元素及關係

      我們已經知道WPF裡已經有了路由事件,可以釋出及傳播一些訊息,那為什麼還需要命令呢?這是因為事件指負責傳送訊息,對訊息如何處理則不管,而命令是有約束力,每個接收者對命令執行統一的行為,比如選單上的儲存,工具欄上的儲存都必須是執行同樣的儲存。

在WPF中,命令(Commanding)被分割成了四個部分,分別是ICommand,ICommandSource,CommandTarget和CommandBinding。下面我們來分別探討這四個部分。

   命令(Command):實現了ICommand介面的類,經常使用的有RoutedCommand類

           (

private RoutedCommand clearCmd = newRoutedCommand("序列化的宣告名稱", typeof(所有者型別));)

   命令源:是命令的傳送者,是實現了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"/>