Ma question est double et j'espère qu'il existe des solutions plus faciles à la fois fournies parWPFplutôt que par les solutions standard de WinForms (fournies par Christophe Geers avant d'avoir apporté cette clarification).
Premièrement, existe-t-il un moyen de rendre Window glissable sans capturer et traiter les événements clic-souris + glisser? Je veux dire que la fenêtre est déplaçable par la barre de titre, mais si je règle une fenêtre de façon à ne pas en avoir et que je veuille toujours pouvoir la faire glisser, existe-t-il un moyen de rediriger simplement les événements vers ceux que la barre de titre déplace en les faisant glisser? ?
Deuxièmement, existe-t-il un moyen d'appliquer un gestionnaire d'événements à tous les éléments de la fenêtre? En tant que, faites glisser la fenêtre quel que soit l'élément sur lequel l'utilisateur clique sur + traîne. Évidemment, sans ajouter manuellement le gestionnaire, à chaque élément. Juste le faire une fois quelque part?
Bien sûr, appliquez l’événement MouseDown
suivant de votre Window
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
Cela permettra aux utilisateurs de faire glisser la fenêtre quand ils cliquent/glissent sur n’importe quel contrôle, SAUF pour les contrôles qui mangent l’événement MouseDown (e.Handled = true
).
Vous pouvez utiliser PreviewMouseDown
au lieu de MouseDown
, mais l'événement glissant mange l'événement Click
, de sorte que votre fenêtre cesse de répondre aux événements de clic gauche. Si vous voulez VRAIMENT pouvoir cliquer et faire glisser le formulaire à partir de n’importe quel contrôle, vous pouvez probablement utiliser PreviewMouseDown
, démarrer un minuteur pour commencer l’opération glisser et annuler l’opération si l’événement MouseUp
est déclenché dans un délai de X millisecondes.
si le formulaire wpf doit pouvoir être déplacé, quel que soit l'endroit où il a été cliqué, la solution de facilité consiste à utiliser un délégué pour déclencher la méthode DragMove () sur l'événement windows onload ou sur le chargement de grille
private void Grid_Loaded(object sender, RoutedEventArgs
{
this.MouseDown += delegate{DragMove();};
}
Parfois, nous n'avons pas accès à Window
, par exemple. si nous utilisons DevExpress
, tout ce qui est disponible est un UIElement
.
La solution consiste à:
MouseMove
;Window
;.DragMove()
sur notre Window
récemment découvert.Code:
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);
}
}
}
L'utilisateur peut faire glisser la fenêtre entière en cliquant sur un élément spécifique, si nous ajoutons cette propriété attachée:
<Border local:EnableDragHelper.EnableDrag="True">
<TextBlock Text="Click me to drag this entire window"/>
</Border>
Dans cet exemple de DevExpress , nous remplaçons la barre de titre d'une fenêtre d'ancrage par notre propre rectangle gris, puis nous nous assurons que si l'utilisateur clique sur le rectangle gris et le fait glisser, la fenêtre se déplacera normalement:
<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>
Disclaimer: je suis pas affilié à DevExpress . Cette technique fonctionnera avec n'importe quel élément utilisateur, y compris standard WPF ou Telerik (un autre excellent fournisseur de bibliothèque WPF).
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
Lève une exception dans certains cas (c'est-à-dire si sur la fenêtre vous avez également une image cliquable ouvrant une boîte de message lorsque vous cliquez dessus. Vous obtiendrez une erreur)
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
this.DragMove();
}
Donc, vous êtes sûr que le bouton gauche est enfoncé à ce moment.
Il est possible de glisser-déposer un formulaire en cliquant n'importe où sur le formulaire, pas seulement dans la barre de titre. C'est pratique si vous avez un formulaire sans bordure.
Cet article sur CodeProject illustre une solution possible pour implémenter ceci:
http://www.codeproject.com/KB/cs/DraggableForm.aspx
Fondamentalement, un descendant du type de formulaire est créé dans lequel les événements de souris bas, haut et déplacer sont gérés.
Et voici une solution similaire expliquée dans un tutoriel vidéo:
http://www.youtube.com/watch?v=tJlY9aX73Vs
Je ne permettrais pas de faire glisser le formulaire lorsqu'un utilisateur clique sur un contrôle de ce formulaire. Les utilisateurs obtiennent des résultats différents lorsqu'ils cliquent sur des contrôles différents. Quand mon formulaire commence à bouger parce que j'ai cliqué sur une liste, un bouton, une étiquette, etc. ce serait déroutant.
Tout cela est nécessaire!
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();
}
}
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
<![CDATA[
private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
]]>
</x:Code>
La méthode la plus utile, à la fois pour les formulaires WPF et Windows, exemple 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);
}
Comme déjà mentionné par @ fjch1997 , il est pratique d'implémenter un comportement. La logique de base est la même que dans answer de @ 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();
}
}
}
Usage:
<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>