どの要素をクリックしても、WPFウィンドウをドラッグ可能にする


111

私の質問は2つあります。WinFormsの標準ソリューション(この説明を行う前にChristophe Geersが提供したもの)ではなく、WPFによって提供される両方のソリューションが簡単になることを願っています。

最初に、マウスクリック+ドラッグイベントをキャプチャして処理せずにウィンドウをドラッグ可能にする方法はありますか?ウィンドウがタイトルバーによってドラッグ可能であることを意味しますが、ウィンドウを設定せずにドラッグできるようにしたい場合、タイトルバーのドラッグを処理するものにイベントを何らかの方法でリダイレクトする方法があります。 ?

次に、ウィンドウ内のすべての要素にイベントハンドラーを適用する方法はありますか?同様に、ユーザーがクリックしてドラッグする要素に関係なく、ウィンドウをドラッグ可能にします。明らかに、ハンドラーを手動で追加せずに、すべての単一の要素に追加します。どこかで一度だけやるの?

回答:


284

確かに、MouseDownあなたの次のイベントを適用してくださいWindow

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ChangedButton == MouseButton.Left)
        this.DragMove();
}

これにより、ユーザーは任意のコントロールをクリックまたはドラッグしたときにウィンドウをドラッグできます。ただし、MouseDownイベント(e.Handled = true)を使用するコントロールは除きます。

PreviewMouseDown代わりに使用できますMouseDownが、ドラッグイベントはイベントを食べるClickため、ウィンドウは左マウスクリックイベントに応答しなくなります。フォームをクリックして任意のコントロールからフォームをドラッグできるようにしたい場合は、を使用しPreviewMouseDown、タイマーを開始してドラッグ操作を開始し、MouseUpXミリ秒以内にイベントが発生した場合は操作をキャンセルできます。


+1。ウィンドウマネージャーに、位置を覚えてウィンドウを移動することで偽装するのではなく、移動を処理させる方がはるかに優れています。(後者の方法も、とにかく特定のエッジケース
Joey

MouseLeftButtonDown.csをチェックインするのではなく、イベントを設定しないのはなぜですか?

1
@Drowinおそらく代わりにそのイベントを使用できますが、バブリングルーティング戦略があるMouseLeftButtonDown一方で直接ルーティング戦略があるので、まずそれをテストしてくださいMouseDown。詳細については、MSDNページのMouseLeftButtonDownの解説セクションを参照してください。また、MouseLeftButtonDownover を使用する場合に注意する必要のある追加事項についても確認してくださいMouseDown
Rachel

@レイチェルうん私はそれを使っており、それは機能しますが、説明をありがとう!

2
@Rahul UserControlのドラッグははるかに困難です...ユーザーコントロールをCanvasのような親パネルに配置し、ユーザーがマウスを動かすときにX / Y(またはCanvas.TopおよびCanvas.Left)プロパティを手動で設定する必要があります。最後にマウスイベントを使用したので、OnMouseDownは位置とキャプチャの移動イベントをキャプチャし、OnMouseMoveはX / Yを変更し、OnMouseUpは移動イベントを削除しました。それがその基本的な考え方です:)
Rachel

9

クリックされた場所に関係なくwpfフォームをドラッグ可能にする必要がある場合、簡単な回避策は、デリゲートを使用して、ウィンドウのonloadイベントまたはグリッドのロードイベントのいずれかでDragMove()メソッドをトリガーすることです。

private void Grid_Loaded(object sender, RoutedEventArgs 
{
      this.MouseDown += delegate{DragMove();};
}

2
これをコンストラクタに追加しました。魅力的な作品。
Joe Johnston、

1
これは、フォームの任意の場所を右クリックした場合に例外をスローします。これはDragMove、マウスの主ボタンが押されているときにのみ呼び出すことができるためです。
Stjepan Bakrac 14

4

たまに、にアクセスできないことがありますWindow。たとえば、を使用している場合DevExpress、利用できるのはだけUIElementです。

手順1:添付プロパティを追加する

解決策は次のとおりです。

  1. MouseMoveイベントにフックします。
  2. 最初の親が見つかるまでビジュアルツリーを検索しWindowます。
  3. .DragMove()新たに発見された私たちを呼び出しますWindow

コード:

using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace DXApplication1.AttachedProperty
{
    public class EnableDragHelper
    {
        public static readonly DependencyProperty EnableDragProperty = DependencyProperty.RegisterAttached(
            "EnableDrag",
            typeof (bool),
            typeof (EnableDragHelper),
            new PropertyMetadata(default(bool), OnLoaded));

        private static void OnLoaded(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var uiElement = dependencyObject as UIElement;
            if (uiElement == null || (dependencyPropertyChangedEventArgs.NewValue is bool) == false)
            {
                return;
            }
            if ((bool)dependencyPropertyChangedEventArgs.NewValue  == true)
            {
                uiElement.MouseMove += UIElementOnMouseMove;
            }
            else
            {
                uiElement.MouseMove -= UIElementOnMouseMove;
            }

        }

        private static void UIElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
        {
            var uiElement = sender as UIElement;
            if (uiElement != null)
            {
                if (mouseEventArgs.LeftButton == MouseButtonState.Pressed)
                {
                    DependencyObject parent = uiElement;
                    int avoidInfiniteLoop = 0;
                    // Search up the visual tree to find the first parent window.
                    while ((parent is Window) == false)
                    {
                        parent = VisualTreeHelper.GetParent(parent);
                        avoidInfiniteLoop++;
                        if (avoidInfiniteLoop == 1000)
                        {
                            // Something is wrong - we could not find the parent window.
                            return;
                        }
                    }
                    var window = parent as Window;
                    window.DragMove();
                }
            }
        }

        public static void SetEnableDrag(DependencyObject element, bool value)
        {
            element.SetValue(EnableDragProperty, value);
        }

        public static bool GetEnableDrag(DependencyObject element)
        {
            return (bool)element.GetValue(EnableDragProperty);
        }
    }
}

ステップ2:任意の要素に添付プロパティを追加して、ウィンドウをドラッグできるようにする

この添付プロパティを追加すると、ユーザーは特定の要素をクリックしてウィンドウ全体をドラッグできます。

<Border local:EnableDragHelper.EnableDrag="True">
    <TextBlock Text="Click me to drag this entire window"/>
</Border>

付録A:オプションの高度な例

このDevExpressの例では、ドッキングウィンドウのタイトルバーを独自の灰色の四角形に置き換え、ユーザーが灰色の四角形をクリックしてドラッグすると、ウィンドウが正常にドラッグされることを確認します。

<dx:DXWindow x:Class="DXApplication1.MainWindow" Title="MainWindow" Height="464" Width="765" 
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" 
    xmlns:local="clr-namespace:DXApplication1.AttachedProperty"
    xmlns:dxdove="http://schemas.devexpress.com/winfx/2008/xaml/docking/visualelements"
    xmlns:themeKeys="http://schemas.devexpress.com/winfx/2008/xaml/docking/themekeys">

    <dxdo:DockLayoutManager FloatingMode="Desktop">
        <dxdo:DockLayoutManager.FloatGroups>
            <dxdo:FloatGroup FloatLocation="0, 0" FloatSize="179,204" MaxHeight="300" MaxWidth="400" 
                             local:TopmostFloatingGroupHelper.IsTopmostFloatingGroup="True"                             
                             >
                <dxdo:LayoutPanel ShowBorder="True" ShowMaximizeButton="False" ShowCaption="False" ShowCaptionImage="True" 
                                  ShowControlBox="True" ShowExpandButton="True" ShowInDocumentSelector="True" Caption="TradePad General" 
                                  AllowDock="False" AllowHide="False" AllowDrag="True" AllowClose="False"
                                  >
                    <Grid Margin="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" MinHeight="15" Background="#FF515151" Margin="0 0 0 0"
                                                                  local:EnableDragHelper.EnableDrag="True">
                            <TextBlock Margin="4" Text="General" FontWeight="Bold"/>
                        </Border>
                        <TextBlock Margin="5" Grid.Row="1" Text="Hello, world!" />
                    </Grid>
                </dxdo:LayoutPanel>
            </dxdo:FloatGroup>
        </dxdo:DockLayoutManager.FloatGroups>
    </dxdo:DockLayoutManager>
</dx:DXWindow>

免責事項:私はDevExpress関係ありません。この手法は、標準のWPFまたはTelerik(別の優れたWPFライブラリプロバイダー)を含むすべてのユーザー要素で機能します。


1
これはまさに私が欲しかったものです。背後にあるすべてのWPFコードは、添付の動作として記述してください。
fjch1997

3
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
    this.DragMove();
}

場合によっては例外をスローします(つまり、ウィンドウ上にクリックするとメッセージボックスが開くクリック可能な画像もある場合。メッセージボックスを終了すると、エラーが発生します)。

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
            this.DragMove();
}

したがって、その時点で左ボタンが押されていることを確認できます。


私が使用しているe.LeftButton代わりに、Mouse.LeftButton具体的には問題ありませんおそらくにもかかわらず、イベント引数に関連付けられたボタンを使用します。
Fls'Zen

2

タイトルバーだけでなく、フォーム上の任意の場所をクリックして、フォームをドラッグアンドドロップすることができます。ボーダレスなフォルムがあると重宝します。

CodeProjectに関するこの記事は、これを実装するための1つの可能なソリューションを示しています。

http://www.codeproject.com/KB/cs/DraggableForm.aspx

基本的には、フォームタイプの子孫が作成され、マウスのダウン、アップ、移動のイベントが処理されます。

  • マウスダウン:位置を覚える
  • マウスの移動:新しい場所を保存
  • マウスアップ:フォームを新しい場所に配置

そして、これはビデオチュートリアルで説明されている同様のソリューションです:

http://www.youtube.com/watch?v=tJlY9aX73Vs

ユーザーがフォーム内のコントロールをクリックしたときにフォームをドラッグすることはできません。ユーザーがさまざまなコントロールをクリックすると、さまざまな結果が発生します。リストボックス、ボタン、ラベルなどをクリックしてフォームが突然動き始めたとき それは混乱するでしょう。


確かにコントロールをクリックしても移動しませんが、クリックしてドラッグした場合、フォームが移動することは期待できません。ボタンやリストボックスが移動することを期待していないことを意味します。たとえば、クリックしてドラッグした場合、フォーム内のボタンをクリックしてドラッグしようとした場合、フォームの動きは当然のことだと思います。
Alex K

推測、それは単なる個人的な好みです。とにかく、コントロールは同じマウスイベントを処理する必要があります。バブルが発生しないため、これらのイベントを親フォームに通知する必要があります。
Christophe Geers、2011

また、これに対するWinFormsソリューションを知っていましたが、WPFでより簡単に存在できる方法を望んでいましたが、質問でこれをより明確にする必要があると思います(現在は単なるタグです)。
Alex K

すみません。WPFタグに気付かなかった。元の質問には記載されていませんでした。デフォルトでWinFormsを想定し、タグを見ました。
Christophe Geers、2011

2

@ fjch1997ですでに述べたように、ビヘイビアを実装すると便利です。ここでは、コアロジックは@ loi.efyの回答と同じです。

public class DragMoveBehavior : Behavior<Window>
{
    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += AssociatedObject_MouseMove;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
    }

    private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && sender is Window window)
        {
            // In maximum window state case, window will return normal state and
            // continue moving follow cursor
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;

                // 3 or any where you want to set window location after
                // return from maximum state
                Application.Current.MainWindow.Top = 3;
            }

            window.DragMove();
        }
    }
}

使用法:

<Window ...
        xmlns:h="clr-namespace:A.Namespace.Of.DragMoveBehavior"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <i:Interaction.Behaviors>
        <h:DragMoveBehavior />
    </i:Interaction.Behaviors>
    ...
</Window>

1

これはすべて必要です!

private void UiElement_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            if (this.WindowState == WindowState.Maximized) // In maximum window state case, window will return normal state and continue moving follow cursor
            {
                this.WindowState = WindowState.Normal;
                Application.Current.MainWindow.Top = 3;// 3 or any where you want to set window location affter return from maximum state
            }
            this.DragMove();
        }
    }

0

WPFとWindowsフォームの両方で最も便利な方法、WPFの例:

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

    public static void StartDrag(Window window)
    {
        WindowInteropHelper helper = new WindowInteropHelper(window);
        SendMessage(helper.Handle, 161, 2, 0);
    }

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.