c# wpf user-interface drag-and-drop

c# - Hacer que la ventana de WPF sea arrastrable, sin importar en qué elemento se haga clic



user-interface drag-and-drop (8)

¡Esto es todo necesario!

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

Mi pregunta es 2 veces, y espero que haya soluciones más sencillas para ambas proporcionadas por WPF en lugar de las soluciones estándar de WinForms (que Christophe Geers proporcionó, antes de hacer esta aclaración).

En primer lugar, ¿hay alguna manera de hacer que la ventana se pueda arrastrar sin capturar ni procesar los eventos de clic del ratón + arrastrar? Quiero decir que la ventana se puede arrastrar por la barra de título, pero si configuro una ventana para no tener una y aún quiero poder arrastrarla, ¿hay alguna manera de redirigir los eventos de alguna manera a lo que sea que maneje la barra de título arrastrando ?

En segundo lugar, ¿hay alguna manera de aplicar un controlador de eventos a todos los elementos en la ventana? Como en, haga que la ventana se pueda arrastrar sin importar en qué elemento haga clic + arrastrar el usuario. Obviamente, sin agregar el controlador manualmente, a cada elemento individual. Solo hazlo una vez en alguna parte?


A veces, no tenemos acceso a Window , por ejemplo, si estamos usando DevExpress , todo lo que está disponible es un UIElement .

Paso 1: agregar propiedad adjunta

La solución es:

  1. Enganche a eventos MouseMove ;
  2. Busque el árbol visual hasta que encuentre la primera Window principal;
  3. Llame a .DragMove() en nuestra Window recién descubierta.

Código:

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

Paso 2: agrega propiedad adjunta a cualquier elemento para permitir que arrastre la ventana

El usuario puede arrastrar toda la ventana haciendo clic en un elemento específico, si agregamos esta propiedad adjunta:

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

Apéndice A: Ejemplo avanzado opcional

En este ejemplo de DevExpress , reemplazamos la barra de título de una ventana de acoplamiento con nuestro propio rectángulo gris, y luego nos aseguramos de que si el usuario hace clic y arrastra dicha rectagle gris, la ventana se arrastrará normalmente:

<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>

Descargo de responsabilidad: no estoy afiliado a DevExpress . Esta técnica funcionará con cualquier elemento de usuario, incluidos WPF estándar o Telerik (otro proveedor de bibliotecas WPF).


El método más útil, tanto para WPF como para Windows, ejemplo de 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); }


Es posible arrastrar y soltar un formulario haciendo clic en cualquier lugar del formulario, no solo en la barra de título. Esto es útil si tienes una forma sin bordes.

Este artículo en CodeProject demuestra una posible solución para implementar esto:

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

Básicamente, se crea un descendiente del tipo de Formulario en el que se manejan los eventos del mouse hacia abajo, arriba y mover.

  • Ratón abajo: recordar posición
  • Mover el mouse: almacenar una nueva ubicación
  • Ratón arriba: posición de la nueva ubicación

Y aquí hay una solución similar explicada en un video tutorial:

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

No permitiría arrastrar el formulario cuando un usuario hace clic en un control en dicha forma. Los usuarios tienen diferentes resultados cuando hacen clic en controles diferentes. Cuando mi forma de repente comienza a moverse porque hice clic en un cuadro de lista, botón, etiqueta ... etc. eso sería confuso


si el formulario wpf necesita ser arrastrable, sin importar dónde se haya hecho clic, lo fácil es utilizar un delegado para activar el método DragMove () en el evento de carga de la red o en el evento de carga de la red

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


Claro, aplique el siguiente evento MouseDown de su Window

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

Esto permitirá a los usuarios arrastrar la ventana cuando hacen clic / arrastrar en cualquier control, EXCEPTO para los controles que comen el evento MouseDown ( e.Handled = true )

Puede utilizar PreviewMouseDown lugar de MouseDown , pero el evento de arrastre se come el evento Click , por lo que su ventana deja de responder a los eventos de clic izquierdo del mouse. Si REALMENTE deseaba poder hacer clic y arrastrar el formulario desde cualquier control, probablemente podría usar PreviewMouseDown , iniciar un temporizador para comenzar la operación de arrastre y cancelar la operación si el evento MouseUp se dispara dentro de X milisegundos.


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

source


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

Está lanzando una excepción en algunos casos (es decir, si en la ventana también tiene una imagen en la que se puede hacer clic, cuando se hace clic se abre un cuadro de mensaje. Cuando sale del cuadro de mensaje, obtendrá un error) Es más seguro usar

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

Entonces, estás seguro de que ese botón izquierdo está presionado en ese momento.