ReactiveProperty の ReactiveCommand に sender と EventArgs を渡したい


個人的には ViewModel に sender 渡すのはどうかなぁと思いますが厳密にはあり合わせの機能では出来ないですが、近いことは出来なくもないです。やってみましょう。

ReactiveProperty.WPF パッケージに含まれる EventToReactiveCommand のコンバーターには AssociateObject プロパティがあって、これに EventTrigger が割り当てられたオブジェクトのインスタンスが入っています。これが sender と一致することがほとんどだと思う(というか一致しないケースってなんだろう)ので、これと EventArgs を渡してやるようにコンバーターで処理すれば OK ですね。

コンバーターはこんな感じ。

MyConverter.cs
using Reactive.Bindings.Interactivity;
using System;

namespace WpfApp3
{
    public class MyConverter : DelegateConverter<EventArgs, (object sender, EventArgs args)>
    {
        protected override (object sender, EventArgs args) OnConvert(EventArgs source) => (AssociateObject, source);
    }
}

ということで適当に ViewModel を作って…

MainWindowViewModel.cs
using Reactive.Bindings;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfApp3
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public ReactiveCommand<(object sender, EventArgs args)> SomeCommand { get; }
        public ReadOnlyReactivePropertySlim<string> Result { get; }

        public MainWindowViewModel()
        {
            SomeCommand = new ReactiveCommand<(object sender, EventArgs args)>();
            Result = SomeCommand
                .Select(x => $"{DateTime.Now}: {x.sender}, {x.args}")
                .ToReadOnlyReactivePropertySlim();
        }
    }
}

MainWindow.xaml を以下のような感じにして

MainWindow.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:behaviors="http://schemas.microsoft.com/xaml/behaviors"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp3"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:rp="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <StackPanel>
        <Button Content="Click">
            <behaviors:Interaction.Triggers>
                <behaviors:EventTrigger EventName="Click">
                    <rp:EventToReactiveCommand Command="{Binding SomeCommand}">
                        <local:MyConverter />
                    </rp:EventToReactiveCommand>
                </behaviors:EventTrigger>
            </behaviors:Interaction.Triggers>
        </Button>
        <TextBlock Text="{Binding Result.Value}" />
    </StackPanel>
</Window>

実行してボタンを押すと Button のインスタンスとイベント引数がわたってることが確認できます。

まとめ

でも UI コントロールを ViewModel に直接渡したいというケースってなんだろう??
どうしようもない状態以外ではしない方がいいかな。きっともっといい方法があると願いたい。