• Créer un conteneur WPF « draggable » (part 2)

    Où l'on commence l'implémentation du contrôle

    Pour commencer l'implémentation de ce contrôle, nous allons nous concentrer sur la détection du « gesture » : c'est assez simple, il suffit que l'utilisateur clique sur le contrôle et bouge d'au moins quelques pixels en maintenant le bouton gauche de la souris pour démarrer le drag/drop.

    Commençons par créer notre classe :

    using System;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows;
    
    namespace Carbenay.Michael.Controls
    {
        public class DraggableBorder : Border
        {
        }
    }

    Il faut ensuite détecter le mouvement de déclenchement :

    • Lorsque l'on clique sur l'élément (événement MouseDown) nous allons conserver la position de la souris
    • Lorsque l'on bouge la souris sur l'élément (évenement MouseMove) après ce clic, il faudra comparer la position de la souris actuelle par rapport a celle au moment de l'appui et si celle-ci est suffisamment différents – ce « suffisamment » étant défini par un paramètre de Windows – déclencher le drag/drop
    • Lorsque le bouton de la souris est relâché au dessus de l'élément (événement MouseUp), le drag/drop est annulé

    Pour être sûr de traiter les événements le plus tôt possible, nous n'allons pas vraiment nous connecter aux événements MouseDown, MouseMove, MouseUp mais à leur version « Preview » (pour plus de renseignement sur les événements et leur versions Preview, je vous conseille Application = Code + Markup de Charles Petzold) :

     

        public DraggableBorder()
        {
            PreviewMouseDown += new MouseButtonEventHandler(MyPreviewMouseDown);
            PreviewMouseMove += new MouseEventHandler(MyPreviewMouseMove);
            PreviewMouseUp += new MouseButtonEventHandler(MyPreviewMouseUp);
        }
        
        // position de la souris sur le MouseDown
        private Point _mouseDownPoint = new Point();
        // a true si le MouseDown a bien été détecté 
        // dans le controle
        private bool _wasMouseDownInControl = false;
    
        void MyPreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            // on "annule" le drag drop
            _wasMouseDownInControl = false;
        }
    
    
        void MyPreviewMouseMove(object sender, MouseEventArgs e)
        {
            // si le bouton gauche de la souris est toujours 
            // appuyé on regarde si la position actuelle
            // est suffisament eloignée de la position de
            // départ et si oui, on lance le drag/drop
            if (e.LeftButton == MouseButtonState.Pressed 
                && _wasMouseDownInControl)
            {
                Point pos = e.GetPosition(null);
    
                if (SystemParameters.MinimumHorizontalDragDistance <=
    Math.Abs(position.X - _mouseDownPoint.X) || SystemParameters.MinimumVerticalDragDistance <=
    Math.Abs(position.Y - _mouseDownPoint.Y)) { BeginDrag(); _wasMouseDownInControl = false; e.Handled = true; return; } } } void MyPreviewMouseDown(object sender, MouseButtonEventArgs e) { // on enregistre la position de la souris // pour détecter le mouvement de drag/drop _mouseDownPoint = e.GetPosition(null); _wasMouseDownInControl = true; }

    Reste la méthode BeginDrag dont le rôle est de préparer les données et de déclencher le drag/drop. Pour cette première version nous allons faire très simple et créer un objet de donnée avec une ligne de texte simple :

        private void BeginDrag()
        {
            DataObject data = new DataObject();
            data.SetData(DataFormats.Text, "Test");
            DragDropEffects de = DragDrop.DoDragDrop(this, 
                                    data, 
                                    DragDropEffects.Copy);
        }
    Au programme du prochain numéro : qu'est-ce que le DataObject et qu'est-il possible d'y mettre ?

  • Créer un conteneur WPF « draggable » (part 1)

    Où l'on choisit le type de contrôle

    Première question intéressante : quel type de contrôle allons-nous bien pouvoir réaliser ?

    Si vous avez déjà un peu fait de développement de contrôles en .net (que ce soit pour asp.net, WinForms ou WPF), vous savez que, chez Microsoft (et dans la plupart des autres solutions graphiques aussi…), l'on vous propose toujours 2 types d'extensibilités sur les contrôles.

    Les user-controls

    Que nous ne traduiront pas par « contrôle utilisateur » mais par « contrôle composite » du fait de leur utilisation principale : permettre de créer des « groupes » de contrôles réutilisables comme par exemple un contrôle de saisie d'adresse. Construire ce genre de contrôle est extrêmement facile : les fenêtres ou pages que vous avez déjà créées avec WPF/ASP.net/WinForms n'étaient, en concept, que des User Controls un peu particulier.

    Ce type de contrôle est très utile aussi bien pour ne pas ré-écrire 50 fois le même groupe que pour fragmenter votre interface en quelque chose de plus facile à gérer. Prenons un exemple que vous avez peut-être déjà été amené à rencontrer : lorsque l'on crée une boite de dialogue pour définir les options d'un logiciel, celle-ci à toujours une tendance à intégrer de plus en plus d'onglets au fur et à mesure des différentes versions et par faire 5 000 ou 10 000 lignes de codes pour gérer tous les boutons et autres options avancées présents sur les pages. En scindant chaque page en un UserControl différent, le code devient beaucoup plus lisible et maintenable.

     OptionDialog218

     

    Les custom controls

    L'autre solution d'extension proposée est de dériver votre contrôle de l'un de ceux existants. Le but est dans ce cas très différent : il s'agit d'apporter de nouvelles fonctionnalités à un contrôle, voire de créer un contrôle complètement nouveau en se basant sur l'un de ceux préexistants. Besoin d'une checkbox qui disparait lorsque l'on met la souris dessus ? (je n'ai jamais dit que les fonctionnalités devaient être intelligentes...) Créez une nouvelle classe, dérivez la de System.Windows.Controls.Checkbox et ajoutez ce qu'il faut dans les gestionnaires d'événements de la souris et le tour est joué !

    Et le vainqueur est :

    Dans notre cas, la solution était toute trouvée : nous ne souhaitions pas créer un contrôle composite mais bien étendre les fonctionnalités de l'un des nombreux conteneurs que propose WPF. La seconde partie de la question – à savoir quel contrôle nous allions étendre – a été très vite traitée : nous utilisons dans l'application des « Border » dans tous nos templates de données, un simple rechercher/remplacer dans nos différentes feuilles de styles différents dictionnaires de ressources nous permettra d'ajouter la fonctionnalité sans trop de manipulations.

  • Créer un conteneur WPF « draggable » (part 0)

    Il y a quelques semaines, pour le projet d'un partenaire, j'ai été amené à créer un contrôle réutilisable permettant le drag de son contenu. Ce n'est pas bien compliqué, et je vais donc vous en faire part.

    Le problème posé était assez simple : permettre à l'utilisateur de faire du drag-drop d'éléments pour recopier des informations (par exemple, permettre de glisser le dossier d'un client vers une zone « à synchroniser » pour l'avoir en mode déconnecté du serveur). Comme les développeurs n'avaient pas vraiment envie de repasser sur tous les écrans et d'y inclure la notion de drag/drop, la solution d'un contrôle personnalisé autorisant le drag a bien vite été choisie.

    Les posts correspondants :

    Ca c'est pour plus tard :

    • part 5 : Dans laquelle l'aspect graphique de la chose est renforcé
    • part 6 : Où l'on se rend compte que l'on est pas tout seul