132 votes

Rendre la fenêtre WPF déplaçable, quel que soit l'élément sur lequel on clique.

Ma question est double, et j'espère qu'il y a des solutions plus faciles pour les deux, fournies par WPF plutôt que les solutions standard de WinForms (que Christophe Geers a fourni, avant que je ne fasse cette clarification).

Tout d'abord, existe-t-il un moyen de rendre la fenêtre glissante sans capturer et traiter les événements de clics de souris + glissades ? Je veux dire que la fenêtre peut être glissée par la barre de titre, mais si je configure une fenêtre qui n'en a pas et que je veux quand même pouvoir la faire glisser, y a-t-il un moyen de rediriger les événements vers ce qui gère le glissement de la barre de titre ?

Deuxièmement, existe-t-il un moyen d'appliquer un gestionnaire d'événement à tous les éléments de la fenêtre ? Par exemple, faire en sorte que la fenêtre puisse être glissée, quel que soit l'élément sur lequel l'utilisateur clique et glisse. Évidemment sans ajouter le gestionnaire manuellement, à chaque élément. Il suffit de le faire une fois quelque part ?

337voto

Rachel Points 49408

Bien sûr, appliquez ce qui suit MouseDown l'événement 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 lorsqu'ils cliquent/tirent 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 drag mange le Click de sorte que votre fenêtre cesse de répondre aux événements liés au clic gauche de la souris. Si vous vouliez VRAIMENT pouvoir cliquer et faire glisser le formulaire à partir de n'importe quel contrôle, vous pourriez probablement utiliser PreviewMouseDown le lancement d'une minuterie pour commencer l'opération de drague, et l'annulation de l'opération si l'opération de drague est terminée. MouseUp se déclenche dans un délai de X millisecondes.

0 votes

+1. Il vaut mieux laisser le gestionnaire de fenêtres gérer le déplacement plutôt que de le simuler en se souvenant de la position et en déplaçant la fenêtre. (Cette dernière méthode a également tendance à se tromper dans certains cas limites, de toute façon).

0 votes

Pourquoi ne pas simplement définir le MouseLeftButtonDown plutôt que de vérifier dans le fichier .cs ?

2 votes

@Drowin Vous pourriez probablement utiliser cet événement à la place, mais assurez-vous de le tester d'abord car MouseLeftButtonDown a une stratégie de routage direct tandis que MouseDown a une stratégie de routage par bulles. Voir la section des remarques de la Page MSDN pour MouseLeftButtonDown (en anglais) pour plus d'informations, et pour certaines choses supplémentaires à savoir si vous allez utiliser MouseLeftButtonDown sur MouseDown .

10voto

Pranavan Maru Points 67

Si le formulaire wpf doit pouvoir être glissé, quel que soit l'endroit où il a été cliqué, la solution la plus simple consiste à utiliser un délégué pour déclencher la méthode DragMove() lors de l'événement Windows onload ou de l'événement grid load.

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

2 votes

J'ai ajouté ceci au constructeur. Ça marche à merveille.

3 votes

Cela déclenchera une exception si vous cliquez avec le bouton droit de la souris n'importe où sur le formulaire, car DragMove ne peut être appelé que lorsque le bouton primaire de la souris est enfoncé.

0 votes

Mieux vaut vérifier ChangedButton this.MouseDown += delegate (object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) DragMove(); };

7voto

Contango Points 7976

Parfois, nous n'avons pas accès à Window par exemple, si nous utilisons DevExpress tout ce qui est disponible est un UIElement .

Étape 1 : Ajouter une propriété jointe

La solution consiste à :

  1. Accrocher dans MouseMove événements ;
  2. Chercher dans l'arbre visuel jusqu'à ce que nous trouvions le premier parent Window ;
  3. Appelez .DragMove() sur notre nouvelle découverte Window .

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

Étape 2 : Ajoutez une propriété attachée à n'importe quel élément pour lui permettre de faire glisser la fenêtre.

L'utilisateur peut faire glisser la fenêtre entière en cliquant sur un élément spécifique, si nous ajoutons cette propriété jointe :

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

Annexe A : Exemple avancé facultatif

Dans cet exemple tiré 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 et fait glisser ledit rectangle gris, la fenêtre se déplace 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 no affilié à DevExpress . Cette technique fonctionnera avec n'importe quel élément utilisateur, y compris WPF standard o Telerik (un autre excellent fournisseur de bibliothèques WPF).

1 votes

C'est exactement ce que je voulais. IMHO tout le code WPF devrait être écrit comme un comportement attaché.

1 votes

@Contango, votre solution est la meilleure de toutes.

6voto

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

Lance une exception dans certains cas (par exemple, si la fenêtre comporte également une image cliquable qui, lorsqu'elle est cliquée, ouvre une boîte de message). Lorsque vous quittez la boîte de messages, vous obtenez une erreur). Il est plus sûr d'utiliser

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

Vous êtes donc sûr que le bouton gauche est enfoncé à ce moment-là.

0 votes

J'utilise e.LeftButton au lieu de Mouse.LeftButton pour utiliser spécifiquement le bouton associé aux args de l'événement, même si cela n'aura probablement jamais d'importance .

4voto

stop-cran Points 2461

Comme déjà mentionné par @fjch1997 il est pratique d'implémenter un comportement. Le voici, la logique de base est la même que celle de l'@loi.efy. réponse :

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

Utilisation :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X