C#(WPF)勉強メモ-ICommandを使用したログインフォームの作成

1. はじめに

 C#勉強メモでは、筆者が学んだことをまとめたものである。今回は、ICommandインタフェースを使用してログインフォームを作成する。

2. 作成するログインフォーム

 今回は、下記のログインフォームを目標とする(図1)。

図1 目標とするログインフォーム

仕様:
(1)  ウィンドウにIDとパスワードの入力フォームを作成する。
(2) IDとパスワードを入力し、ログインボタンを押すと、ログイン成功と表示する。
(3) ただし、未入力がある場合は、入力エラーと表示する。

3. 画面を作成する

 XAMLで画面を作成する。

<Window x:Class="WpfApp3.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:WpfApp3"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="420">
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBlock Text="ID:" Grid.Row="0" VerticalAlignment="Center"/>
        <TextBox x:Name="IdTextBox"  Grid.Row="0" Margin="50,0,0,5" Width="200" Text="{Binding UserId, UpdateSourceTrigger=PropertyChanged}" />

        <TextBlock Text="パスワード:" Grid.Row="1" VerticalAlignment="Center"/>
        <TextBox x:Name="PasswordTextBox" Grid.Row="1" Margin="50,0,0,5" Width="200"
         Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" />
        
        <Button Content="ログイン" Grid.Row="2" Margin="130,26,130,-16" Width="100" Height="50"
                Command="{Binding LoginCommand}"/>
    </Grid>
</Window>

 上記を実行すると、下記の画面を表示する。

 今回、特に注目したいのが、Commandである。

<Button Content="ログイン" Grid.Row="2" Margin="130,26,130,-16" Width="100" Height="50"
    Command="{Binding LoginCommand}"/>

 Commandキーワードでバインディングしておくことで、XAMLと実装コードを分離することができる。また、ICommandインタフェースを使用して、コマンドの実行条件を動的に制御することができる。

 もう一点が、UpdateSourceTrigger=PropertyChangedである。

Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"

 UpdateSourceTriggerとは、バインドしたプロパティを更新するタイミングを指定する。今回は、テキストボックスを更新するタイミングでPasswordプロパティに反映する。

4. ICommandインタフェース

 3項で、Command={Binding…}と記述している。これは、Commandインタフェースを使用してコントロールを制御することを意味している。ICommandインタフェースの中身を確認する。

public interface ICommand
{
    event EventHandler? CanExecuteChanged;
    bool CanExecute(object? parameter);
    void Execute(object? parameter);
}

 ICommandの中身はインタフェースで、CanExecuteChangedというイベントハンドラとCanExecute、Executeというメソッドがある。

4.1 CanExecute

 CanExecuteでは、コマンドが実行可能な状態であるか判断するメソッドである。

 メソッドのシグネチャは、以下のようになっている。

bool CanExecute(object? parameter);

//記述例
public bool CanExecute(object parameter)
{
    //実行可能
    return true;
}

public bool CanExecute(object parameter)
{
    //実行不可
    return false;
}

 trueだと実行可能で、falseだと実行不可を表す。例えば、IDとパスワードが入力されていない場合は、ログインボタンを押せないとする。この場合は、入力フォームをチェックし、どちらかが空の場合はfalseすることで、ボタンを無効状態にする。

4.2 CanExecuteChangedイベント

 CanExecuteChangedは、GUIの変更があった場合に、CanExecuteを呼び出すイベントである。これによって、イベントに従って監視をして置き、ボタンの有効・無効などを設定できる。

4.3 Execute

 Executeは、コマンドを実行したときに呼び出すメソッドである。

void Execute(object? parameter);

 例えば、ボタンクリックした場合は、ここが呼びされる。

4.4 例題

 まず、ユーザIDとパスワードを管理するクラスを定義する。


//UserInfo.cs
namespace WpfApp3
{
    public class UserInfo
    {
        public string UserId;         //ユーザID
        public string Password;       //パスワード
    }
}

 次に、MainWindow.csを定義する。

//MainWindow.cs
using System.Windows;
using System.Windows.Input;

namespace WpfApp3
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new LoginViewModel();
        }
    }
}

 最後に、LoginViewModel.csを定義する。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApp3
{
    public class LoginViewModel : INotifyPropertyChanged
    {
        UserInfo _userInfo = new UserInfo();
        private RelayCommand _loginCommand; //ログインコマンド
        
        public string UserId
        {
            get { return _userInfo.UserId; }
            set
            {
                if (_userInfo.UserId != value)
                {
                    _userInfo.UserId = value;
                    OnPropertyChanged(nameof(UserId));
                    _loginCommand.RaiseCanExecuteChanged();
                }
            }
        }

        public string Password
        {
            get { return _userInfo.Password; }
            set
            {
                if (_userInfo.Password != value)
                {
                    _userInfo.Password = value;
                    OnPropertyChanged(nameof(Password));
                    _loginCommand.RaiseCanExecuteChanged();
                }
            }
        }
        public ICommand LoginCommand => _loginCommand;


        public LoginViewModel()
        {
            _loginCommand = new RelayCommand(ExecuteLogin, CanExecuteLogin);
        }

        private void ExecuteLogin(object parameter)
        {
            MessageBox.Show("ログインOK");
        }

        private bool CanExecuteLogin(object parameter)
        {
            //テキストボックスがnullであるか判定する
            return !string.IsNullOrWhiteSpace(UserId) && !string.IsNullOrWhiteSpace(Password);
        }

        //プロパティの変更通知
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;

        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }
            return _canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }
}

 結果、簡単なログインフォームが作成できた。


いいなと思ったら応援しよう!