レガシーコードからの脱却

レガシーな職場でも独学でどうにか頑張っていくブログです。

【お勉強記録】WPF コマンド

久しぶりにWPF +MVVM関係です。 データバインディングについては基本的な部分は以前やりまして、今回はコマンドについて勉強しました。

コマンド

コマンドはXamlでの操作に対応して、実行される処理を定義したもの?みたいです。 コマンドとバインディングするにはICommandインターフェースの実装したクラスが必要なようです。

そのICommandインターフェースを見てみると以下のようになっています。

    // 概要:
    //     コマンドを定義します。
    public interface ICommand
    {
        // 概要:
        //     コマンドを実行するかどうかに影響するような変更があった場合に発生します。
        event EventHandler CanExecuteChanged;

        // 概要:
        //     現在の状態でこのコマンドを実行できるかどうかを判断するメソッドを定義します。
        //
        // パラメーター:
        //   parameter:
        //     コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。
        //
        // 戻り値:
        //     このコマンドを実行できる場合は true。それ以外の場合は false。
        bool CanExecute(object parameter);
        //
        // 概要:
        //     コマンドの起動時に呼び出されるメソッドを定義します。
        //
        // パラメーター:
        //   parameter:
        //     コマンドで使用されたデータ。 コマンドにデータを渡す必要がない場合は、このオブジェクトを null に設定できます。
        void Execute(object parameter);
    }

コマンドで実行される実処理を担当する部分としてExecuteとコマンドが実行可能かを判断する他の部分に分かれています。

サンプル

理解のために実際に書いてみました。 「テキストボックスに何かしらの値が入っていれば、実行ボタンが押せる。(=何か入れないと実行できない。)」

ますはcs側から…。

ExecuteCommandクラスでICommandを実装しています。 CanExecuteでは、実行可能になる条件を、Executeでは実行処理をそれぞれ実装しています。 実装したExexuteCommandクラスを、実際にXamlと結ばれるCommandViewModelでプロパティとして使用しています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace Command
{
    public class CommandViewModel : INotifyPropertyChanged
    {
        
        public string Text
        {
            get { return text; }
            set
            {
                text = value;
                NotifyPropertyChange("Text");
            }
        }
        private string text;
        
        public ICommand Command { get { return command ?? (command = new ExecuteCommand(this)); } }
        private ICommand command;

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChange([CallerMemberName]string propertyName = null)
        {
            if (this.PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        private class ExecuteCommand: ICommand
        {
            private CommandViewModel commandViewModel;
            //コンストラクタ
            public ExecuteCommand(CommandViewModel viewModel)
            {
                commandViewModel = viewModel;

                commandViewModel.PropertyChanged += (sender, e) =>
                {
                    if (CanExecuteChanged != null)
                    {
                        CanExecuteChanged(sender, e);
                    }
                };
            }

            public bool CanExecute(object parameter)
            {
                return !String.IsNullOrEmpty(commandViewModel.Text);
            }

            public event EventHandler CanExecuteChanged;

            public void Execute(object param)
            {
                MessageBox.Show("実行");
            }
        }
    }
}

Xaml側では、DataContextプロパティで先ほどのCommandViewModelをセットして、 ButtonのCommandプロパティにバインドしています。

<Window x:Class="Command.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Command"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:CommandViewModel x:Name="viewModel"/>
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Text}"></TextBox>
        <Button Content="Focus用"></Button>
        <Button Content="実行" Command="{Binding Command}"></Button>        
    </StackPanel>
</Window>

以下実行結果です。

f:id:Gappory:20170827225142p:plain

実行と書いてあるボタンが押下不可状態になっています。

f:id:Gappory:20170827225241p:plain

テキストボックスに何らか入力すると実行可能に!(今回はサンプルだしフォーカスが移動必要なままです。)

ちなみに押すと「実行」とメッセージボックスが出ます。

次回以降はMVVM

データバインディングとコマンドを一通り見られたので、いよいよ今後はMVVMについて学んでいく予定です。 まずはTodoアプリでも作ってみたいと思います。