어떤 요소를 클릭하더라도 WPF 창을 드래그 할 수 있도록합니다.


111

내 질문은 두 배이며 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, 타이머를 시작하여 드래그 작업을 시작하고, MouseUp이벤트가 X 밀리 초 이내에 발생 하면 작업을 취소 할 수 있습니다 .


+1. 창 관리자가 위치를 기억하고 창을 이동하여 위조하는 대신 이동을 처리하도록하는 것이 훨씬 좋습니다. (후자의 방법은 어쨌든, 특정 가장자리의 경우 잘못하는 경향이있다)
조이

MouseLeftButtonDown.cs를 확인하는 대신 이벤트를 설정하지 않는 이유는 무엇 입니까?

1
@Drowin 아마도 그 이벤트를 대신 사용할 수 있지만 버블 링 라우팅 전략이있는 MouseLeftButtonDown동안 직접 라우팅 전략이 있으므로 먼저 테스트해야합니다 MouseDown. 자세한 내용 과 over 를 사용할 경우 알아야 할 추가 사항 은 MouseLeftButtonDown에 대한 MSDN 페이지 의 설명 섹션을 참조하십시오 . MouseLeftButtonDownMouseDown
Rachel

@Rachel 그래 나는 그것을 사용하고 있으며 작동하지만 설명해 주셔서 감사합니다!

2
@Rahul UserControl을 드래그하는 것은 훨씬 더 어렵습니다 ... Canvas와 같은 부모 패널에 배치하고 사용자가 마우스를 움직일 때 X / Y (또는 Canvas.Top 및 Canvas.Left) 속성을 수동으로 설정해야합니다. 지난번에 마우스 이벤트를 사용했기 때문에 OnMouseDown 캡처 위치 및 이동 이벤트 등록, OnMouseMove 변경 X / Y 및 OnMouseUp 제거 이동 이벤트. 그것이 그것의 기본 아이디어입니다. :)
Rachel

9

wpf 양식을 클릭 한 위치에 관계없이 드래그 할 수 있어야하는 경우 쉬운 해결 방법은 대리자를 사용하여 windows onload 이벤트 또는 그리드로드 이벤트에서 DragMove () 메서드를 트리거하는 것입니다.

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

2
나는 이것을 생성자에 추가했습니다. 매력을 발휘합니다.
Joe Johnston

1
DragMove기본 마우스 버튼이 눌러져있을 때만 호출 될 수 있기 때문에 양식의 아무 곳이나 마우스 오른쪽 버튼으로 클릭하면 예외 가 발생합니다.
Stjepan Bakrac 2014

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
이것이 바로 제가 원했던 것입니다. IMHO 모든 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에 대한이 기사는이를 구현할 수있는 한 가지 가능한 솔루션을 보여줍니다.

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

기본적으로 Form 유형의 하위 항목이 생성되어 마우스 아래, 위로 및 이동 이벤트가 처리됩니다.

  • 마우스 아래로 : 위치 기억
  • 마우스 이동 : 새 위치 저장
  • 마우스 위로 : 양식을 새 위치에 배치

다음은 비디오 자습서에서 설명한 유사한 솔루션입니다.

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);
    }

0
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
    <![CDATA[            
        private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }
    ]]>
</x:Code>

출처

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.