7

私のWPFアプリケーションにはUserControl、ポップアップウィンドウのように見えて動作するはずのがありますが、ウィンドウではありません。コントロールがクラスから派生しない理由は、サードパーティの仮想オンスクリーンキーボードが含まれているためです。そのコントロールは、ボタンをクリックしたときに入力文字を送信するコントロールとWindow同じウィンドウにある必要があります。 TextBox。キーボードコントロールが同じウィンドウにない場合、TextBoxコントロールも表示されません。

私が抱えている問題は、ダイアログをドラッグするときのパフォーマンスがひどいことです。マウスがドラッグ領域から外れ、マウスの追従が停止するほど十分に遅いです。もっと良い方法が必要です。

コントロールのxamlからの抜粋を次に示します。

<Grid Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Border Background="{DynamicResource PopupBackground}"
            BorderBrush="{DynamicResource PopupBorder}"
            BorderThickness="5,5,5,0"
            MouseLeftButtonDown="Grid_MouseLeftButtonDown"
            MouseLeftButtonUp="Grid_MouseLeftButtonUp"
            MouseMove="Grid_MouseMove">
    . . .
    </Border>
</Grid>

マウスイベントハンドラは次のとおりです。

    private void Grid_MouseLeftButtonDown( object sender, MouseButtonEventArgs e ) {
        Canvas canvas = Parent as Canvas;
        if ( canvas == null ) {
            throw new InvalidCastException( "The parent of a KeyboardPopup control must be a Canvas." );
        }
        DraggingControl = true;
        CurrentMousePosition = e.GetPosition( canvas );
        e.Handled = true;
    }

    private void Grid_MouseLeftButtonUp( object sender, MouseButtonEventArgs e ) {
        Canvas canvas = Parent as Canvas;
        if ( canvas == null ) {
            throw new InvalidCastException( "The parent of a KeyboardPopup control must be a Canvas." );
        }

        if ( DraggingControl ) {
            Point mousePosition = e.GetPosition( canvas );

            // Correct the mouse coordinates in case they go off the edges of the control
            if ( mousePosition.X < 0.0 ) mousePosition.X = 0.0; else if ( mousePosition.X > canvas.ActualWidth ) mousePosition.X = canvas.ActualWidth;
            if ( mousePosition.Y < 0.0 ) mousePosition.Y = 0.0; else if ( mousePosition.Y > canvas.ActualHeight ) mousePosition.Y = canvas.ActualHeight;

            // Compute the new Left & Top coordinates of the control
            Canvas.SetLeft( this, Left += mousePosition.X - CurrentMousePosition.X );
            Canvas.SetTop( this, Top += mousePosition.Y - CurrentMousePosition.Y );
        }
        e.Handled = true;
    }

    private void Grid_MouseMove( object sender, MouseEventArgs e ) {
        Canvas canvas = Parent as Canvas;
        if ( canvas == null ) {
            // It is not.  Throw an exception
            throw new InvalidCastException( "The parent of a KeyboardPopup control must be a Canvas." );
        }

        if ( DraggingControl && e.LeftButton == MouseButtonState.Pressed ) {
            Point mousePosition = e.GetPosition( canvas );

            // Correct the mouse coordinates in case they go off the edges of the control
            if ( mousePosition.X < 0.0 ) mousePosition.X = 0.0; else if ( mousePosition.X > canvas.ActualWidth  ) mousePosition.X = canvas.ActualWidth;
            if ( mousePosition.Y < 0.0 ) mousePosition.Y = 0.0; else if ( mousePosition.Y > canvas.ActualHeight ) mousePosition.Y = canvas.ActualHeight;

            // Compute the new Left & Top coordinates of the control
            Canvas.SetLeft( this, Left += mousePosition.X - CurrentMousePosition.X );
            Canvas.SetTop ( this, Top  += mousePosition.Y - CurrentMousePosition.Y );

            CurrentMousePosition = mousePosition;
        }
        e.Handled = true;
    }

Canvasコントロールは、それを使用するウィンドウのa内に配置する必要があることに注意してください。

DragMoveこのクラスはクラスのメソッドでありWindow、このクラスはから派生しているため、使用できませんUserControl。このコントロールのドラッグのパフォーマンスを向上させるにはどうすればよいですか?Win32 APIに頼る必要がありますか?

4

5 に答える 5

6

MouseDragElementBehaviorを使用するだけです。

UPD動作 に関する重要なことMouseDragElementBehavior

MouseDragElementBehaviorの動作は、MouseClickイベントを処理するコントロール(Button、TextBox、ListBoxコントロールなど)では機能しません。これらのタイプのいずれかのコントロールをドラッグする機能が必要な場合は、そのコントロールをドラッグ可能なコントロールの子(境界線など)にします。次に、MouseDragElementBehaviorビヘイビアーを親要素に適用できます。

次のような独自のドラッグ動作を実装することもできます。

public class DragBehavior : Behavior<UIElement>
{
    private Point elementStartPosition;
    private Point mouseStartPosition;
    private TranslateTransform transform = new TranslateTransform();

    protected override void OnAttached()
    {
        Window parent = Application.Current.MainWindow;
        AssociatedObject.RenderTransform = transform;

        AssociatedObject.MouseLeftButtonDown += (sender, e) => 
        {
            elementStartPosition = AssociatedObject.TranslatePoint( new Point(), parent );
            mouseStartPosition = e.GetPosition(parent);
            AssociatedObject.CaptureMouse();
        };

        AssociatedObject.MouseLeftButtonUp += (sender, e) =>
        {
            AssociatedObject.ReleaseMouseCapture();
        };

        AssociatedObject.MouseMove += (sender, e) =>
        {
            Vector diff = e.GetPosition( parent ) - mouseStartPosition;
            if (AssociatedObject.IsMouseCaptured)
            {
                transform.X = diff.X;
                transform.Y = diff.Y;
            }
        };
    }
}
于 2012-12-13T18:42:35.553 に答える
5

@DmitryMartovoiの回答の情報に基づいて、私はこれを機能させる方法を考え出しました。彼の貢献なしにはこれを理解することができなかったので、私はまだドミトリーに+1を与えています。

私がしたことTranslateTransformは、コンストラクターでを作成し、それをそのプロパティUserControl'sに割り当てたことです。RenderTransform

RenderTransform = new TranslateTransform();

XAMLではBorder、ユーザーがコントロール全体をドラッグするためにクリックするコントロールに名前を付けました。

<Border Background="{DynamicResource PopupBackground}"
        BorderBrush="{DynamicResource PopupBorder}"
        BorderThickness="5,5,5,0"
        MouseLeftButtonDown="Grid_MouseLeftButtonDown"
        MouseLeftButtonUp="Grid_MouseLeftButtonUp"
        MouseMove="Grid_MouseMove"
        Name="TitleBorder">

    . . .
</Border>

最後に、さまざまなMouseイベントハンドラーを次のように変更しました。

private void Grid_MouseLeftButtonDown( object sender, MouseButtonEventArgs e ) {
    CurrentMousePosition = e.GetPosition( Parent as Window );
    TitleBorder.CaptureMouse();
}

private void Grid_MouseLeftButtonUp( object sender, MouseButtonEventArgs e ) {
    if ( TitleBorder.IsMouseCaptured ) {
        TitleBorder.ReleaseMouseCapture();
    }
}

private void Grid_MouseMove( object sender, MouseEventArgs e ) {
    Vector diff = e.GetPosition( Parent as Window ) - CurrentMousePosition;
    if ( TitleBorder.IsMouseCaptured ) {
        ( RenderTransform as TranslateTransform ).X = diff.X;
        ( RenderTransform as TranslateTransform ).Y = diff.Y;
    }
}

これは美しく機能します。をドラッグすると、その内容全体UserControlとすべてがスムーズに移動Borderし、マウスに追いつきます。そして、UserControlその表面の他の場所をクリックしても、全体は移動しません。

彼が提供したコードを提供してくれた@DmitryMartovoiに再度感謝します。

編集:上記のコードは機能しましたが、完全ではなかったため、この回答を編集しています。その欠点は、タイトルバー領域をクリックしたとき、およびドラッグを開始する前に、コントロールが画面上の元の場所に戻ることです。これは迷惑で完全に間違っていました。

私が思いついたアプローチは、実際には問題なく機能し、最初にコントロールをに入れましたCanvas。コントロールの親がaであることが重要ですCanvas。そうでない場合、次のコードは機能しません。また、の使用をやめましたRenderTransformcanvasタイプというプライベートプロパティを追加しましたCanvasLoaded重要な初期化を行うために、ポップアップコントロールにイベントハンドラーを追加しました。

private void KeyboardPopup_Loaded( object sender, RoutedEventArgs e ) {
    canvas = Parent as Canvas;
    if ( canvas == null ) {
        throw new InvalidCastException( "The parent of a KeyboardPopup control must be a Canvas." );
    }    
}

これがすべて完了したら、変更されたマウスイベントハンドラーは次のとおりです。

private void TitleBorder_MouseLeftButtonDown( object sender, MouseButtonEventArgs e ) {
    StartMousePosition = e.GetPosition( canvas );
    TitleBorder.CaptureMouse();
}

private void TitleBorder_MouseLeftButtonUp( object sender, MouseButtonEventArgs e ) {
    if ( TitleBorder.IsMouseCaptured ) {
        Point mousePosition = e.GetPosition( canvas );
        Canvas.SetLeft( this, Canvas.GetLeft( this ) + mousePosition.X - StartMousePosition.X );
        Canvas.SetTop ( this, Canvas.GetTop ( this ) + mousePosition.Y - StartMousePosition.Y );
        canvas.ReleaseMouseCapture();
    }
}

private void TitleBorder_MouseMove( object sender, MouseEventArgs e ) {
    if ( TitleBorder.IsMouseCaptured && e.LeftButton == MouseButtonState.Pressed ) {
        Point mousePosition = e.GetPosition( canvas );

        // Compute the new Left & Top coordinates of the control
        Canvas.SetLeft( this, Canvas.GetLeft( this ) + mousePosition.X - StartMousePosition.X );
        Canvas.SetTop ( this, Canvas.GetTop ( this ) + mousePosition.Y - StartMousePosition.Y );
        StartMousePosition = mousePosition;
    }
}

コントロールは、タイトルバーをクリックしてもう一度移動したときにドロップした場所にとどまり、タイトルバーをクリックしたときにのみ移動します。コントロール内の他の場所をクリックしても何も起こらず、ドラッグはスムーズで応答性が高くなります。

于 2012-12-14T16:09:26.993 に答える
2

http://www.codeproject.com/Tips/442276/Drag-and-Drop-WPF-Controls これは、多くの時間を費やした後に得た素晴らしいソリューションです。ここに示されている例は通常のコントロールですが、いくつかの変更を加えると、ユーザーコントロールでも機能するようになります。

于 2016-08-08T13:07:03.767 に答える
0

これに対する私の解決策は、@ DmitryMartovoiとこのスレッドを組み合わせることです:https ://www.codeproject.com/Questions/1014138/Csharp-WPF-RenderTransform-resets-on-mousedown

私の唯一の変更は、@DmitryMartovoiからの変更でした。答えはマウスの左ボタンの下にあります。これにより、最初にクリックしたときにテレポートが停止します。これを行うには、Systems.Windows.Interactivity.WPFNugetパッケージも必要になります。

AssociatedObject.MouseLeftButtonDown += (sender, e) =>
{
    var mousePos = e.GetPosition(parent);
    mouseStartPosition = new Point(mousePos.X-transform.X, mousePos.Y-transform.Y);
    AssociatedObject.CaptureMouse();
};
于 2020-04-14T18:20:58.020 に答える
-1

MouseDownイベントを実装するだけで、実装にthis.DragMove();

例:

<StackPanel MouseDown="UIElement_OnMouseDown">

</StackPanel>

void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
    this.DragMove();
}
于 2021-09-26T09:15:59.510 に答える