WPFのコードビハインドでObservable.FromEventPatternを使いマウスイベントを処理するサンプルコード

コンピュータ

コードビハインドで処理するイベントをストリームとして扱うサンプルプログラムです。
マウス座標を取得しタイトルバーに表示するアプリケーションになります。

ソースコード

ファイル名:RxMouseMove.csproj


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

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

  <ItemGroup>
    <PackageReference Include="System.Reactive" Version="6.0.0" />
    <PackageReference Include="System.Reactive.PlatformServices" Version="6.0.0" />
  </ItemGroup>

</Project>

ファイル名:MainWindow.xaml.cs

using System.Windows;
using System.Windows.Input;
using System.Reactive.Linq;
using System.Reactive.Concurrency;
using System.Threading;

namespace RxMouseMove;

public partial class MainWindow : Window
{
    private IDisposable? _subscription;
    public MainWindow()
    {
        InitializeComponent();

        // Windowがロードされたら購読開始
        Loaded += (_, __) =>
        {
            var context = SynchronizationContext.Current!;            
            var uiScheduler = new SynchronizationContextScheduler(context);
            SetupMouseRx(uiScheduler);
        };

        // 閉じるときにはDispose
        Closed += (_, __) => _subscription?.Dispose();
    }

    private void SetupMouseRx(IScheduler uiScheduler)
    {
        // マウス移動イベントをObservable化
        var mouseMove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
            h => this.MouseMove += h,
            h => this.MouseMove -= h
        );

        // 座標のストリームを作成
        _subscription = mouseMove
            .Select(ev => ev.EventArgs.GetPosition(this))  // Pointを取り出す
            .Throttle(TimeSpan.FromMilliseconds(5))        // 負荷軽減(任意)
            .ObserveOn(uiScheduler)        // UIスレッドへ戻す
            .Subscribe(p =>
            {
                Title = $"X={p.X:F0}  Y={p.Y:F0}";
            });
    }

}

実行イメージ

ウィンドウ内でマウスカーソルを移動させると、タイトルバーに座標が表示されます。

感想

今一つ理解が足りていないのですが、mouseMoveをSubscribeしていることは確認できます。

まず、イベントをObservaleへ変換

// マウス移動イベントをObservable化
var mouseMove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
    h => this.MouseMove += h,
    h => this.MouseMove -= h
);

次に、Observale化したストリーム(mouseMove)を購読(Subscribe)する条件をメソッドチェーンで定義しています。

// 座標のストリームを作成
_subscription = mouseMove
    .Select(ev => ev.EventArgs.GetPosition(this))  // Pointを取り出す
    .Throttle(TimeSpan.FromMilliseconds(5))        // 負荷軽減(任意)
    .ObserveOn(uiScheduler)        // UIスレッドへ戻す
    .Subscribe(p =>
    {
            Title = $"X={p.X:F0}  Y={p.Y:F0}";
     });

マウス座標を取得するだけなら、イベント処理を書いた方がシンプルですが、負荷軽減で間引くコードなどはイベント処理では意外と面倒です。理解が進んだらもう少し複雑な処理を書いてみたいと思います。

コメント