rksoftware

Visual Studio とか C# とかが好きです

まだまだ現役! Windows フォームアプリの新機能を確認する (5)

devblogs.microsoft.com

最近あまり面白い話題が少ない感じでしたがついに来ました。みんな大好き Windows フォームアプリのお話です。
↑ のブログ記事。見ていきましょう!

※ 現在 WPF でなく WinForms を愛用している日本の IT 技術者が WinForms アプリを MVVM で作りたいと思っているかは別として。

■ OnRequestCommandExecute メソッド

ButtonBase クラスと ToolStripItem クラスに OnRequestCommandExecute メソッドが追加されたとのことです。
ButtonBase クラス、確かに、バージョン 7 からとなっています。
learn.microsoft.com

ToolStrigItem クラスには見当たりません......。 learn.microsoft.com しかし Visual Studio で見ると確かにいますね。

このメソッドを override することで、Command の呼び出しを制御できるようですね。 試してみましょう。

■ 検証コード

public class BindableBase : System.ComponentModel.INotifyPropertyChanged
{
    public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;

    protected bool SetProperty<T>(ref T property, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
    {
        if (Object.Equals(property, value)) return false;
        property = value;
        PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        return true;
    }
}

class Command : System.Windows.Input.ICommand
{
    public event EventHandler? CanExecuteChanged;

    private readonly Action<object?> _commandAction;
    private readonly Func<bool>? _canExecuteCommandAction;

    public Command(Action<object?> commandAction, Func<bool>? canExecuteCommandAction = null)
    {
        _commandAction = commandAction;
        _canExecuteCommandAction = canExecuteCommandAction;
    }

    public bool CanExecute(object? parameter) => _canExecuteCommandAction?.Invoke() ?? true;
    public void Execute(object? parameter) => _commandAction.Invoke(parameter);
    public void NotifyCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

public class ViewModel : BindableBase
{
    bool _enabled = true;
    private string _text;
    public string Text { get => _text; private set => SetProperty(ref _text, value); }
    public System.Windows.Input.ICommand ButtonCommand { get; init; }

    public ViewModel()
    {
        ButtonCommand = new Command(((parameter) => Text += $"Button clicked: {parameter?.ToString()}\n"), canExecute);

        bool canExecute() => _enabled;
    }
}
namespace WinFormsApp1
{
    public partial class Form1 : Form
    {
        class MyButton : System.Windows.Forms.Button
        {
            public bool CanRequestExecute { get; set; } = true;
            protected override void OnRequestCommandExecute(EventArgs e) { if (CanRequestExecute) base.OnRequestCommandExecute(e); }
        }

        public Form1()
        {
            this.components = new System.ComponentModel.Container();
            InitializeComponent();

            var viewModel = new ViewModel();
            var bindingSource = new System.Windows.Forms.BindingSource(components);
            bindingSource.DataSource = viewModel;

            var button = new MyButton
            {
                AutoSize = true,
                Location = new System.Drawing.Point(16, 32),
                Size = new System.Drawing.Size(94, 29),
                Text = "button1",
                UseVisualStyleBackColor = true,
            };
            var checkBox = new System.Windows.Forms.CheckBox
            {
                AutoSize = true,
                Location = new System.Drawing.Point(16, 76),
                Size = new System.Drawing.Size(101, 24),
                Text = "checkBox1",
                UseVisualStyleBackColor = true,
                Checked = true,
            };
            var label = new System.Windows.Forms.Label
            {
                AutoSize = true,
                Location = new System.Drawing.Point(20, 172),
                Size = new System.Drawing.Size(50, 20),
                Text = "label1",
            };
            button.DataBindings.Add(new System.Windows.Forms.Binding("Command", bindingSource, "ButtonCommand", true));
            label.DataBindings.Add(new System.Windows.Forms.Binding("Text", bindingSource, "Text", true));
            this.Controls.Add(button);
            this.Controls.Add(checkBox);
            this.Controls.Add(label);

            checkBox.CheckedChanged += (object? sender, EventArgs e) => button.CanRequestExecute = ((CheckBox?)sender)?.Checked ?? true;
        }
    }
}

実行結果

計算通り! CheckBox の ON/OFF を切り替えるとボタンの Command が実行されたりされなかったりします。画像ではわからないですけど。

■ コードの要点

ボタンの派生クラスを作って OnRequestCommandExecute メソッドを override しています。

class MyButton : System.Windows.Forms.Button
{
    public bool CanRequestExecute { get; set; } = true;
    protected override void OnRequestCommandExecute(EventArgs e) { if (CanRequestExecute) base.OnRequestCommandExecute(e); }
}

次回

次回は DataContext プロパティを見ていきましょう。