Сделайте окно WPF перетаскиваемым, независимо от того, какой элемент нажат
мой вопрос в 2 раза, и я надеюсь, что есть более простые решения для обоих предоставляемых WPF вместо стандартных решений от WinForms (которые предоставил Кристоф Гирс, прежде чем я сделал это уточнение).
во-первых, есть ли способ сделать окно перетаскиваемым без захвата и обработки событий мыши+перетаскивания? Я имею в виду, что окно перетаскивается по строке заголовка, но если я установлю окно не иметь его и все еще хочу иметь возможность перетащить его, есть ли способ просто перенаправить события каким-то образом на то, что обрабатывает перетаскивание строки заголовка?
во-вторых, есть ли способ применить обработчик событий ко всем элементам в окне? Например, сделайте окно перетаскиваемым независимо от того, какой элемент пользователь нажимает+перетаскивает. Очевидно, без добавления обработчика вручную, к каждому элементу. Просто сделать это один раз где-нибудь?
8 ответов:
конечно, примените следующее
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 миллисекунд.
если форма wpf должна быть перетаскиваемой независимо от того, где она была нажата, легкая работа вокруг использует делегат для запуска метода DragMove() либо в событии Windows onload, либо в событии загрузки сетки
private void Grid_Loaded(object sender, RoutedEventArgs { this.MouseDown += delegate{DragMove();}; }
иногда у нас нет доступа к
Window
, например, если мы используемDevExpress
, все, что доступно-этоUIElement
.Шаг 1: добавьте вложенное свойство
решение:
- крючок в
MouseMove
событий;- поиск по визуальному дереву, пока мы не найдем первого родителя
Window
;- вызов
.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).
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(); }
таким образом, вы уверены, что левая кнопка нажата в этот момент.
можно перетащить форму, нажав в любом месте формы, а не только в строке заголовка. Это удобно, если у вас есть форма без полей.
эта статья о CodeProject демонстрирует одно возможное решение для реализации этого:
http://www.codeproject.com/KB/cs/DraggableForm.aspx
в основном создается потомок типа формы, в котором обрабатываются события мыши вниз, вверх и перемещения.
- мышь вниз: запомнить положение
- перемещение мыши: сохранить новое место
- мышь вверх: форма позиции на новое место
и вот аналогичное решение описано в видео уроке:
http://www.youtube.com/watch?v=tJlY9aX73Vs
Я не позволил бы перетащить форму, когда пользователь нажимает на элемент управления в форме. Пользователи epexct разные результаты, когда они нажимают на разные элементы управления. Когда моя форма внезапно начинается перемещение, потому что я нажал на список, кнопку, метку...так далее. это было бы странно.
наиболее полезный метод, как для WPF, так и для windows form, пример 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); }
<Window ... WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/> <x:Code> <![CDATA[ private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DragMove(); } ]]> </x:Code>
Это все, что нужно!
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(); } }