Lorsqu'on lance un fichier Excel sur un poste avec plusieurs écrans, à chaque fois les userform s'affichent sur un autre écran que l'écran ou est affiché excel.
y a-t-il une parade pour que cet userform s'affiche dans la même fenêtre que excel ?
je joint le même fichier solutionné dernièrement pour le test.
Tu as sûrement raison. Et tu es satisfait de ce que tu as produit.
Et moi je suis satisfait de ma fonction centralisée de positionnement PositionUserFormSurObjetFeuille() qui fait référence à l'API pour des choses nécessaires, y compris pour les conversions de Pixels en Points même si tu préfères faire autrement. Je suis aussi satisfait de la manière dont je détermine dans une fonction dédiée le Pane de l'Objet qui n'est pas dépendant de leurs VisibleRanges. Et aussi de la marge entre le RECT de la Window et le RECT du DWM.
Bref, on est tous les deux satisfaits. C'est pas beau la vie ?
oui de toute façon ca fonctionne
et en voulant te montrer la simplicité je me suis apercu d'un inconvénient a utiliser une methode (api ou autre ) pour point to pixel
dans l'exemple que je viens de te donner j'ai mis en dur 1.333333333333333
ya pas de soucis c'est ça de toute façon je suis bien en dpi 100%
sauf!! que quand j'applique un zoom excel ben le coeff 1.33333... n'est plus valable
alors qu'avec ma méthode pointtoscreenpixels 72 ben zoom ou pas zoom on reste bon
et c'est logique a vrai dire
fait le test tu verra
Un ratio Pixel To Point ou l'inverse est invariant. Chez moi 0.6 et 1.66. L'API me le donne.
C'est lorsqu'on fait référence à une objet sensible au zoom qu'il faut appliquer le Zoom sur le calcul de sa position. Tu crois l'appliquer au ratio Pixel to Point mais tu te trompes à cause de ta méthode de calcul du ratio à laquelle je préfère l'API à cause de ça et des décimales pas toujours stables avec la variation du Zoom.
D'autant que les Pane.PointsToScreenPixelsX et Y tiennent compte du Zoom. Pas besoin de corriger !
Et lorsque je décale le UserForm je n'applique nullement le Zoom à la conversion Pixel To Point car le UserForm n'est pas sensible au Zoom.
Dans mon code je n'ai AUCUNE référence au zoom et pourtant la position du UserForm s'adapte parfaitement au zoom grâce au Pane.PointsToScreenPixelsX et Y.
Là franchement @patricktoulon, je sais pas quoi te dire.
Ma fonction de détermination du Pane permet de déterminer sur option:
- Le Pane que l'objet y soit visible ou non
- Le Pane uniquement si l'objet y est visible (méthode qui rejoint +/- la tienne)
Si ce code ne te convient pas je ne vois pas quoi d'autre te proposer ni dans la forme ni dans la fonctionnalité.
VB:
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Private Function ObjectPane(Obj As Object, Optional Visible As Boolean = True) As Pane
Dim Rng As Range
Dim i As Integer
Dim Pan As Pane
Dim PosRow As Integer
Dim PosColumn As Integer
'Le Parent de l'objet n'est pas la feuille
If Not TypeOf Obj.Parent Is Worksheet Then Exit Function
'Selon le type d'objet
Select Case True
Case TypeOf Obj Is Range
Set Rng = Obj
Case Else
Set Rng = Obj.TopLeftCell
End Select
With ActiveWindow
'--------------------------------
'L'objet peut être visible ou non
'--------------------------------
If Not Visible Then
If .SplitRow = 0 Then
PosRow = 1
Else
If Rng.Row <= .SplitRow Then PosRow = 2 Else PosRow = 3
End If
If .SplitColumn = 0 Then
PosColumn = 1
Else
If Rng.Column <= .SplitColumn Then PosColumn = 4 Else PosColumn = 5
End If
Select Case PosRow * PosColumn
Case 1, 2, 4, 8
Set Pan = .Panes(1)
Case 3, 5, 10
Set Pan = .Panes(2)
Case 12
Set Pan = .Panes(3)
Case 15
Set Pan = .Panes(4)
End Select
'-------------------------
'L'objet doit être visible
'-------------------------
Else
'Recherche du Pane de l'objet si visible
For i = 1 To .Panes.Count
If Not Intersect(Rng, .Panes(i).VisibleRange) Is Nothing Then Exit For
Next i
If i <= .Panes.Count Then Set Pan = .Panes(i)
End If
End With
'Return Value
Set ObjectPane = Pan
End Function
Edit: D'ailleurs cette fonction n'est pas liée à un objet nécessairement sélectionné. Mais à mon avis qu'il soit sélectionné ou pas tu n'y coupes pas de passer par un code équivalent car la sélection n'aide en rien à déterminer son Pane. Car .Pane n'est pas une propriété de Selection.
Et tu n'as pas 36 options:
- Soit tu t'intéresses au VisibleRange des Panes (auquel cas ça limite ta recherche à la partie visible)
- Soit tu t'intéresses au point de croisement des volets (les fameux .SplitRow et .SplitColumn que tu dédaignes)
Maintenant tu as peut-être une méthode miracle à laquelle je n'ai pas pensé.
Je me méfie quand tu me poses ce genre de question pour m'amener à exposer des options que tu te ferais un plaisir de dézinguer !
Une autre méthode plus "lisible" mais moins "élaborée" que la première à mon avis.
VB:
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Function ObjectPane2(Obj As Object, Optional Visible As Boolean = True) As Pane
Dim Rng As Range
Dim i As Integer
Dim Pan As Pane
Dim FreezeCell As Range
'Le Parent de l'objet n'est pas la feuille
If Not TypeOf Obj.Parent Is Worksheet Then Exit Function
'Selon le type d'objet
Select Case True
Case TypeOf Obj Is Range
Set Rng = Obj
Case Else
Set Rng = Obj.TopLeftCell
End Select
With ActiveWindow
'--------------------------------
'L'objet peut être visible ou non
'--------------------------------
If Not Visible Then
'https://www.mrexcel.com/board/threads/vba-how-do-i-figure-out-what-cell-at-which-panes-are-frozen-with-freezepanes.1129717/
Set FreezeCell = ActiveSheet.Cells(.Panes(1).ScrollRow + .SplitRow, .Panes(1).ScrollColumn + .SplitColumn)
'2 Panes
If .Panes.Count = 2 Then
'Split sur une ligne
If FreezeCell.Column = 1 Then
If Rng.Row < FreezeCell.Row Then
Set Pan = .Panes(1)
Else
Set Pan = .Panes(2)
End If
'Split sur une colonne
Else
If Rng.Column < FreezeCell.Column Then
Set Pan = .Panes(1)
Else
Set Pan = .Panes(2)
End If
End If
'4 panes
Else
'Dans les 2 premiers Panes
If Rng.Row < FreezeCell.Row Then
If Rng.Column < FreezeCell.Column Then
Set Pan = .Panes(1)
Else
Set Pan = .Panes(2)
End If
'Dans les 2 derniers Panes
Else
If Rng.Column < FreezeCell.Column Then
Set Pan = .Panes(3)
Else
Set Pan = .Panes(4)
End If
End If
End If
'-------------------------
'L'objet doit être visible
'-------------------------
Else
'Recherche du Pane de l'objet si visible
For i = 1 To .Panes.Count
If Not Intersect(Rng, .Panes(i).VisibleRange) Is Nothing Then Exit For
Next i
If i <= .Panes.Count Then Set Pan = .Panes(i)
End If
End With
'Return Value
Set ObjectPane2 = Pan
End Function
On dirait qu'il te faut tout au plus court du code source. Faire des fonctions séparées ça permet de pas mélanger les choses et de pouvoir les ré-utiliser dans des contextes différents et de les partager.
Au passage, pour le Pixel To Point sans API, tu ferais bien d'en faire des fonctions séparées qui te rendent toujours l'équivalent de l'API. Car à tout concentrer dans des instructions à rallonge (Zoom dans le calcul des Pixel To Point + Zoom ou pas Zoom dans le calcul de la position) tu ne sais plus ce que tu fais.
Tu peux faire du code in-line pour la détermination du Pane, mélangé avec tes calculs de positions, de conversions Pixel To Point et de décalages. En concentrant l'écriture au maximum pour faire croire que tu fais court, mais c'est pas comme ça qu'il faut faire. C'est une vieille histoire.
Le deal c'est pas de faire un code compressé comme un Zip avec des lignes multi-instructions et des calculs qui finissent par être incompréhensibles à la relecture 3 jours plus tard. Ça n'accélère pas l'exécution du code et ça rend la maintenance et le partage difficiles.
Le deal c'est pas de faire un code compressé comme un Zip avec des lignes multi-instructions et des calculs qui finissent par être incompréhensibles à la relecture 3 jours plus tard. Ça n'accélère pas l'exécution du code et ça rend la maintenance et le partage difficiles.
ben sans vouloir offenser d'aucune manière tu devrais appliquer cette doctrine a toi même
le pire je crois c'est que tu est convaincu de ce que tu dis
honnêtement quand je regarde ton code je ne sais même pas par ou commencer pour en comprendre la mécanique et c'est pas peine de faire des efforts crois moi
je connais très bien la fonction pointstoscreenpixels( et ses mystères qui n'en sont pas) et je l'ai suffisamment défendu dans un débat (qui date un peu) sur DVP alors que tout le monde était contre moi
je reviens sur ma question
est tu capable de m'expliquer en quoi et comment ta fonction détermine le pane de l'oleobject ou shapes cliqué
encore une fois et honnêtement ,je teste par ce que je veux pas dire sans preuve mais a la simple lecture du code je savais déjà que ça ne marcherait pas
ou a lors j'ai raté un épisode
je crois que quand tu code tu ne dois pas faire tout les tests
VB:
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Private Function ObjectPane(Obj As Object, Optional Visible As Boolean = True) As Pane
Dim Rng As Range
Dim i As Integer
Dim Pan As Pane
Dim PosRow As Integer
Dim PosColumn As Integer
'Le Parent de l'objet n'est pas la feuille
If Not TypeOf Obj.Parent Is Worksheet Then Exit Function
'Selon le type d'objet
Select Case True
Case TypeOf Obj Is Range
Set Rng = Obj
Case Else
Set Rng = Obj.TopLeftCell
End Select
With ActiveWindow
'--------------------------------
'L'objet peut être visible ou non
'--------------------------------
If Not Visible Then
If .SplitRow = 0 Then
PosRow = 1
Else
If Rng.Row <= .SplitRow Then PosRow = 2 Else PosRow = 3
End If
If .SplitColumn = 0 Then
PosColumn = 1
Else
If Rng.Column <= .SplitColumn Then PosColumn = 4 Else PosColumn = 5
End If
Select Case PosRow * PosColumn
Case 1, 2, 4, 8
Set Pan = .Panes(1)
Case 3, 5, 10
Set Pan = .Panes(2)
Case 12
Set Pan = .Panes(3)
Case 15
Set Pan = .Panes(4)
End Select
'-------------------------
'L'objet doit être visible
'-------------------------
Else
'Recherche du Pane de l'objet si visible
For i = 1 To .Panes.Count
If Not Intersect(Rng, .Panes(i).VisibleRange) Is Nothing Then Exit For
Next i
If i <= .Panes.Count Then Set Pan = .Panes(i)
End If
End With
'Return Value
Set ObjectPane = Pan
End Function
je constate aussi que tu disais hier ne pas avoir a utiliser visible range t tout le toin toin
et je lis dans ton code
VB:
For i = 1 To .Panes.Count
If Not Intersect(Rng, .Panes(i).VisibleRange) Is Nothing Then Exit For
Next i
de plus comme je suis quelqu'un qui aime être sur je teste ta fonction
avec un seul shape dans une feuille fractionnée (voir démo visuelle après )
allez c'est parti un test simple
VB:
Sub Rectangle1_Cliquer()'macro affectée à la shape
MsgBox ObjectPane(ActiveSheet.Shapes(1), True).Index 'true ou false ca change rien c'est toujours la pane 1 qui est trouvée
End Sub
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Function ObjectPane(Obj As Object, Optional Visible As Boolean = True) As Pane
Dim Rng As Range
Dim i As Integer
Dim Pan As Pane
Dim PosRow As Integer
Dim PosColumn As Integer
'Le Parent de l'objet n'est pas la feuille
If Not TypeOf Obj.Parent Is Worksheet Then Exit Function
'Selon le type d'objet
Select Case True
Case TypeOf Obj Is Range
Set Rng = Obj
Case Else
Set Rng = Obj.TopLeftCell
End Select
With ActiveWindow
'--------------------------------
'L'objet peut être visible ou non
'--------------------------------
If Not Visible Then
If .SplitRow = 0 Then
PosRow = 1
Else
If Rng.Row <= .SplitRow Then PosRow = 2 Else PosRow = 3
End If
If .SplitColumn = 0 Then
PosColumn = 1
Else
If Rng.Column <= .SplitColumn Then PosColumn = 4 Else PosColumn = 5
End If
Select Case PosRow * PosColumn
Case 1, 2, 4, 8
Set Pan = .Panes(1)
Case 3, 5, 10
Set Pan = .Panes(2)
Case 12
Set Pan = .Panes(3)
Case 15
Set Pan = .Panes(4)
End Select
'-------------------------
'L'objet doit être visible
'-------------------------
Else
'Recherche du Pane de l'objet si visible
For i = 1 To .Panes.Count
If Not Intersect(Rng, .Panes(i).VisibleRange) Is Nothing Then Exit For
Next i
If i <= .Panes.Count Then Set Pan = .Panes(i)
End If
End With
'Return Value
Set ObjectPane = Pan
End Function
ben je suis désolé mais tu a tout faux
ta fonction me renvoie toujours la pane 1
démonstration
et pour qu'il n'y ai pas d’ambiguïté je donne le fichier test avec ta fonction tel que je l'ai testé chez moi 2013 et au boulot 2016
La fonction ne donne pas le Pane de l'objet cliqué mais le Pane de l'objet désigné.
Si tu veux qu'elle donne le Pane de l'objet cliqué tu l'appelles comme ça: Set Pan = ObjectPane(Selection)
Ou encore si tu sélectionnes un objet et que tu scrolles pour qu'il disparaisse de l'écran et que tu veux quand même son Pane tu l'appelles comme ça: Set Pan = ObjectPane(Selection, Visible:=False)
Edit: Le paramètre Visible est commenté, c'est simple:
VB:
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Function ObjectPane(Obj As Object, Optional Visible As Boolean = True) As Pane
De toutes façon je ne t'impose aucunement l'usage de cette fonction en particulier.
Si tu préfères mettre le code in-line selon que tu cherches un objet visible ou pas, tu peux reprendre des bouts de code de la fonction ou inventer ton propre code, personnellement ça m'est égal et je ne te ferai ici aucun commentaire sur ta façon de faire.
re Set Pan = ObjectPane(Selection)
je sais que ça marchera pas mais bon peut être ai je raté un episide
ok je vais tester
et je reviens
après je te donnerais ma méthode si tu veux
re
bon ben j'ai testé pour ne pas mourir idiot
comme tu peux le constater ca ne fonctionne pas
et il suffit meme que j'ai sélectionné une cellule dans une des 4 pannes avant pour changer le resultat
Si le clic le sélectionne, une Shape ou une image par exemple ça fonctionne très bien.
Si le clic ne le sélectionne pas, un bouton par exemple, c'est à toi de trouver l'objet concerné.
C'est quand même pas à la fonction de faire ce boulot !
La fonction ne donne pas le Pane de l'objet cliqué mais le Pane de l'objet désigné.
Si tu veux qu'elle donne le Pane de l'objet cliqué tu l'appelles comme ça: Set Pan = ObjectPane(Selection)
Ou encore si tu sélectionnes un objet et que tu scrolles pour qu'il disparaisse de l'écran et que tu veux quand même son Pane tu l'appelles comme ça: Set Pan = ObjectPane(Selection, Visible:=False)
Edit: Le paramètre Visible est commenté, c'est simple:
VB:
'----------------------------------------------------
'Pane de l'objet dont le parent est la feuille active
'- Visible = True, l'objet doit être visible
'- Visible = False, l'objet peut être visible ou non
'----------------------------------------------------
Function ObjectPane(Obj As Object, Optional Visible As Boolean = True) As Pane
allons dudu2 c'est justement ça le problème que visiblement tu ne comprends pas malgré mes démos animées plus que parlantes
une shape ou un activx ou ce que tu veux n'a pas de panne !!! ou tout du moins elle peut se retrouver sur plusieurs (comme dans mes démos animées
et je ne détaille pas le reste (a propos des visible ou pas) car tout est faux dans ton raisonnement
c'est incroyable que tu ne comprenne pas cela
regarde une shapes peut être dans toutes les panes
si tu comprends pas là je sais plus moi
parti de là comment veux tu avec ta fonction déterminer sur quelle copie de la shape dudu2 dans chaque pane a tu cliqué
coquin de sort fait le test toi même avec ton module