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