XL 2016 Quelle API pour savoir si une ListBox (UserForm ou ActiveX) a sa ScrollBar Verticale présente ?

Dudu2

XLDnaute Barbatruc
Bonjour les XLDNautes,

Si on peut, sans API, savoir si une ComboBox a son ascenseur affiché grâce à la différence (ComboBox.ListCount - ComboBox.ListRows) ce n'est pas possible avec les ListBoxes.
Certes on connaît son ListBox.TopIndex mais il n'existe pas de ListBox.BottomIndex.
Un approximation est possible avec la ListBox.Font.Size mais ça reste imprécis.

Reste l'API qui pourrait indiquer la présence d'une Vertical ScrollBar mais mes essais sont restés infructueux.
Merci pour toute suggestion.
 
Solution
pour ce qui est de cette discussion j'ai revue la chose pour les ListBox dans userform et feuille

patricktoulon

XLDnaute Barbatruc
Voilà le fichier débarrassé des autres méthodes avec seulement la méthode WindowFromPoint qui est la seule qui soit généraliste et relativement simple. J'ai gardé ma méthode "profondeur" pour les ComboBoxes UserForm mais tu peux te faire plaisir et la remplacer par la tienne. Pareil pour le Pane de l'Objet.
ok je vais regarder
mais je t'avoue des fois c'est difficile d'adapter a ta facon de coder
je travaille pre et ou post action
tu fait différemment toi

edit:
bon j'ai remplacé ta fonction objectpane par un dérivé la mienne
il s'avere que pour les listbox tu n'y fait pas appel et les combo oui c'est normal ?
 

Dudu2

XLDnaute Barbatruc
il s'avere que pour les listbox tu n'y fait pas appel et les combo oui c'est normal
Oui c'est normal car les ListBoxes on a le résultat immédiatement.
Pour la ComboBox ActiveX de feuille, on doit vérifier si le curseur est en-dessous de la ComboBox elle-même d'où la nécessité d'en trouver la position Pan.PointsToPixelsY et donc d'en connaître le Pane.

Après il existe peut-être une autre méthode avec les RECT des fenêtres mais je n'ai pas trouvé.
 

patricktoulon

XLDnaute Barbatruc
Je sais pas trop la définition.
Quand c'est la feuille qui est Parent j'ai considéré Control ActiveX pour les différencier des Controls de Formulaire. Et Control de UserForm quand c'est le UserForm qui est Parent.
Il est possible que les 2 soient ActiveX, je ne me suis jamais posé la question.
a ben grosse erreur justement
il y a les controls formulaire et les activx c'est tout

les activx sont les controls possédants des events implémenté

les controls formulaires eux n'en ont pas on leur affecte une macro

pour les userform et feuilles les activX sont les mêmes pour certains paramétrés différemment mais ca reste les mêmes
 

patricktoulon

XLDnaute Barbatruc
bon voilaje t'ai ecris une fonction objectpane dérivée de la mienne
c'est simple elle te dit quelle panes est sous le curseur
y a rien de plus a faire
c’a doit être déclenché au move des controls ou a une sélection de range ou ce que tu veux
elle remplace toute celle que j'ai fait dans les année précédentes
bon il y a une api utilisée la getcursorpos qui est absolument nécessaire pour les controls
car les control a l'inverse des range n'ont pas de panes parent .visiblerange

et le topleftcell donne juste la cellule et en cas de fractionnés ou cette même cellule est apparente dans plusieurs panes c'est absolument impossible de discerner le pane.index
avec ça il n'y a plus de question a se poser


quelle panes est sous le curseur c'est tout
VB:
Function ObjectPane(obj As Object)
Dim psX As POINTAPI, panIndex&
    GetCursorPos psX
    panIndex = 1
       With ActiveWindow
        If .Panes.Count >= 2 Then If psX.x > .Panes(2).PointsToScreenPixelsX(.Panes(2).VisibleRange.Left) Then panIndex = 2
        If .Panes.Count >= 2 Then If psX.y < .Panes(2).PointsToScreenPixelsY(.Panes(2).VisibleRange.Top) Then panIndex = 1
        If .Panes.Count = 4 Then If psX.y > .Panes(3).PointsToScreenPixelsY(.Panes(3).VisibleRange.Top) Then panIndex = panIndex + 2
       Set ObjectPane = .Panes(panIndex)
      [B1] = "Panes(" & panIndex & ")"
         End With
End Function
 

patricktoulon

XLDnaute Barbatruc
Ok, alors tu m'apprends quelque chose ! :cool:
1668073101492.png


on les utilise légèrement différemment c'est tout
dans le userform par la collection msforms.control
dans une feuille par la collection OLEObjects suffixé par ".object" ou as object tout court selon le besoins

exemple dans un userform
dim monbouton as msforms.commandbutton (ou msforms.control)
set monbouton=commandbutton1

le même dans une feuille
dim monbouton as oleobject
set monbouton=activesheet.OLEObjects("CommandButton1")' on atteint ses propertie de niveau oleobject
--------------------------
dim monbouton as object
set monbouton=activesheet.OLEObjects("CommandButton1").object' on atteint ses propertie de niveau object


on peut l'atteindre par la collection shapes aussi
mais ça reste le même
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
après sincèrement tout dépend du principe que tu veux utiliser
si tu utilise windowfrompoint tu n'a plus besoins de objectpane

c'est un peu ca que je comprend pas dans tes exercice
je pige pas très bien l'utilisation d'une méthode ou une autre

la chose est simple
1°avec objectpane et pointtoscreenpixels
tu peux très bien déterminer ton rectangle pas besoins de windowfrompoint

2°à l'inverse avec windowfrompoint là non on a pas besoins de tout ca

comme on travaille sur feuille et userform en effet la methode windowfrompoint semble etre la plus simple

pour ma part j'ai pris parti d'utiliser la methode pane et pointtoscreenpixels et(extrapolé pour userform)
panes et pointtoscreenpixels c'est universel a tout les version d'excel ;)

je peux essayer de te faire un model simple pour que tu comprenne mon point de vue un peu a ta façon avec plusieurs fonction séparée
 
Dernière édition:

Dudu2

XLDnaute Barbatruc
En l'occurrence tu devrais plutôt appeler ta fonction CursorPane() et pas ObjectPane().

Il est vrai que partant du WindowFromPoint(), c'est à dire à partir de la position curseur, une fonction CursorPane() est tout à fait suffisante sans qu'il soit besoin de connaître le Pane du Control lui-même.

D'ailleurs, une autre version d'une fonction CursorPane() pourrait être...
VB:
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long

Private Type POINTAPI
    x As Long
    y As Long
End Type

Function CursorPane() As Pane
    Dim Hold As POINTAPI
    Dim Pan As Pane
    Dim i As Integer
    
    GetCursorPos Hold
    
    With ActiveWindow
        For i = 1 To .Panes.Count
            With .Panes(i)
                If Hold.x >= .PointsToScreenPixelsX(.VisibleRange.Left) _
                And Hold.x < .PointsToScreenPixelsX(.VisibleRange.Left + .VisibleRange.Width) _
                And Hold.y >= .PointsToScreenPixelsY(.VisibleRange.Top) _
                And Hold.y < .PointsToScreenPixelsY(.VisibleRange.Top + .VisibleRange.Height) Then Exit For
            End With
        Next i
        
        Set CursorPane = .Panes(i)
    End With
End Function

Edit: code modifié 12h35
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
@Dudu2 alors

oui après tu lui donne le nom que tu veux à cette fonction


je suis parti de ton fichier #post58

je l'ai regardé en profondeur

je constate que le msgbox se déclenche uniquement dans le child de la combo ou les listbox
parti de la



le handle de la child combo est parfaitement bien donnée quand on passe dessus

hors tu dis dans ton code

VB:
'---------------------------------
'GetControlHandleByWindowFromPoint
'Ne fonctionne pas pour les Controls de UserForms
'---------------------------------

ben en fait tu n'a pas besoins de capter le handle de la combo puisque ta démo msgbox repose sur les listbox et child de combo

ensuite je vois deepness et objectpane et autres

question: a quoi ca te sert dans cette demo?

voila ce que j'ai fait de ta fonction GetControlHandleByWindowFromPoint
j'ai ajouté juste l'api getclassname
j'ai supprimer tout ça sans distinction histoire que j'y vois plus clair et je n'est gardé que le debut de ta fonction
là voila
VB:
Option Explicit

#If Win64 Then
    Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal point As LongLong) As LongPtr
    Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
#Else
    Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, ByVal yPoint As Long) As LongPtr
#End If

Private Declare PtrSafe Function GetWindowRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As Long
Private Declare PtrSafe Function GetClientRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As Long
Private Declare PtrSafe Function GetParent Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As LongPtr

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type POINTAPI
    x As Long
    y As Long
End Type

#If Win64 Then
Function PointToLongLong(point As POINTAPI) As LongLong
    Dim ll As LongLong
    Dim cbLongLong As LongPtr

    cbLongLong = LenB(ll)

    ' make sure the contents will fit
    If LenB(point) = cbLongLong Then
        CopyMemory ll, point, cbLongLong
    End If

    PointToLongLong = ll
End Function
#End If

'---------------------------------
'GetControlHandleByWindowFromPoint
'Ne fonctionne pas pour les Controls de UserForms
'première nouvelle :):)
'---------------------------------
Function GetControlHandleByWindowFromPoint(Ctl As Object) As LongPtr
    Dim Hold As POINTAPI, clss$, hwnd&, hwndP&
    'Position curseur
    GetCursorPos Hold
    #If Win64 Then
        hwnd = WindowFromPoint(PointToLongLong(Hold))
    #Else
        hwnd = WindowFromPoint(Hold.x, Hold.y)
    #End If
    'ListBox
    Select Case True
    Case TypeOf Ctl Is MSForms.ListBox
        GetControlHandleByWindowFromPoint = hwnd
        Exit Function

    Case TypeName(Ctl) = "ComboBox"
        clss = Space$(255)
        hwndP = GetParent(hwnd)
        GetClassName hwndP, clss, 255
        If InStr(1, clss, "F3 MdcPopup") = 0 Then hwnd = 0    ' si c'est un popu alors
    End Select

    GetControlHandleByWindowFromPoint = hwnd
End Function
plus de profondeur plus de rien du tout
et je t'assure il n'y a plus rien d'autre dans le module
et j'ai exactement le même résultat
 

Pièces jointes

  • GetControlHandleWithWindowFromPöint V pat .xlsm
    52.6 KB · Affichages: 0

patricktoulon

XLDnaute Barbatruc
Je suis allé un peu vite en besogne car RangeFromPoint retourne une Shape si elle est sous le curseur, et là ça ne marche plus 😭
je le redis :
c'est pour ca que j'ai fait la fonction par getcursorpos
comme ca c'est universel object ou range c'est kif kif
on a plus a se casser la tête

mais je me répète
avec windowfrompoint et Getwindowrect tu n'a pas besoins de tout ca
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
après finalement là ou je te donne raison

(je sais cest pas le sujet de cette discussion mais tout de même)

plutôt que d'aller chercher le hook direct au move avec (si ceci ou cela par ce que)

on pourrait se contenter de le déclencher simplement quand c'est possible

a savoir
1° quand le windowfrompoint donne le handle listbox (ou frame chez moi)
2°ou quand le handle de la child combo est confirmé

ça éviterait pas mal de tours pour rien et gérer les erreurs et générer des latences a la lowlevelmouseproc ;)
 

Statistiques des forums

Discussions
314 487
Messages
2 110 119
Membres
110 676
dernier inscrit
Hoolaurent