これは私を少し困惑させました。
いくつかのマウス イベントで Observables を提供するために、UIElement にいくつかの拡張メソッドを記述しました。関連するものは次のとおりです。
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseLeftButtonDown(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseLeftButtonDown");
}
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseMove(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseMove");
}
ここまでは、気が遠くなるほど単純です。次に、ユーザーが UIElement でドラッグを開始したことを検出する複合イベントを作成します (これはすべて、私のカスタム コントロールによって使用されますが、その正確な性質は特に関係ありません)。最初に、ユーザーが最小ドラッグ距離までドラッグしたかどうかを確認するための小さなヘルパー関数を次に示します。
private static bool MinimumDragSeen(Point start, Point end)
{
return Math.Abs(end.X - start.X) >= SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(end.Y - start.Y) >= SystemParameters.MinimumVerticalDragDistance;
}
そして、複合オブザーバブル自体:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
return Observable.CombineLatest(
element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element)),
element.ObserveMouseMove().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed),
(md, mm) => new { Down = md, Move = mm })
.Where(i => MinimumDragSeen(i.Down, i.Move.EventArgs.GetPosition(element)))
.Select(i => i.Move);
}
WPF コントロールを使用した Rx ベースのドラッグ アンド ドロップの適切な例がないという多くの歯ぎしりと煩わしさの後で、これはすべてうまく機能します (それらのほとんどは、キャンバス内で画像をドラッグする単純な複製です)。
問題は、ウィンドウが最大化されたとき、特にタイトル バーをダブルクリックしたときに発生します。最大化されたレイアウトで、独自の ObserveMouseDrag() にサブスクライブされているコントロールの 1 つがマウス カーソルの下にある場合、MouseLeftButtonDown と MouseMove を受け取ります。最終的な結果として、ドラッグ イベントが開始され、マウス ボタンが離されるとすぐに停止し、コントロールがそれ自体の上にドロップされる傾向があり、このアプリの状況によっては、実際に何かが行われます。
ダブルクリックの最大化によって発生する MouseDown と MouseMove を受け取る理由がわからないため、これは非常に奇妙です。私はカスタム ウィンドウの境界線などを使用していないため、関連するすべてのマウス イベントは Windows で処理する必要があります。
それで、誰かアイデアはありますか?
次の日...
修正しました!(以下のLeeの回答と、この質問の助けを借りて:Rxを使用してマウスドラッグの終了を決定する適切な方法は何ですか?)
コードは次のようになります。
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
var mouseDown = element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element));
var mouseMove = element.ObserveMouseMove();
var stop = Observable.Merge(
element.ObserveMouseUp(),
element.ObserveMouseLeave().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
);
return mouseDown.SelectMany(
md => mouseMove
.Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
.Where(ep => MinimumDragSeen(md, ep.EventArgs.GetPosition(element)))
.TakeUntil(stop)
);
}
また、最小ドラッグ距離、マウスアップ イベント、およびドラッグが完全に適切に機能するために必要なあらゆる種類の処理を行います。最大化のバグも解消されました (まだ完全には理解できていませんが、マウスアップの処理が関係していると思われます)。
ここで重要なことは、SelectMany を使用して、コントロール内で mouseUp、またはマウス ボタンを押したままマウス ポインターが離れるまで、mouseMove から複数のイベント ストリームを処理することです。