WPF AttachedPropertyをコードビハインドから使う方法(Commandバインディング実装例)

コンピュータ

そちらの記事の使い方が定番だと思いますが、今回はコードビハインドから使う方法を確認してみます。


CanvasClick.cs

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

namespace AttachidPropertyCodeBehind;

public static class CanvasClick
{
    // Commandという名前のDPを生やす
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached(
            "Command",  // 名前
            typeof(ICommand),   // バインド元の型 ... 今回はコマンド
            typeof(CanvasClick),    // 所有するクラス。
            new PropertyMetadata(null, OnCommandChanged));  // バインド時呼び出す

    // コードビハインド用Setter
    public static void SetCommand(DependencyObject element, ICommand value)
        => element.SetValue(CommandProperty, value);

    // コードビハインド用Getter
    public static ICommand GetCommand(DependencyObject element)
        => (ICommand)element.GetValue(CommandProperty);

    // バインド時の処理
    private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // キャンバスで無ければ返る
        if (d is not Canvas canvas)
            return;

        // OldValueがある場合はイベント解除
        if (e.OldValue != null)
            canvas.MouseLeftButtonDown -= Canvas_MouseLeftButtonDown;

        // NewValueがある場合はイベントの登録
        if (e.NewValue != null)
            canvas.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
    }

    // マウスのクリックイベント(イベントドリブン)
    private static void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        // キャンバスで無い場合返る。
        if (sender is not Canvas canvas)
            return;

        // コマンドが取得できなければ返る。
        var command = GetCommand(canvas);
        if (command == null)
            return;

        // クリック座標取得
        Point pos = e.GetPosition(canvas);

        // コマンドが実行可能なら実行する。
        if (command.CanExecute(pos))
        {
            // 引数にクリック座標を渡す。
            command.Execute(pos);
        }
    }
}

前回の記事と同じ


RelayCommand.cs

using System.Windows.Input;

namespace AttachidPropertyCodeBehind;

public class RelayCommand<T> : ICommand
{
    private readonly Action<T?> execute;

    public RelayCommand(Action<T?> execute)
    {
        this.execute = execute;
    }

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

    public void Execute(object? parameter)
        => execute((T?)parameter);

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

ICommandの簡易実装ヘルパー


MainWindow.xaml

<Window x:Class="AttachidPropertyCodeBehind.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:AttachidPropertyCodeBehind"
        mc:Ignorable="d"
        Title="MainWindow" Height="150" Width="200">
    <Grid>
        <Canvas x:Name="MyCanvas"
            Background="LightGray" />
    </Grid>
</Window>

MainWindow.xaml.cs

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

namespace AttachidPropertyCodeBehind;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        ICommand cmd = new RelayCommand<Point>(p =>
        {
            MessageBox.Show($"Click : {p.X:F0}, {p.Y:F0}");
        });

        CanvasClick.SetCommand(MyCanvas, cmd);
    }
}

実行し、灰色のキャンバスをクリック

座標がメッセージボックスで表示されます。

コードビハインドで実装しましたが、AttachedPropertyは前回と同じCanvasClick.csを使うことが出来ました。

AttachedPropertyは内部的にはDependencyPropertyとして実装されているため、
通常のプロパティと同じようにSetValue(SetCommandの内部)で設定することが出来ます。


AttachedPropertyは、データバインドだけでなくコードビハインドからも扱うことが出来ます。

そのため、

・XAML(宣言)
・コードビハインド(命令)

どちらのスタイルでも利用可能な柔軟な仕組みです。

ライブラリとして定義しておくことで、用途に応じて使い分けが出来る点も大きなメリットです。

コメント