Ceci est une page optimisée pour les mobiles. Cliquez sur ce texte pour afficher la vraie page.

XL 2016 VBA - Problème positionnement UserForm

  • Initiateur de la discussion Initiateur de la discussion Dudu2
  • Date de début Date de début

Boostez vos compétences Excel avec notre communauté !

Rejoignez Excel Downloads, le rendez-vous des passionnés où l'entraide fait la force. Apprenez, échangez, progressez – et tout ça gratuitement ! 👉 Inscrivez-vous maintenant !

Dudu2

XLDnaute Barbatruc
Bonjour,
J'essaie de positionner un UserForm sur un Moniteur, en Left = 1044.
Que je le fasse en .Left = ou en .Move il ne tient pas compte de la position que je lui donne et le place en 2054+. Rien à faire, j'ai épuisé mes forces, je ne sais plus quoi faire. Quelqu'un a-t-il déjà eu ce problème ?
 
Solution
Merci à vous tous qui vous êtes penchés sur cette question.
Je te retournerai un peu la question : ton code crée-t-il me même problème chez quelqu'un d'autre ?
Oui !
Je crois avoir trouvé l'explication (à confirmer par un code encore à corriger).
Pour certaines raisons (je pourrais expliquer si demande), j'ai fait, dans ce cas précis (ce n'est pas une généralité), un SetParent du UserForm à l'ActiveWindow.

Conséquences:
  1. Lors d'un set du .Left, il faut le faire strictement par rapport à la Window (de 0 à .Width) même si le Left de la Window sur un moniteur #2 n'est pas 0 mais 1440 par exemple. Si on utilise le vrai .Left par rapport au moniteur, il va l'ajouter à ce qu'on a demandé au setting.
  2. Par contre...
Bonjour,
Il y a quand même un problème.
Je n'arrive pas à déterminer si un SetParent de l'ActiveWindow a été fait ou pas sur le UserForm.
Que je fasse ou non le SetParent, le GetParent du UserForm est toujours ActiveWindow.hWnd !!
 

Pièces jointes

re
et oui c'est ca le problème généré par la gestion MDI moderne
Beaucoup de vieilles apis renvoient une donnée erronée

Quand comprendras-tu que l'application excel n'est pas une fenêtre win32 comme les autres. 😉
et que Les UserForms peuvent être owned (attaché en tant qu'enfant) par Excel, mais ne sont jamais de vrais child windows Win32.

en gros donc on pourrait dire qu'il faut chercher a quelle fenêtre l'userform a été attaché et non la fenêtre parent

Allez kado
VB:
Private Declare PtrSafe Function GetWindow Lib "user32" ( _
                    ByVal hwnd As LongPtr, ByVal uCmd As Long) As LongPtr

Private Const GW_OWNER As Long = 4

Function GetRealParent(hWndForm As LongPtr)
'patricktoulon collection api 2023
    Dim hwndParent As LongPtr
    GetRealParent = GetWindow(hWndForm, GW_OWNER)
End Function
Patrick
 
Dernière édition:
OK mais ça donne toujours le Handle de l'Application dans les 2 cas, ça ne discrimine pas !
Le GetWindow avec GW_OWNER je pense que c'est exactement la même chose qu'un GetParent.

Par contre, chez moi (2021 / 64), il y a possibilité de discriminer avec GW_HWNDPREV ou GW_HWNDFIRST.

Avec GW_HWNDFIRST:
-> SetParent: on récupère le Handle du UserForm
-> Natif: on récupère un autre Handle

Avec GW_HWNDPREV:
-> SetParent: on récupère 0
-> Natif: on récupère un autre Handle

Donc l'idée était bonne et ça me sauve la vie.
 

Pièces jointes

Dernière édition:
sinon tu pourrais essayer getAncestor
VB:
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function GetParent Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
    Private Declare PtrSafe Function SetParent Lib "user32" (ByVal hWndChild As LongPtr, ByVal hWndNewParent As LongPtr) As LongPtr
    Private Declare PtrSafe Function GetAncestor Lib "user32" (ByVal hwnd As LongPtr, ByVal gaFlags As Long) As LongPtr
    
#Else
    Private Declare Function SetParent Lib "user32" (ByVal hWndChild As Long, ByVal hWndNewParent As Long) As Long
    Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
    Private Declare Function GetAncestor Lib "user32" (ByVal hwnd As Long, ByVal gaFlags As Long) As Long
#End If

Private Const GA_ROOTOWNER As Long = 3
Private Sub UserForm_Activate()
    Dim UserFormHandle As LongPtr
    Dim BN As Integer
    Dim x
    UserFormHandle = GetUserFormHandleByCaption(Me)
    
    BN = MsgBox("SetParent de l'ActiveWindow pour le UserForm ?", vbYesNo)
    
    If BN = vbYes Then
        Call SetParent(UserFormHandle, ActiveWindow.hwnd)
    End If
    
    MsgBox "UserForm = " & UserFormHandle & vbCrLf & _
            "ActiveWindow = " & ActiveWindow.hwnd & vbCrLf & _
            "UserForm Parent = " & GetParent(UserFormHandle) & vbCrLf & _
            "GetRealParent = " & GetAncestor(UserFormHandle, GA_ROOTOWNER) & vbCrLf & _
            x
    Application.Wait Now + TimeSerial(0, 0, 1)
    Unload Me
End Sub
 
re
je viens d'avoir confirmation
getparent getancestor et surtout getwindow n'expose pas de methode de retroingenérie pour les userforms parcontre il expose une methode descendante valide je pourait adapter ma fonction perso du projet Taskpane 4.0 pour confirmer à 100% l'affiliation
autrement dit j'arrive a avoir le handle parent le comparer au handle application
non seulement j'ai le handle parent mais j'ai la possibilité de confirmer l'affiliation avec excel
je bricole ma fonction si tu veux
 
regarde si on garde que la partie handle de la fonction on laisse tomber toute la liste et tout le toutim on garde que la recursivité
si le userform est attaché il va apparaitre dans la la liste des descendantes directes ou indirectes de xlmain
et quand on regarde la colonne i c'est bien le handle de la fenêtre excel
par contre si il n'est pas attaché il n'apparait tout simplement pas dans la liste
 

Pièces jointes

voila si on enleve tout ce qui t'es inutile on transforme un peu
renvoie le handle du parent si attaché a l'application excel sinon 0
explore les filles et soeurs
VB:
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function GetWindow Lib "user32" _
                              (ByVal hwnd As LongPtr, ByVal uCmd As Long) As LongPtr
    Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" _
                              (ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare PtrSafe Function GetParent Lib "user32" _
                              (ByVal hwnd As LongPtr) As LongPtr
#Else
    Private Declare Function GetWindow Lib "user32" _
                              (ByVal hwnd As Long, ByVal uCmd As Long) As Long
    Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
                              (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare Function GetParent Lib "user32" _
                              (ByVal hwnd As Long) As Long
#End If

Private Const GW_CHILD As Long = 5
Private Const GW_HWNDNEXT As Long = 2


Public Function FindThunderDFrameParent(Optional ByVal hWndStart As LongPtr = 0) As LongPtr
    Dim hChild As LongPtr, cls As String * 64, l As Long
    If hWndStart = 0 Then hWndStart = Application.hwnd
    l = GetClassName(hWndStart, cls, Len(cls))
    If left$(cls, l) = "ThunderDFrame" Then
        FindThunderDFrameParent = GetParent(hWndStart)
        Exit Function
    End If
    hChild = GetWindow(hWndStart, GW_CHILD)
    Do While hChild <> 0
        FindThunderDFrameParent = FindThunderDFrameParent(hChild)
        If FindThunderDFrameParent <> 0 Then Exit Function
        hChild = GetWindow(hChild, GW_HWNDNEXT)
    Loop
End Function
 
plus sure encore on teste de handle du userform en plus de la classe au cas ou tu aurais 36 userform affilié a excel
VB:
Option Explicit

#If VBA7 Then
    Private Declare PtrSafe Function GetWindow Lib "user32" _
                              (ByVal hwnd As LongPtr, ByVal uCmd As Long) As LongPtr
    Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" _
                              (ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare PtrSafe Function GetParent Lib "user32" _
                              (ByVal hwnd As LongPtr) As LongPtr
#Else
    Private Declare Function GetWindow Lib "user32" _
                              (ByVal hwnd As Long, ByVal uCmd As Long) As Long
    Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
                              (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    Private Declare Function GetParent Lib "user32" _
                              (ByVal hwnd As Long) As Long
#End If

Private Const GW_CHILD As Long = 5
Private Const GW_HWNDNEXT As Long = 2


Public Function FindThunderDFrameParent(hWndStart As LongPtr, hwndForm As LongPtr) As LongPtr
    Dim hChild As LongPtr, cls As String * 64, l As Long
    If hWndStart = 0 Then hWndStart = Application.hwnd
    l = GetClassName(hWndStart, cls, Len(cls))
    If left$(cls, l) = "ThunderDFrame" And hWndStart = hwndForm Then
        FindThunderDFrameParent = GetParent(hWndStart)
        Exit Function
    End If
    hChild = GetWindow(hWndStart, GW_CHILD)
    Do While hChild <> 0
        FindThunderDFrameParent = FindThunderDFrameParent(hChild, hwndForm)
        If FindThunderDFrameParent <> 0 Then Exit Function
        hChild = GetWindow(hChild, GW_HWNDNEXT)
    Loop
End Function
test en bloquant la ligne setparent et en la debloquant
en fait la validation passe par la presence de la classe thunderDframe et handle du userform en descendant l'arborescence des filles et soeurs
voila puisque l'on peut pas remonter on descend LOL
 

Pièces jointes

- Navigue sans publicité
- Accède à Cléa, notre assistante IA experte Excel... et pas que...
- Profite de fonctionnalités exclusives
Ton soutien permet à Excel Downloads de rester 100% gratuit et de continuer à rassembler les passionnés d'Excel.
Je deviens Supporter XLD

Discussions similaires

Les cookies sont requis pour utiliser ce site. Vous devez les accepter pour continuer à utiliser le site. En savoir plus…