Tutoriels > Tutoriels & Astuces Excel > L'API Windows > mDF MsgBoxPerso « le Retour »

mDF MsgBoxPerso « le Retour »

Publié par myDearFriend! le 02-11-2008 (8654 lectures)

Une «vraie-fausse» MsgBox avec boutons personnalisés et bien plus encore...

 

Voici donc le 3ème et dernier volet des articles que je consacre à la MsgBox, outil bien connu des développeurs VBA puisqu'il s'agit du premier moyen à disposition pour interagir avec l'utilisateur.

 

  • Dans un premier article intitulé Utilisation des MsgBox et m'adressant plus particulièrement aux débutants, j'ai souhaité aborder le B-A-BA en la matière et introduire les deux façons d'employer une MsgBox : utiliser la MsgBox en tant que méthode ou utiliser la MsgBox en tant que fonction, voilà bien toute une différence ! Cette introduction au sujet nous rappelle aussi que seuls 7 boutons prédéterminés sont mis à notre disposition : OK Annuler Abandonner Recommencer Ignorer Oui Non. Et il n'est d'ailleurs pas permis de les combiner entre eux comme on le souhaite...

  • Dans un deuxième article MsgBox personnalisées (API) , je vous ai présenté une façon d'obtenir une MsgBox avec boutons personnalisés en utilisant exclusivement l'Api Windows. Nous l'avions vu dans cet article, la méthode est certe efficace, mais elle souffre elle aussi de contraintes assez gênantes, à savoir : le nombre de boutons personnalisables est restreint à deux (avec toutefois la possibilité d'un bouton Annuler supplémentaire) et les libellés des boutons sont limités en taille (longueur).


L'objectif est donc de vous présenter cette fois ce que je pense être une solution viable et représentant une vraie alternative à l'existant.



Les contraintes fixées

    • Le nombre de boutons personnalisables ne doit pas être limité (un grand nombre de boutons peut toutefois nuire à l'esthétique et à la convivialité des dialogues avec l'utilisateur. Ce sera donc au développeur de fixer SA limite)
    • La taille des boutons devra être fonction de la taille des libellés choisis (et non l'inverse).
    • Le code doit pouvoir s'adapter aux souhaits du développeur sans avoir à être remanié. Un panel d'options débrayables doit donc être envisagé et l'ensemble doit rester le plus léger possible.
    • Le code ne doit pas remettre en cause les habitudes de développement et doit donc coller au plus près d'une MsgBox classique en terme d'appel de fonction et/ou méthode. Les nouveaux arguments mis à disposition devront donc être optionnels afin de permettre au développeur de se familiariser rapidement avec MsgBoxPerso. Par ailleurs, l'aide intuitive avec liste des arguments possibles lors de la saisie dans l'éditeur VBE serait bienvenues.
    • Tout comme la vraie MsgBox, la MsgBoxPerso devra pouvoir "capter" la réponse utilisateur pour permettre le bon déroulement du programme. Le choix d'une fonction personnalisée est donc requis pour retourner le choix de l'utilisateur.
    • L'apparence de la boîte de dialogue doit se rapprocher au plus près d'une MsgBox dite classique (position des boutons, icônes d'état, etc...)
 
 

Méthode retenue 

A y réfléchir, je ne vois qu'une seule façon d'aborder le problème : il nous faut avoir recours à un Userform simulant une vraie MsgBox.
Mais bien évidemment, hors de question de créer un Userform «en dur» dans le projet puisque d"une part, celui-ci doit être réutilisable à souhait et d'autre part, son apparence devra varier en fonction de chaque appel dans le programme. Par ailleurs, un Userform «en dur» et les contrôles associés ajouteraient un poids non négligeable au projet et ne répondraient donc pas aux exigences fixées plus haut.
 
J'ai donc retenu le principe suivant : la création dynamique d'un Userform «jetable» après utilisation.
 
Le point qui m'a demandé le plus de recherches et de fil à retordre, ce fut : les icônes. En effet, comment insérer les icônes des vraies MsgBox dans un Userform, ce dernier étant, de plus, généré à la volée ?
 
Seule possibilité : le recours à l'Api Windows, mais sur ce seul point cette fois !
 
Il a donc fallu que je remonte les manches et que je m'y colle... N'étant pas un virtuose dans le domaine, ça n'a pas été très simple, mais j'y suis finalement parvenu !
 
Je résume la situation :
  • Il nous faut une fonction personnalisée avec ses arguments attendus.
  • A l'appel de cette fonctionn, un Userform sera généré à la volée (avec l'apparence de notre boîte de dialogue).
  • Le choix utilisateur sera ensuite retourné par cette fonction et le Userform détruit.
  

Améliorations supplémentaires

Comme vous allez le voir, cette méthode retenue m'a permis d'ajouter d'autres options intéressantes, à savoir :
 
Concernant le texte du message dans la boîte de dialogue :
      • Le changement de police et de la taille des caractères sera possible.
      • Les attributs Gras et Italic seront également disponibles.
      • L'alignement du texte à gauche, au centre ou à droite sera aussi permis au développeur.
Concernant la MsgBox elle-même (le Userform) :
      • Par défaut, la boîte de dialogue sera centrée sur l'écran. Mais il sera possible au développeur d'indiquer des coordonnées d'affichage précises pour en définir la position souhaitée.
 

 

L'objectif 

L'objectif, c'est de vous permettre d'obtenir l'affichage de quelque chose comme ça, en ajoutant une seule ligne de code à votre projet :

 

mDF MsgBoxPerso

 

 

Le code de cette fonction personnalisée

Le code de la fonction personnalisée, je l'ai réalisé pour vous et je vous le fourni sous forme d'un module de code à insérer - tel quel - dans votre projet.

 

Vous retrouverez le module complet au bas de cet article (code à copier dans un module de code standard vierge ou fichier à télécharger et à importer directement dans votre projet dans l'éditeur VBE) 

 

Télécharger le module de code Au bas de cet article , vous pouvez télécharger le module de code  .txt prêt à être importé dans votre projet VBA.
 
 

La mise en oeuvre

Je vous propose donc un simple module de code à copier ou importer dans votre projet VBA, module qui vous permettra d'obtenir facilement des MsgBox originales et rendre ainsi plus efficace l'interaction avec vos utilisateurs.

Une fois le module de code importé, tout est fait pour rendre l'utilisation la plus simple possible et pour rester au plus près de la syntaxe VBA que vous connaissez avec les MsgBox « classiques ». Le développeur que vous êtes ne sera donc pas dérouté par cette mise en oeuvre VBA.
 
Attention! ATTENTION!

Le code VBA utilisé nécessite impérativement que l'option

«Faire confiance au projet Visual Basic»

soit cochée dans le menu Outils / Macro / Sécurité... / onglet Editeurs approuvés.

(sur Excel 2003)

 

MsgBoxPerso : syntaxe et arguments

L'appel de la fonction MsgBoxPerso  est le plus proche possible de ce que vous connaissez déjà.


Syntaxe :

MsgBoxPerso(Prompt [, Title] [, Icon] [, Buttons] [, FontName] [, FontSize] [, FontStyle] [, txtAlign] [, X] [, Y])


Les arguments nommés sont décrits ci-dessous :

ArgumentsDescriptions
 Prompt
  • Obligatoire. Chaîne de caractères représentant le texte de la boîte de dialogue.
 Title
  • Facultatif. Chaîne de caractères valant titre de la boîte de dialogue.
    (à défaut, le nom de l'application sera pris pour titre de la MsgBox)
 Icon
  • Facultatif. Peut être une des constantes VBA suivantes : 0 - vNoIcon (par défaut), 1 - vCritical vCritical, 2 - vQuestion vQuestion, 3 - vExclamation vExclamation ou 4 - vInformation vInformation.
 Buttons
  • Facultatif. Chaîne de caractères représentant les libellés des boutons.
    Chaque bouton sera séparé par un caractère | (pipe).
    Pour définir le bouton qui aura le focus (bouton par défaut), faites suivre son libellé d'un caractère * (étoile).
 FontName
  • Facultatif. Chaîne de caractères représentant la police à utiliser pour le message. Ex : "Arial"
 FontSize
  • Facultatif. Valeur numérique pour la taille des caractères du message. Valeurs admises : de 0 à 72.
 FontStyle
  • Facultatif. Représente le style Normal, Gras ou Italic.
    Valeurs admises : 0 - vNormal (par défaut), 1 - vBold, 2 - vItalic ou 3 - vBoldItalic.
 txtAlign
  • Facultatif. Représente l'alignement du texte du message.
    Valeurs admises : 0 - vLeft (par défaut), 1 - vCenter ou 2 - vRight.
 X
  • Facultatif. Valeur numérique représentant la coordonnée X du coin supérieur gauche de la boîte de dialogue.
 Y
  • Facultatif. Valeur numérique représentant la coordonnée Y du coin supérieur gauche de la boîte de dialogue.

Ainsi, pour obtenir la boîte de dialogue suivante :

MsgBox personnalisée

La simple instruction suivante est suffisante (soit vRet une variable de type Integer) :

 

vRet = MsgBoxPerso("Mon message...", "Mon titre", vQuestion, "Bouton 1|Bouton 2")


Il convient ensuite de voir comment exploiter la réponse (vRet) de l'utilisateur. C'est ce que nous allons voir maintenant...



Valeur de retour

L'intérêt ici, est d'utiliser la procédure en tant que fonction afin de recevoir en retour le choix de l'utilisateur. Le terme boîte de «dialogue» prend ici tout son sens.

Comme notre MsgBox bien connue, la MsgBoxPerso retourne une valeur numérique nous indiquant le bouton sélectionné par l'utilisateur. On notera toutefois une différence avec la valeur retournée par la MsgBox originale :

 

La fonction personnalisée MsgBoxPerso retourne le numéro d'ordre du bouton cliqué par l'utilisateur, valeur de type Integer :

 

Si l'utilisateur clique sur ...
... voici la valeur retournée.
Bouton perso n°11
Bouton perso n°22
Bouton perso n°33
Etc...Etc...


En cas d'erreur, la fonction pourra toutefois retourner la valeur 0 (ça sera le cas notamment, si l'option «Faire confiance au projet VB» n'est pas cochée dans les paramètres de sécurité).

Exemple d'utilisation

MsgBox personnalisée

Le code nécessaire pour afficher cette MsgBoxPerso et en exploiter la réponse utilisateur peut ressembler à celui-ci :

 

Dim Message As String
Dim vRet As Integer

Message = "Mes cher(e)s ami(e)s," & vbLf & "Comment trouvez-vous cette mise en oeuvre ?"

vRet = MsgBoxPerso(Message, , vQuestion, "C'est très simple|Je n'ai rien compris|Sans avis")

Select Case vRet
Case 1
' ici le traitement si la réponse est "C'est très simple"
' ...
Case 2
' ici le traitement si la réponse est "Je n'ai rien compris"
' ...
Case 3
' ici le traitement si la réponse est "Sans avis"
' ...
End Select

 

 

Le module de code

Télécharger le module de code Au bas de cet article , vous pouvez télécharger le module de code  .txt prêt à être importé dans votre projet VBA.

Voici le contenu du module de code nécessaire à mDF_MsgBoxPerso :

Le module de code nécessaire à l'utilisation de mDF MsgBoxPerso
Option Explicit 
Option Private Module

' <<<<< LE PRESENT MODULE DE CODE EST A INSERER DANS VOTRE PROJET TEL QUEL ! >>>>>

' ***************************************************************************************
' *                        ATTENTION!                             *
' *                                                                                      *
' * Pour fonctionner, ce module nécessite que l'option "Faire confiance au projet *
' *       Visual Basic" soit cochée dans le menu Outils / Macro / Sécurité...        *
' ***************************************************************************************

'---------------------------------------------------------------------------------------
' Author    : Didier FOURGEOT (myDearFriend!) - www.mdf-xlpages.com
' Date      : 02/11/2008
' Topic     : mDF MsgBoxPerso et boutons personnalisés
'---------------------------------------------------------------------------------------

' UTILISATION :
' -----------
' Dans votre code, vous ferez appel à la MsgBoxPerso comme vous le faites pour une MsgBox
' "classique", mais avec quelques arguments (optionnels) supplémentaires :

' SYNTAXE de la fonction :
' ----------------------
' R = MsgBoxPerso(Prompt, Title, Icon, Buttons, FontName, FontSize, FontStyle, txtAlign, X, Y)

' ARGUMENTS de la fonction :
' ------------------------
'   * Prompt:    Obligatoire. Il s'agit du texte de votre message.
'   * Title:     Facultatif. Titre de votre message.
'   * Icon:      Facultatif. Représente l'icône que vous voulez voir dans le message.
'                            Valeurs admises : 0 - vNoIcon (par défaut), 1 - vCritical,
'                                              2 - vQuestion, 3 - vExclamation ou
'                                              4 - vInformation.
'   * Buttons:   Facultatif. Chaîne de caractères représentant les libellés des boutons.
'                            Chaque bouton sera séparé par un caractère | (pipe).
'                            Pour définir le bouton qui aura le focus (bouton par défaut),
'                            faites suivre son libellé d'un caractère * (étoile).
'                            Exemple : Oui|Non*|Annuler
'                            ("Non" sera le bouton par défaut)
'   * FontName:  Facultatif. Chaîne de caractères représentant la police à utiliser pour le
'                            message. Exemple : "Arial"
'   * FontSize:  Facultatif. Valeur numérique pour la taille des caractères du message.
'                            Valeurs admises : de 0 à 72.
'   * FontStyle: Facultatif. Représente le style Normal, Gras ou Italic.
'                            Valeurs admises : 0 - vNormal (par défaut), 1 - vBold,
'                                              2 - vItalic ou 3 - vBoldItalic.
'   * TextAlign: Facultatif. Représente l'alignement du texte du message.
'                            Valeurs admises : 0 - vLeft (par défaut), 1 - vCenter ou
'                                              2 - vRight.
'   * X:         Facultatif. Valeur numérique représentant la coordonnée X du coin supérieur
'                            gauche de la boîte de dialogue.
'   * Y:         Facultatif. Valeur numérique représentant la coordonnée Y du coin supérieur
'                            gauche de la boîte de dialogue.

' RETOUR de la fonction :
' ---------------------
' Utilisée en tant que fonction, MsgBoxPerso retourne une valeur de type Integer correspondant
' au numéro d'ordre du bouton cliqué par l'utilisateur.
'
' EXEMPLE :
'
' Dim vRet As Integer
' vRet = MsgBoxPerso("Quel jour de la semaine ?", , , "Lundi|Mardi|Mercredi|Jeudi|Vendredi")
'
' Si l'utilisateur choisit "Jeudi", alors la fonction retournera la valeur 4.

' INFOS COMPLEMENTAIRES :
' ---------------------
' On peut aussi faire l'appel à l'aide des arguments nommés :
'   vRet = MsgBoxPerso(Prompt:="Etes-vous d'accord ?", Buttons:="Oui|Non|Sans avis")
'
' On peut aussi utiliser la macro comme méthode (sans retour de résultat) :
'   MsgBoxPerso "C'est fini !", , , "Ok"

' **************************************************************************************

' Déclaration des fonctions Api Windows pour récupération des Icônes de MsgBox
Public Declare Function FindWindowA& Lib "user32" (ByVal lpClassName$, ByVal lpWindowName$)
Public Declare Function GetDC& Lib "user32" (ByVal hwnd&)
Public Declare Function LoadIconA& Lib "user32" (ByVal hInstance&, ByVal lpIconName&)
Public Declare Function DrawIcon& Lib "user32" (ByVal Hdc&, ByVal X&, ByVal Y&, ByVal hIcon&)
Public Declare Function DestroyIcon& Lib "user32" (ByVal hIcon&)
' Pour les arguments nommés de la fonction
Public Enum StyleIcon
    vNoIcon
    vCritical
    vQuestion
    vExclamation
    vInformation
End Enum
Public Enum
TextAlign
    vLeft
    vCenter
    vRight
End Enum
Public Enum
StyleFont
    vNormal
    vBold
    vItalic
    vBoldItalic
End Enum
'Variable publique pour "capter" la réponse utilisateur
Public VmsgBoxValue

Function MsgBoxPerso(ByVal Prompt, Optional ByVal Title, Optional ByVal Icon As _
    StyleIcon = vNoIcon, Optional ByVal Buttons = "Ok", Optional ByVal FontName _
    = "Tahoma", Optional ByVal FontSize = 10, Optional ByVal FontStyle As StyleFont _
    = vNormal, Optional ByVal Align As TextAlign = vLeft, Optional ByVal X = 0, _
    Optional ByVal Y = 0) As Integer
Dim
Btn
Dim Usf As Object, lblM As Object
Dim
Icn As StyleIcon
Dim TestVbp$
Dim LngMaxB%, MargBtn%, Margin%
Dim i As Byte, xBtn As Byte
    'Test si "Faire confiance au projet VB" est coché
    On Error Resume Next
    TestVbp = ThisWorkbook.VBProject.Name
    On Error GoTo 0
    If TestVbp = vbNullString Then
        MsgBox "L'utilisation de la MsgBoxPerso nécessite que l'option" _
            & vbLf & """Faire confiance au projet Visual Basic"" soit cochée" _
            & vbLf & "dans menu Options / Macro / Sécurité...", _
            vbCritical, "mDF MsgBoxPerso..."
        MsgBoxPerso = 0
        Exit Function
    End If

    '
    Btn = Split(Buttons, "|")
    Icn = IIf(Icon < 1, 0, IIf(Icon > 4, 0, Icon + 32512))
    Margin = IIf(Icn > 0, 45, 0)
    FontStyle = Abs(Val(FontStyle))
    If FontStyle > 3 Then FontStyle = 0
    X = Abs(Val(X))
    Y = Abs(Val(Y))
    'Création du USF
    Set Usf = ThisWorkbook.VBProject.VBComponents.Add(3)
    'Title
    If IsMissing(Title) Then Title = Application.Name
    Usf.Properties("Caption") = Title
    Usf.Properties("StartUpPosition") = IIf(X + Y = 0, 1, 0)
    'Création zone de Prompt
    Set lblM = Usf.Designer.Controls.Add("Forms.Label.1")
    With lblM
        .Move 0, 15
        .WordWrap = False
        .Font.Size = Application.Min(Abs(Val(FontSize)), 72)
        .Font.Name = CStr(FontName)
        .TextAlign = IIf(Align < 0, 1, IIf(Align > 2, 1, Align + 1))
        .Font.Bold = FontStyle Mod 2 <> 0
        .Font.Italic = FontStyle > 1
        .AutoSize = True
        .Caption = Prompt
        .AutoSize = False
    End With

    'Création Buttons
    xBtn = 1    'Focus sur le premier bouton par défaut
    For i = 0 To UBound(Btn)
        With Usf.Designer.Controls.Add("Forms.CommandButton.1")
            .AutoSize = True
            .Caption = Application.Substitute(Btn(i), "*", "")
            LngMaxB = Application.Max(LngMaxB, .Width)
            .AutoSize = False
            'On mémorise le bouton désigné par défaut (terminé par *)
            If Right(Btn(i), 1) = "*" Then xBtn = i + 1
        End With
    Next
i
    LngMaxB = Application.Max(LngMaxB, 50)
    'Placement des contrôles sur le USF et insertion du code VBA évènementiel
    With lblM
        Usf.Properties("Width") = Application.Max((LngMaxB + 10) * _
            (UBound(Btn) + 1) + 5, .Width + 24)
        Usf.Properties("Height") = 85 + .Height
        .Move Margin + 10, 15, Usf.Properties("Width") - 24, .Height
    End With
    With
Usf
        MargBtn = (.Properties("Width") - (LngMaxB + 5) * (UBound(Btn) + 1)) 2
        'Procédure UserForm_Activate()
        With .CodeModule
            .InsertLines .CountOfLines + 1, "Private Sub UserForm_Activate(): " _
                & "Dim hwnd&, hIcon&: DoEvents:" _
                & "hwnd = FindWindowA(vbNullString, Me.Caption):" _
                & "hIcon = LoadIconA(0&," & Icn & "):" _
                & "DrawIcon GetDC(hwnd), 26, 24, hIcon:" _
                & "DestroyIcon hIcon: Me.Controls(""CommandButton" & xBtn & """)" _
                & ".Setfocus:Beep:End Sub"
        End With
        For
i = 0 To UBound(Btn)
            .Designer.Controls("CommandButton" & i + 1).Move Margin + MargBtn + _
                (LngMaxB + 5) * i, lblM.Top + lblM.Height + 22, LngMaxB, 20
            'Procédures évènementielles liées aux Buttons
            With .CodeModule
                .InsertLines .CountOfLines + 1, "Sub CommandButton" & i + 1 _
                    & "_Click():VmsgBoxValue =" & i + 1 & " :Unload Me:End Sub"
            End With
        Next
i
        Usf.Properties("Width") = Usf.Properties("Width") + Margin
        'Interdire fermeture par la croix
        With Usf.CodeModule
            .InsertLines .CountOfLines + 1, "Private Sub UserForm_QueryClose(Cancel " _
                & "As Integer, CloseMode As Integer):Cancel = CloseMode = 0:End Sub"
        End With
        'Affichage la MsgBoxPerso, puis auto-destruction du USF
        If X + Y > 0 Then
            Usf.Properties("Left") = X
            Usf.Properties("top") = Y
        End If
        VBA.UserForms.Add(.Name).Show
    End With
    ThisWorkbook.VBProject.VBComponents.Remove Usf
    MsgBoxPerso = VmsgBoxValue
End Function

 

 

Démonstration

Pour illustrer les différentes possibilités et l'utilisation du module de code téléchargeable ci-dessous , vous trouverez également en Section Téléchargements - catégorie Classeurs Exemples / API Windows, le fichier mDF MsgBoxPerso Démo .

mDF MsgBoxPerso






Pour toutes vos questions ou si vous rencontrez des difficultés, n'hésitez pas à rejoindre nos Forums de Discussions !

Tags Fonction   Dynamique   VBA   Bouton   Macro   Exemple   Procédure   Code   API   MsgBox   Méthode   MsgBoxPerso   Personnaliser   Userform   Module   Projet   Sécurité   Icône  

 

Autres articles dans cette catégorie Publié le Vues
mDF MsgBoxPerso « le Retour » 02-11-2008 8655
MsgBox personnalisées (API) 18-11-2007 24378
API Windows - Introduction 03-07-2006 6805