3

これは私を少し困惑させました。

いくつかのマウス イベントで 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 から複数のイベント ストリームを処理することです。

4

1 に答える 1

2

ウィンドウを最大化したときにマウスが最初に移動する理由がわからないという点で、バグである可能性があるようです。

最新のものを結合する代わりに、selectMany + takeuntil を使用します。すなわち:

var mouseDown = element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element));
var mouseUp = element.ObserveMouseLeftButtonUp();
var mouseMove = element.ObserveMouseMove();
var from md in mouseDown  
    from mm in mouseMove.TakeUntil(mouseUp)
    where MinimumDragSeen(md, mm.EventArgs.GetPosition(element)))   
    select mm;

これにより、マウスがダウンしているときにのみ移動が開始され、マウスがアップするとすぐに移動が終了します。コードに MouseUp の機能があるとは思いません。

また、イベントArgsを介して利用できると思うので、要素を保存して状態として使用することを避けることも検討します。また、MouseMove で Zip を使用することも検討して、最後のマウス移動の変化のデルタだけを取得できるようにします (適切な場合)。

私はあなたが何をしているのか本当に聞きたいです。私は Rx に関する本を書いている最中で、作業中のビルドで、ドラッグ ドロップを使用する WPF コントロールの作成を完了しました。

それが役立つことを願っています

リー

于 2012-02-13T17:33:38.000 に答える