Remplace le sujet http://www.developpez.net/forums/d14...fenetre-excel/
Les questions pour positionner une UserForm sont légions c'est que le besoin existe. Comme je ne trouvais pas de solutions complètes et satisfaisante, je m'y colle.
Ce projet ne pose aucune difficulté particulière pour les personnes connaissant les modules de classes et l'A.P.I. Windows.
Cahier des charges
L'utilisateur doit pouvoir afficher le formulaire à des positions précises (Nord - Sud - Sud Est - Ouest - etc.) ou librement.
Le formulaire doit "suivre" Excel quand sa fenêtre est déplacée ou redimensionnée.
Cela n'a de sens que si l'utilisateur choisi d'afficher le formulaire en mode vbModeless, puisque en mode modal, il ne peut pas déplacer ou dimensionner la fenêtre Excel (lol).
Enfin le traitement doit pouvoir s'appliquer à n'importe quel formulaire.
Architecture
L'utilisateur créé une procédure qui appelle le point d'entré (procédure FormMagnet du module pkg_Magnet).
Il passe divers paramètres, nom du formulaire, position, etc. (module pkgSample).
Le point d'entré met en place un Timer pour "écouter" Excel puis créé l'instance de la classe CUserForm qui gère l'affichage.
Le timer averti le programme qu'il faut déplacer notre formulaire.
Ce Timer est codé dans un simple module pkgTimer (idéalement, ce devrait être une classe, c'est dans ma TOUTOU list).
Plusieurs autres classes sont nécessaires pour récupérer les "mesures" d'Excel. Elles sont toutes manipulées par la classe CUserForm.
- CAppInfo
- CScreen
- CScreenConvert
Techniques
Pour récupérer les dimensions et positions encore faut-il s'entendre sur la zone à considérer.
- Au plus haut niveau nous avons la fenêtre Excel, elle contient les menus et barres d'outils ou les onglets.
- La barre de formule et d'adresses viennent se loger dessous.
- Enfin on trouve une zone que l'on pourrait appeler le "bureau Excel".
C'est dans cette zone que viennent s'afficher les classeurs et c'est la zone que j'utiliserais car je ne souhaite pas que l'UserForm s'affiche sur la barre d'onglets.
Remarque : avec un peu d'adaptation, on pourrait "aimanter" non pas au bureau mais au classeur (encore un truc dans ma TOUT DOUX list).
Il existe des zones complémentaires à prendre en compte pour plus de précisions ce sont les bordures et barres de défilement.
Pour appliquer le traitement à n'importe quel formulaire, il faut que la classe CUserForm soit indépendante. Les instructions du style Set myForm = UserForm1 sont donc interdites.
En mode vbModeless, il serait possible d'exécuter 2 formulaires simultanément et ce n'est pas souhaitable. Un contrôle est mis en place pour empêcher ce comportement.
Coding
Plusieurs moyens s'offrent à nous pour récupérer les mesures.
Les propriétés Excel.
Excel travaille avec essentiellement 4 informations Top - Left - Width - Height.
Ces deux dernières sont déclinés en utilisant la zone utilisable UsableWidth et UsableHeight, c'est la zone que j'apelle le "Bureau" Excel.
Les A.P.I. Windows
Lorsque l'on ausculte la liste des classes d'Excel chargées dans Windows avec un outil de Spy, on retrouve nos zones.
XLMAIN représente l'application Excel.
XLDESK c'est la zone utilisable.
La classe EXCEL7 représente le classeur, c'est celle qu'il faudrait utiliser pour "magnétiser" au classeur.
Les autres classes ne sont pas utiles au projet, d'ailleurs certaines restent obscures et peu de documentation existe.
- EXCEL;
La partie gauche de la barre de formule (le symbole de l'assistant fonction).
Inclu la barre d'adresse.- ComboBox : La liste des noms de cellules (barre d'adresse).
- EXCEL< : Zone d'édition de la barre de formule.
- EXCEL2
The four command bar docking areas (top, left, right and bottom). Comment : my english is poor as my larfeuille.
- MsoCommandBar : A command bar.
- EXCELE
A window used to provide in-sheet editing of embedded charts. Comment : see my previous comment.- EXCEL4 : Barre de statut.
- EXCELI
- EXCELH
- EXCELG
- XLCTL
L'intérêt de passer par l'A.P.I. c'est qu'elle donne des informations complémentaires à savoir les positions droite et basse de la zone considérées.
En utilisant les mesures de l'application cela n'offre aucun intérêt puisque nous possédons les propriétés Left et Top.
Mais pour la zone utilisable, Excel ne propose que Width et Height (les dimensions) ce qui est insuffisant, il manque la position.
Récupérer les informations données par Windows
En premier lieu il faut récupérer les handles des diverses zones, pour ensuite reprendre les informations souhaitées. C'est le rôle de la classe CAppInfo.
Pour les mesures des barres de défilement et bordures de fenêtres, c'est la classe CScreen qui le prend en charge.
Enfin Windows et VB n'utilisent pas la même unité de mesure (les cons !), le premier utilise le pixel alors qu'Excel le twips.
La classe CConvert calcule le ratio à appliquer pour la conversion en fonction de l'écran.
Appliquer le traitement quelque soit le formulaire
Excel propose la collection UserForms de l'objet VBA qui permet de parcourir les formulaires par un indice.
Ainsi pour ajouter un formulaire, il suffit de l'ajouter à la collection VBA.UserForms.Add ("NomFormulaire").
Pour le manipuler il suffit d'aller à l'indice du formulaire et l'utiliser comme l'objet.
À la fermeture du formulaire, l'élément est détruit de la collection.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 VBA.UserForms.Add ("NomFormulaire") VBA.UserForms(0).Caption = "Titre" VBA.UserForms(0).Show vbModeless
Écouter Excel
Il existe un timer dans Excel Application.OnTime. Mais pour plus de précisions, j'utilise le Timer version A.P.I. Windows.
Le timer est déclenché sur la procédure point d'entrée. À la fermeture du formulaire le timer est arrêter pkgTimer => EndTimer.
Le timer attend en paramètre la fonction à exécuter. C'est la procédure pkgMagnet => TimerProc, où les anciennes et nouvelles mesures sont comparées et qui agit en conséquence.
Usage
Inserer une UserForm et écrire les deux lignes suivantes sur l'évènement , surtout ne pas oublier ces deux lignes !
Remarque
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Private Sub UserForm_Terminate() EndTimer Set myFormUser = Nothing
Si le formulaire était toujours affiché en mode vbModal, je pourrais écrire ces deux lignes à la fin de la procédure point d'entrée pkg_Magnet => FormMagnet().
Mais le comportement de vbmodeless qui poursuit l'exécution du code quand l'UserForm est affichée m'en empêche, ce qui me gène fortement.
Si quelqu'un sait comment contourner ce comportement !
Il y aurait bien la possibilité de faire une boucle de temporisation, mais c'est loin d'être propre.
Le code est largement commenté, il a été créé à l'aide d'Excel 2010 32 bits sur Windows 7 - 32 bits et n'a pas été testé dans d'autres conditions.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 Do While VBA.UserForms(0).Visible DoEvents Loop
Partager