Bien que MessageBox soit bien adapté aux avertissements de bas niveau, elle n'est souvent pas la solution la plus conviviale lorsqu'il s'agit d'avertissements métier. Ce post décrit une technique permettant d'afficher un UserControl de manière pseudo-modale en jouant à la fois avec un effet visuel et le focus. Les bénéfices d'une telle technique sont les suivants :

  • Améliorer le look&feel en proposant un avertissement dont l'esthétique s'intègre mieux au flow l'application
  • Afficher autre chose que du texte
  • Permettre le déplacement de la fenêtre parente même si le UserControl modal est affiché


20080915 BeforeAfter

Le projet exemple affiche le UserControl pseudo-modal en cliquant sur le bouton "Afficher MyUserControl". Lors de cet affichage les boutons d'origine deviennent inaccessibles tandis que le focus est passé de manière exclusive au nouveau contrôle. Le bouton "Fermer" de MyUserControl permet de revenir à l'écran d'origine.

Le fonctionnement se base sur le Panel principal de la fenêtre parente qui est de type Canvas. Cela permet ainsi de positionner le UserControl modal où l'on désire sur le plan, tout en le superposant par le biais de la propriété attachée ZIndex. Si le Panel principal de votre application n'est pas un Canvas - et cela n'est en effet quasiment jamais le cas - il suffira de placer le précédent panel parent comme seul enfant du nouveau Canvas (par exemple Window.Grid deviendrait donc Window.Canvas.Grid).

Lorsque l'on souhaite afficher le UserControl modal, les opérations suivantes sont effectuées:

  • Un Rectangle gris et transparent est ajouté comme enfant du Canvas. Son ZIndex élevé (998 dans l'exemple) bloque l'accès par la souris aux contrôles d'origine
  • Le UserControl modal que l'on souhaite afficher est instancié, dimensionné/positionné puis ajouté au Canvas avec un ZIndex plus elevé que le Rectangle (999)
  • La navigation par TAB est définie comme cyclique sur ce contrôle, afin de forcer le focus clavier à rester dans le UserControl, finalisant ainsi l'effet modal
    control.SetValue(KeyboardNavigation.TabNavigationProperty, KeyboardNavigationMode.Cycle);
  • On s'attache à l'évènement IsEnabledChanged du contrôle modal afin de pouvoir le supprimer (ainsi que le Rectangle) lorsque IsEnabled passe à false

    control.IsEnabledChanged += new DependencyPropertyChangedEventHandler(delegate(object sender2, DependencyPropertyChangedEventArgs e2)

    {

    if ((bool)e2.NewValue == true) return;

            myCanvas.Children.Remove(control);

                   myCanvas.Children.Remove(grayRectangle);

           });

  • Le focus clavier est donné au nouveau contrôle par le biais de MoveFocus
    control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

20080915 Popup layers

Note: le UserControl modal étant situé dans le visual tree de la fenêtre parente, les styles définis au niveau fenêtre sont appliqués.

Merci à Benoist et Maxime pour l'idée de ce post.