WPFでRoutedUICommand を生成する Helper を使う最小サンプル

コンピュータ

XAMLではデータバインディングが基本となり、ボタンを押した場合のアクションはコマンド(ICommand)とバインディングすることになります。
では、コマンドで何をしているかと言いますと、実際処理を行うメソッドの呼び出しが主な内容となります。
本格的にはICommandを実装したクラスが必要となりますが、メソッドを呼び出すだけの為にクラスを作るのは大仰かと思い、
簡易的なコードでコマンドを実現してみたいと思います。

サンプルコード

ファイル名:RoutedCommandSample.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>

ファイル名:Helpers\RoutedCommandHelper.cs

using System.Windows;
using System.Windows.Input;

namespace RoutedCommandSample.Helpers;

public static class RoutedCommandHelper
{
    public static RoutedUICommand Create(
        Window window,
        string? name,
        Action execute,
        Func<bool>? canExecute = null,
        Key? key = null,
        ModifierKeys modifiers = ModifierKeys.None)
    {
        var cmd = name == null
            ? new RoutedUICommand()
            : new RoutedUICommand(name, name, window.GetType());

        ExecutedRoutedEventHandler exec = (_, __) =>
            execute();

        CanExecuteRoutedEventHandler can = (_, e) =>
            e.CanExecute = canExecute?.Invoke() ?? true;

        window.CommandBindings.Add(
            new CommandBinding(cmd, exec, can));

        if (key != null)
        {
            window.InputBindings.Add(
                new KeyBinding(cmd, key.Value, modifiers));
        }

        return cmd;
    }
}

RoutedUICommandオブジェクトを生成するファクトリメソッドです。


ファイル名:MainWindow.xaml.cs

using System.Windows;
using System.Windows.Input;
using RoutedCommandSample.Helpers;

namespace RoutedCommandSample;

public partial class MainWindow : Window
{
    public RoutedUICommand OpenCommand { get; private set; } = null!;
    public RoutedUICommand ExitCommand { get; private set; } = null!;

    bool _canOpen = true;

    public MainWindow()
    {
        InitializeComponent();

        OpenCommand = RoutedCommandHelper.Create(
            window: this,
            name: "Open",
            execute: OpenFile,
            canExecute: () => _canOpen,
            key: Key.O,
            modifiers: ModifierKeys.Control);

        ExitCommand = RoutedCommandHelper.Create(
            window: this,
            name: "Exit",
            execute: Close,
            key: Key.Q,
            modifiers: ModifierKeys.Control);
        
        DataContext = this;
    }

    void OpenFile()
    {
        MessageBox.Show("Open executed");

        // CanExecute の変化確認用
        _canOpen = false;
        CommandManager.InvalidateRequerySuggested();
    }
}

XAMLから呼び出せるコマンドOpenCommandと実際のメソッドOpenFile()をRoutedUICommandで紐づけを行います。
RoutedUICommandの生成は少し面倒なのでHelperとしてファクトリメソッドを作りました。


ファイル名:MainWindow.xaml

<Window
    x:Class="RoutedCommandSample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="RoutedCommand Sample"
    Width="400"
    Height="200">

    <DockPanel>

        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open"
                          Command="{Binding OpenCommand}" />
                <MenuItem Header="Exit"
                          Command="{Binding ExitCommand}" />
            </MenuItem>
        </Menu>

        <StackPanel Margin="20" VerticalAlignment="Center">

            <Button
                Height="30"
                Margin="0,0,0,10"
                Content="Open (Ctrl+O)"
                Command="{Binding OpenCommand}" />

            <Button
                Height="30"
                Content="Exit (Ctrl+Q)"
                Command="{Binding ExitCommand}" />

        </StackPanel>

    </DockPanel>
</Window>

メニューとボタンで同じコマンドにバインディングしています。


実行例

スクリーンショット

RoutedUICommandはマイクロソフトが用意してくれているICommandの実装になります。
サンプルコードの通り、XAMLのボタンやメニューとバインドし機能することが確認出来ました。
ボタンやメニューを押すアクションとのバインドの他にショートカットキーとのバインドも同時に行うことが出来ます。

コメント