Partage d'expérience avec ruban personnalisé (Objet IRibbonUI)
Bonjour à tous, j'essaye d'ouvrir 2 fichiers en même temps qui contiennent tout deux un ruban personnalisé mais bien que chaque élément de chaque ruban aient des ID différentes, cela semble générer des bizarreries sur le fonctionnement du ruban restant dès que je ferme l'un des deux fichiers.
Afin d'orienter mes recherches pour générer potentiellement un fichier de démo à déposer ici, commençons déjà par partager nos expériences.
Avez vous déjà effectué cela et avez vous rencontré des soucis?
Par avance, merci de vos retours
Bonne journée à tous
Bonjour,
Lorsque vous fermez un des 2 fichiers, est-ce que vos codes prévoient de supprimer le ruban lié au fichier que vous fermez ?
De même lorsque vous passez d'un fichier à l'autre.
La gestion des menus perso nécessite l'ajout de codes (activate, deactivate...) pour que des menus ne soient montrés que dans le classeur actif
Cordialement
Bonjour à tous,
cela semble générer des bizarreries sur le fonctionnement du ruban restant dès que je ferme l'un des deux fichiers.
Pouvez-vous nous en dire un peu plus ?
Vos rubans sont-ils gérés depuis un fichier xml et CallBacks ?
les fichiers contiennent bien des xml et callbacks @Jean-Paul et la gestion activate / déactivate est en place @Dan mais uniquement au démarrage
Dois-je comprendre que lorsque le fichier appelant reprend le focus (quand il ouvre le second, ce second prend directement le focus) je dois relancer les procédures d'activation (un invalidate par exemple)?
@Jean-paul les bizarreries viennent du fait que la fonction activatetab devient inopérante et parfois les invalidates
merci d'avance de vos retours
Re
Dois-je comprendre que lorsque le fichier appelant reprend le focus (quand il ouvre le second, ce second prend directement le focus) je dois relancer les procédures d'activation (un invalidate par exemple)?
Ce que je pensais c'est que si vous activez le fichier 2, il faut enlever le ruban lié au fichier 1
genre de code --> Private Sub Workbook_WindowActivate(ByVal Wn As Window) et Private Sub Workbook_WindowDeactivate(ByVal Wn As Window)
Dans une appli il y a quelques années j'avais fait utilisé ce genre de code pour les changements de fenêtre
A voir en fonction de ce que vous avez réalisé dans vos fichiers.
Crdlt
Bonjour @Dan, Bonjour @Jean-Paul et bonjour à tous
J'ai tardé à répondre car je souhaitait essayer toutes les solutions que je maitrisait avant de demander plus d'assistance mais malheureusement mon problème n'est pas résolu, voici les symptômes :
Après ouverture du fichier nommé Validation, un message « 400 » apparait, il ne bloque pas le code mais la fonction activatetab du ruban ne fonctionne plus et parfois l’appel à invalidatecontrol (Très rare), cette erreur ne se produit pas s’il n’y a plus de ruban personnalisé dans le fichier nommé Validation @Jean-Paul, j'ai fait un check complet de tout ce que vous m'avez appris mais je ne trouve pas ce qui cloche
Préparatifs :
Mettre les deux fichiers dans le même répertoire
Ouvrir chaque fichier sans lancer le code (Réponse non) et mettez votre login de session dans A2 de la feuille Utilisateurs
Mettez ensuite l’adresse du répertoire dans lequel vous travaillez en B23 de la feuille Paramêtres
Vous pouvez maintenant tester : Attention, en cas d’arrêt du code, supprimer onglet « En_cours » et remettre B3 à B6 à 0 ainsi que B7= » » dans l’onglet « Paramètres
Voici comment reproduire le phénomène :
1/ Lancez le fichier "Validation ruban en développement" avec son code, vous arrivez sur la partie menu principal du ruban personnalisé.
2/ Cliquez sur l’option « Faire une validation initiale » choisissez ensuite l’option 1 faite enter et cliquez ensuite sur non pour la question dupliquer le rapport
3 / vous êtes maintenant sur la partie option de travail du ruban personnalisé, utilisez le bouton « Emettre Rapport de Validation »
4/Cliquez sur "Emission d’un rapport final" et Valider le répertoire d’enregistrement (Par défaut ce sera celui que vous avez entré en B23 mais vous pouvez en choisir un autre
5/ Laissez 0 à la question sur les pièces jointes cette partie de code ne pose pas de Pb, cliquez sur enter
6/idem pour le code d’ajout de photo, cliquez sur non pour passer cette étape
7/ enfin, cliquez sur oui pour le traitement dans le planning centralisé, excell tente alors d’ouvrir le fichier nommé "Validation" et affiche une erreur « 400 » (Cette erreur ne se produit pas s’il n’y a pas de ruban personnalisé dans le fichier Validation), pourtant, en validant, le code se poursuit sans soucis, choisir "périodique" puis à l’ouverture de la fenêtre suivante cliquez sur envoyer
8/ vous revenez alors au départ mais le ruban personnalisé ne repasse plus sur la partie menu principal (activatetab) alors qu’il le fait bien si on enclenche pas la procédure de gestion dans le planning centralisé, idem pour l'erreur "400"
Merci d'avance à tous de vos suggestions,
bonne journée
PS : ci joint les deux fichiers de test
Bonjour à tous,
@Patrick,
J'ai remarqué que le ruban du fichier validation-ruban-en-developpement comportait des erreurs, Il faut bien comprendre qu'en xml il ne faut pas mettre d’accents sur les Id des contrôles, (prenez l'habitude de ne pas mettre d'accents partout même en VBA, Excel vous le rendra bien.) De plus vous avez des SplitButtons qui ne sont pas entièrement programmés, vous devez leurs ajouter des boutons, menus, ou autre. Mais ne pas les laisser vide, si vous ne voulez pas ajouter de boutons alors transformez-les en boutons simple.
Maintenant si je regarde votre procédure OnLoad (Ouverture_Ruban) du ruban vous y collez un Select Case où seule la la réponse 'Oui' est traitée si l'on répond non votre ruban n'est pas initialisé. Donc vous pouvez toujours courir à faire un Invalidate...
Maintenant vous devez absolument faire du tri dans vos modules. Vous devriez avoir un module CallBacks qui contient les procédures du ruban. Pour vous (Appel_Bouton_Bandeau).
Vous ne devez aussi faire le ménage au niveau des noms de ruban.
Bonjour à tous, @Jean-Paul, merci de votre retour, en effet j'avais omis un des ID dans mes corrections, désolé.
Concernant les SplitButtons, ce sont des IdMso et tous les documents trouvé sur ces deux IdMso indiquent que ce sont des SplitButtons, je ne pas à les faire fonctionner autrement et il prennent automatiquement la bonne forme, j'ai fait de nombreuses tentavite en essayant d'ajouter un bouton voir un menu mais je n'y parvient pas. Dois-je ajouter un bouton "factice" en y mettant l'image d'origine par sont code Mso car je n'y suis pas parvenu non plus? Si j'essaye de les transformer en bouton simple, ils ne s'affichent simplement pas.
Concernant la procédure onload, en effet j'ai ajouté ce select case afin que les membres du forum puissent bloquer le code et le regarder avant de l’exécuter toutefois je prends la remarque je vais séparer le code de la procédure Onload du code de démarrage de mon fichier
pour les regroupements, j'ai suivi vos conseils et commencé à regrouper beaucoup de choses mais en effet il reste une ou deux procédures qui ne sont pas à leur place, je continue d'y travailler
En l'état actuel, j'ai corrigé le dernier Id avec accent, supprimé la table contenant les SplitButtons incomplets mais l'erreur est toujours présente (même sans le select case), dernier point à regarder, qu'entendez vous par "ménage au niveau des noms de ruban" les id utilisés sont ils trop "complexe"?
merci encore pour votre aide
Bonjour à tous,
@Patrick, Vous déclarer Public MonRuban As IRibbonUI dans le module Variables_Globales. Ce n'est pas une bonne solution. Vous devriez regrouper les fonctions et procédures du ruban dans un seul module. Voici un module minimum pour les Callbacks :
'@ModuleDescription "Callbacks for CustonUI."
'@IgnoreModule
'@Folder "Ribbon"
'-------------------------- CALLBACKS MINIMUM
Option Explicit
#If VBA7 Then
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef destination As Any, _
ByRef SOURCE As Any, _
ByVal Length As Long)
#Else
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef destination As Any, _
ByRef SOURCE As Any, _
ByVal Length As Long)
#End If
#If VBA7 Then
Private Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
#Else
Private Function GetRibbon(ByVal lRibbonPointer As Long) As Object
#End If
Dim RibbonTemp As Object
CopyMemory RibbonTemp, lRibbonPointer, LenB(lRibbonPointer)
Set GetRibbon = RibbonTemp
Set RibbonTemp = Nothing
End Function
'******************************************************
' // PUT HERE ALL THE FUNCTIONS OF THE RIBBON CONTROLS.
'******************************************************
'@Description "Function to be run when loading ribbons."
Public Sub OnRibbonLoad(Ribbon As IRibbonUI)
Set mUIRibbon = Ribbon
mUIRibbon.ActivateTab "tab0"
' // Save the Ribbon Handle
sys_Settings.Range("sysvr_RibbonHandle").Value = CStr(ObjPtr(mUIRibbon))
End Sub
'@Description "Recovers the ribbon if lost."
Sub RefreshRibbon(Optional ByVal ControlID As String = "")
If mUIRibbon Is Nothing Then
#If VBA7 Then
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
#Else
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
#End If
If ControlID = vbNullString Then
mUIRibbon.Invalidate
Else
mUIRibbon.InvalidateControl ControlID
End If
Else
If ControlID = vbNullString Then
mUIRibbon.Invalidate
Else
mUIRibbon.InvalidateControl ControlID
End If
End If
End SubIl ne vous reste plus qu'à y intégrer vos routines des Callbacks des contrôles du ruban.
Vous n'avez pas besoin de déclarer un ruban en public. Vous devez simplement appeler la procédure RefreshRibbon du module Callbacks c'est elle qui se charge de récupérer le ruban s'il est passé à Nothing.
Si vous ne voulez pas passer par un autre module pour les procédures du ruban (exemple 'RibbonX' pour ma part) faites en sorte de nommer chaque procédures de vos contrôle un exemple ci dessous.
Ici les procédure sont regroupées par actions 'GetEnabled', 'OnAction', etc...
<button id="Quitter2"
visible="true"
label="Quitter Excell"
screentip="Permets de sortir d'Excell"
image="Quitter"
supertip="Pour sortir proprement du programme fermer excell avec remise en état de tous les paramètres"
getEnabled="GetEnabledButtonQuitter2"
onAction="OnActionButtonQuitter2"/>
<button id="btnFermer"
visible="true"
label="Fermer le fichier"
screentip="Permets de fermer l'outil de Validation"
image="fermer"
supertip="Pour sortir proprement du programme avec remise en état de tous les paramètres"
getEnabled="GetEnabledButtonFermer"
onAction="OnActionButtonFermer"/>
Mais vous pouvez les regrouper par contrôle en passant le nom du contrôle devant (exemple : Quitter2_GetEnabled)
bonsoir à tous, bonsoir@Jean-Paul et merci pour ces nouveaux conseils, je vais integrer ces nouvelles optimisations et tester si ce "beug" disparait, je tiendrais le forum au courant
bonne fin de journée à tous
Bonjour,
Désolé de mon retour tardif.
Non pas que je ne veuille plus m'occuper de votre souci mais comme Jean Paul vous répond dans la continuité de votre fichier, je ne pense pas être de grande utilité.
Seule chose que j'avais remarqué au début en regardant votre fichier, ce sont les accents utilisé dans votre code. A éviter...C'est un détail dans votre problème mais comme je l'ai souvent écrit, avec VBA il faut toujours penser anglais. Le cas échéant vous risquez les soucis.
Cordialement
Bonjour à tous,
Patrick, j'ai mis à jour le module de Callbacks, vous pouvez maintenant utiliser les Wrappers pour toutes les méthodes.
- ActivateMyTab --> ActivateTab
- ActivateMyTabMso --> ActivateTabMso
- ActivateMyTabQ --> ActivateTabQ
- InvalidateMyControlMso --> InvalidateControlMso
- InvalidateMyControl --> InvalidateControl et Invalidate
Voici le module :
'@ModuleDescription "Callbacks for CustonUI."
'@IgnoreModule
'@Folder "Ribbon"
Option Explicit
'Private mUIRibbon As Office.IRibbonUI
#If VBA7 Then
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef destination As Any, _
ByRef SOURCE As Any, _
ByVal Length As Long)
#Else
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef destination As Any, _
ByRef SOURCE As Any, _
ByVal Length As Long)
#End If
#If VBA7 Then
Private Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
#Else
Private Function GetRibbon(ByVal lRibbonPointer As Long) As Object
#End If
Dim RibbonTemp As Object
CopyMemory RibbonTemp, lRibbonPointer, LenB(lRibbonPointer)
Set GetRibbon = RibbonTemp
Set RibbonTemp = Nothing
End Function
'Public Property Get UIRibbon() As Office.IRibbonUI
' Set UIRibbon = mUIRibbon
'End Property
'@Description "Activates a custom tab on the ribbon, recovering the ribbon if lost."
Public Sub ActivateMyTab(ByVal TabID As String)
' 1. Vérification et Récupération du Ruban
If mUIRibbon Is Nothing Then
#If VBA7 Then
' Utiliser CLngPtr pour les systèmes 64 bits
On Error Resume Next ' Gérer le cas où la valeur est vide/invalide
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#Else
' Utiliser CLng pour les systèmes 32 bits
On Error Resume Next
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#End If
End If
' 2. Appel de la Méthode d'Activation
If Not mUIRibbon Is Nothing Then
mUIRibbon.ActivateTab TabID
Else
' Optionnel : Gérer l'échec de la récupération (ex: afficher un message d'erreur)
Debug.Print "Erreur : Impossible d'activer l'onglet '" & TabID & "'. Le ruban n'a pu être récupéré."
End If
End Sub
'@Description "Activates a built-in tab (MSO) on the ribbon, recovering the ribbon if lost."
Public Sub ActivateMyTabMso(ByVal TabMsoID As String)
' 1. Vérification et Récupération du Ruban (même logique)
If mUIRibbon Is Nothing Then
#If VBA7 Then
On Error Resume Next
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#Else
On Error Resume Next
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#End If
End If
' 2. Appel de la Méthode d'Activation MSO
If Not mUIRibbon Is Nothing Then
mUIRibbon.ActivateTabMso TabMsoID
Else
' Optionnel : Gérer l'échec de la récupération
Debug.Print "Erreur : Impossible d'activer l'onglet MSO '" & TabMsoID & "'. Le ruban n'a pu être récupéré."
End If
End Sub
'@Description "Activates a custom tab on the ribbon and gives it focus (Q), recovering the ribbon if lost."
Public Sub ActivateMyTabQ(ByVal TabID As String)
' 1. Vérification et Récupération du Ruban
If mUIRibbon Is Nothing Then
#If VBA7 Then
On Error Resume Next
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#Else
On Error Resume Next
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#End If
End If
' 2. Appel de la Méthode d'Activation
If Not mUIRibbon Is Nothing Then
mUIRibbon.ActivateTabQ TabID
Else
Debug.Print "Erreur : Impossible d'activer l'onglet '" & TabID & "'. Le ruban n'a pu être récupéré."
End If
End Sub
'@Description "Invalidates a built-in control (MSO) on the ribbon, recovering the ribbon if lost."
Public Sub InvalidateMyControlMso(ByVal ControlMsoID As String)
' 1. Vérification et Récupération du Ruban
If mUIRibbon Is Nothing Then
#If VBA7 Then
On Error Resume Next
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#Else
On Error Resume Next
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
On Error GoTo 0
#End If
End If
' 2. Appel de la Méthode d'Invalidation
If Not mUIRibbon Is Nothing Then
mUIRibbon.InvalidateControlMso ControlMsoID
Else
Debug.Print "Erreur : Impossible d'invalider le contrôle MSO '" & ControlMsoID & "'. Le ruban n'a pu être récupéré."
End If
End Sub
'@Description "Function to be run when loading ribbons."
Public Sub OnRibbonLoad(Ribbon As IRibbonUI)
Set mUIRibbon = Ribbon
mUIRibbon.ActivateTab "tab0"
' // Save the Ribbon Handle
sys_Settings.Range("sysvr_RibbonHandle").Value = CStr(ObjPtr(mUIRibbon))
End Sub
'@Description "Recovers the ribbon if lost."
Sub InvalidateMyControl(Optional ByVal ControlID As String = "")
If mUIRibbon Is Nothing Then
#If VBA7 Then
Set mUIRibbon = GetRibbon(CLngPtr(sys_Settings.Range("sysvr_RibbonHandle").Value))
#Else
Set mUIRibbon = GetRibbon(CLng(sys_Settings.Range("sysvr_RibbonHandle").Value))
#End If
If ControlID = vbNullString Then
mUIRibbon.Invalidate
Else
mUIRibbon.InvalidateControl ControlID
End If
Else
If ControlID = vbNullString Then
mUIRibbon.Invalidate
Else
mUIRibbon.InvalidateControl ControlID
End If
End If
End SubPour l'appel je vous conseille toujours de faire des appels explicites, NomDuModule.NomDeLaFonction exemple pour un module callbacks.
Callbacks.ActivateMyTab "MonTab" ' // Activation d'un onglet
Callbacks.InvalidateMyControl ' // Sans rien il mets à jour le ruban complet.
Callbacks.InvalidateMyControl "MonControl" ' // Mise à jour du contrôleDans le module j'ai commenté la propriété Get UIRibbon elle n'a plus lieux d'être, puisque toutes les méthodes sont Wrappées.
Je peux vous fournir un module complet avec les méthodes, pour vos contrôles personnalisés.
Bonjour à tous et désolé de cette absence prolongée, en premiers lieu tous mes voeux à chacun d'entre vous.
@Jean-Paul, merci pour ce travail, je vais reprendre cela dès que possible le programme étant en stand by pour le moment pour diverses raison.
Bonne journée à tous