今更ながらWPFに置き換えてみる(15)


DragMoveの件

DragMove便利なんですがやろうと思ってるScreenの縁へのスナップがうまいこといかず。
やりたいのは一定以上縁に近づいたら縁に吸着させるというもの。
VBの場合は
①ドラッグの開始時にウインドウのTOP・LEFTを取っておく
②ドラッグ中は左ボタンが押されてる間マージンのサイズを加算してカーソルポジションを算出し、ウインドウを算出座標にロケートする、というのような実装でOKでした。VB↓

    Private Sub DragStart()
        Dragging = True
        offsetPos.X = MousePosition.X - Left
        offsetPos.Y = MousePosition.Y - Top
    End Sub

    Private Sub Drag(e As MouseEventArgs)
        If e.Button = MouseButtons.Left Then
            If Dragging = True Then
                Me.Location = GetPos(offsetPos)
            End If
        End If
    End Sub

    Private Sub DragEnd()
        If Dragging = True Then
            Me.Location = GetPos(offsetPos)
        End If
        Dragging = False
    End Sub

    Private Function GetPos(aPos) As Point
        GetPos.Y = MousePosition.Y - aPos.Y
        GetPos.X = MousePosition.X - aPos.X

        'スクリーンフレームへの吸着
        If GetPos.Y < marginsize Then GetPos.Y = 0
        If GetPos.X < marginsize Then GetPos.X = 0
        If (GetPos.Y + Height + marginsize) > h Then GetPos.Y = h - Height
        If (GetPos.X + Width + marginsize) > w Then GetPos.X = w - Width
    End Function

最初DragMoveを使わずにそのままC#に置き換えてみましたが、早いドラッグ操作に画面がついてこれません。で画面からカーソルアウトしてしまうと、そこで制御が失われます。
どうにも使えそうにないので、やはり反応がいいDragMoveを使い、以下のような手順に変更。
ドラッグ中のChangeLocationイベント内で、ウインドウの上下左右の座標がScreenのWorkingエリアにスナップマージンを加算・減算した範囲内になった場合、スナップ後の指定値にウインドウ位置を変更。
C#ではこんな感じで↓(G.SRには画面解像度が入ってます。1.25みたいな)


        private void Window_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (e.ButtonState != MouseButtonState.Pressed) return;
            DragMove(); //ここからDragMove
        }

        private void Window_MouseUp(object sender, MouseButtonEventArgs e)
        {
            //Draggingの終了
            if (G.isDragging == true) G.isDragging = false;
        }

        private void Window_LocationChanged(object sender, EventArgs e)
        {
            if(G.isTransing == false)   //画面TransitionによるLocationChangedは無視する
            {
                //ドラッグ中の場合はG.isDragging = trueにしておくことで最初のクリックイベントを回避
                if (Mouse.LeftButton == MouseButtonState.Pressed) G.isDragging = true;
                    else G.isDragging = false;

                //スクリーン縁への吸着
                var NewPos = GetPos();
                var hwnd = new WindowInteropHelper(this).Handle;
                NativeMethods.SetWindowPos(hwnd, IntPtr.Zero, (int)Math.Round(NewPos.X), (int)Math.Round(NewPos.Y),
                            (int)Math.Round(Width * G.SR), (int)Math.Round(Height * G.SR), Consts.SWP_NOZORDER);
            }
        }

        private Point GetPos()
        {
            var NewWindowPos = new Point();

            NewWindowPos.X = this.Left * G.SR;  
            NewWindowPos.Y = this.Top * G.SR;
            if (NewWindowPos.X < G.marginsize) NewWindowPos.X = 0;  //左辺への吸着
            if (NewWindowPos.Y < G.marginsize) NewWindowPos.Y = 0;  //上辺への吸着
            if (NewWindowPos.X + (Width * G.SR) + G.marginsize > G.SW) NewWindowPos.X = G.SW - (Width * G.SR);  //右辺への吸着
            if (NewWindowPos.Y + (Height * G.SR) + G.marginsize > G.SH) NewWindowPos.Y = G.SH - (Height * G.SR);  //底辺への吸着
            return NewWindowPos;
        }

結果、縁へのスナップは成功したのですが、枠外にドラッグし続けた場合ちらつきが発生します。見た感じ、DragMoveによっていったん枠外に描画して、そのあとLocationChanged内のSetWindowPosで枠内に描画している風。
解決策思いつかないので、このままでいくことに。