WPFヘルパー:RelayCommand.cs – ICommand の簡易実装クラス。

コンピュータ

ファイル名:Helpers\RelayCommand.cs

using System.Windows.Input;

namespace Maywork.WPF.Helpers;

public sealed 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 ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object? parameter)
        => _canExecute?.Invoke(parameter) ?? true;

    public void Execute(object? parameter)
        => _execute(parameter);

    public event EventHandler? CanExecuteChanged;

    public void RaiseCanExecuteChanged()
        => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

/*
// 使い方
// ICommand の簡易実装クラス。
// execute : 実行時に呼ばれる処理
// canExecute : 実行可否を判定する処理(省略時は常に true)
// RaiseCanExecuteChanged() を呼ぶことでボタン状態を更新可能。

// 使用例

public RelayCommand SubmitCommand { get; }

public MainViewModel()
{
    SubmitCommand = new RelayCommand(
        execute: _ => MessageBox.Show($"Title: {Title}"),
        canExecute: _ => !IsEmpty
    );
}
 
 */
Download

利用例(クリックで展開)

ファイル名:App.xaml

<Application x:Class="ViewModelBaseDemo.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ViewModelBaseDemo"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
         
    </Application.Resources>
</Application>

ファイル名:App.xaml.cs

using System.Configuration;
using System.Data;
using System.Windows;

namespace ViewModelBaseDemo;

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}


ファイル名:Helpers\ViewModelBase.cs

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Maywork.WPF.Helpers;


public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string? name = null)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

    protected bool SetProperty<T>(
        ref T field,
        T value,
        Action<T>? onChanged = null,
        [CallerMemberName] string? propertyName = null)
    {
        if (Equals(field, value))
            return false;

        field = value;

        onChanged?.Invoke(value);

        OnPropertyChanged(propertyName);

        return true;
    }
}

/*
// 使い方

// INotifyPropertyChanged の実装を簡略化する基底クラス。
// SetProperty() は値変更時のみ通知を行い、
// onChanged デリゲートで追加処理を記述できます。

// 使用例

using Maywork.WPF.Helpers;

namespace ViewModelBaseDemo;

internal class MainViewModel : ViewModelBase
{
    private string _title = "";

    public string Title
    {
        get => _title;
        set => SetProperty(ref _title, value);
    }
}

 */

ファイル名:MainViewModel.cs

using Maywork.WPF.Helpers;
using System.Windows;

namespace ViewModelBaseDemo;

public class MainViewModel : ViewModelBase
{
    private string _title = "";

    public string Title
    {
        get => _title;
        set => SetProperty(ref _title, value, _ =>
        {
            OnPropertyChanged(nameof(IsEmpty));
            SubmitCommand.RaiseCanExecuteChanged();
        });
    }

    public bool IsEmpty => string.IsNullOrWhiteSpace(_title);

    public RelayCommand SubmitCommand { get; }

    public MainViewModel()
    {
        SubmitCommand = new RelayCommand(
            execute: _ => MessageBox.Show($"Title: {Title}"),
            canExecute: _ => !IsEmpty
        );
    }
}

ファイル名:MainWindow.xaml

<Window x:Class="ViewModelBaseDemo.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:ViewModelBaseDemo"
        mc:Ignorable="d"
        Title="{Binding Title}" Height="450" Width="800">
    <Grid>
        <StackPanel Margin="20">
            <TextBox Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}" />
            <Button Content="Submit"
            Command="{Binding SubmitCommand}"
            Margin="0,10,0,0"/>
        </StackPanel>
    </Grid>
</Window>

ファイル名:MainWindow.xaml.cs

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.Navigation;
using System.Windows.Shapes;

namespace ViewModelBaseDemo;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var vm = new MainViewModel();
        this.DataContext = vm;

        this.Loaded += (_, __) =>
        {
            vm.Title = "タイトルが変更された";
        };
    }
}

ファイル名:ViewModelBaseDemo.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net10.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

コメント