Gunthers IDBE ribbon creator

Hi EE
Trying to enable = false a button in IDBE ribbon creator. If I go to basRibbonCallback then Sub Getenabled(Control as IRibboncontrol) find the button in the case
statement and change enabled to false works when running the app. My question now is would it be possible to change to enabled = true via VBA or is there some other way of doing this. I have spent some days trying to sort this out without success. Any help appreciated.

chestera
chesteraAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

John TsioumprisSoftware & Systems EngineerCommented:
MainRibbon.InvalidateControl ("NameOfYourControl")

Open in new window

This will cause the code that is related to the control to reevalute to the current conditions
EDIT: maybe your Ribbon is named differently ....just check the Property that returns the RibbonUI
It should be something like this
Public Property Get MainRibbon() As IRibbonUI
On Error Resume Next
  'global property that re-exposes the ribbon system-wide
  'so consumers can call the IRibbonUI.Invalidate method as required
  Set MainRibbon = m_ribbon
End Property

Open in new window

0
ste5anSenior DeveloperCommented:
In addition to John's answer;

To change the behavior of the ribbon during runtime, you need to call Invalidate on the ribbons element, which should change. You cannot modify it by adding or removing controls. Thus you need to add all required controls in your XML describing it and add GetVisible handler for those you want to hide.

As you need to store the ribbon in a module variable: make any method interacting with it foolproof. Thus add error handling to any of those methods. Otherwise, it is possible that an uncaught error in the module can lead to a reset and thus losing the ribbon access.
0
chesteraAuthor Commented:
John Tsioumpris

Thank you for this info. It's late here so will try it in the morning

Alan
0
10 Tips to Protect Your Business from Ransomware

Did you know that ransomware is the most widespread, destructive malware in the world today? It accounts for 39% of all security breaches, with ransomware gangsters projected to make $11.5B in profits from online extortion by 2019.

chesteraAuthor Commented:
ste5an

thank you will take on board

Alan
0
chesteraAuthor Commented:
John Tsioumpris

Hi John
Had a look at your info hope you don't mind I am still new at creating custom ribbon and could do with some help..
I have put together a small testdB with a few buttons. I called the ribbon MainRibbon to stay on the same page as you.
in the statement MainRibbon.InvalidateControl("Namecontrol") I assume I can put behind a Button on a form so I tried and got the error Object Required.
Can't find "Public Property Get MainRibbon() as Ribbon". I checked the Ribbon name by going into the USysRibbons table and checking the XML

In the old Access2003 I had as part of the dropdown Menu "Administrator" which was disabled and could be enabled via a password. I have similar in a custom Ribbon and trying to achieve the same.

Alan
0
ste5anSenior DeveloperCommented:
Post your sample database.
0
chesteraAuthor Commented:
0
chesteraAuthor Commented:
ste5an

Sorry sent it twice

Alan
0
ste5anSenior DeveloperCommented:
First of all: Use Option Explicit. All code modules must start with

Option Compare Database
Option Explicit

Open in new window

To enable it per default, when modul is created, set Require Variable Declaration in the VBA IDE under Tools/Options.

In the initial phase OnRibbonLoad() in basRibbonCallbacks is called to store the reference to the actual ribbon. You need to call invalidate on that reference. Thus your invalidate call in your should look be:

Option Compare Database
Option Explicit

Private Sub cmdEnable_Click()
  
  gobjRibbon.InvalidateControl ("btn4")
  
End Sub

Open in new window

But calling global variables is imho a bad thing, I would change the ribbon code. Change the declaration of gobjRibbon in basRibbonCallbacks to private and add there an invalidate method, e.g.

'[..]
Private gobjRibbon As IRibbonUI

Public bolEnabled As Boolean    ' Used in Callback "getEnabled"
                                ' Further informations in Callback "getEnabled"
                                ' Fuer Callback "getEnabled"
                                ' Genauere Informationen in Callback "getEnabled".
                               
Public bolVisible As Boolean    ' Used in Callback "getVisible"
                                ' More information in Callback "getVisible
                                ' Fuer Callback "getVisible"
                                ' Further informations in Callback "getVisible

' For Sample Callback "GetContent"
' Fuer Beispiel Callback "GetContent"
Public Type ItemsVal
    ID As String
    label As String
    imageMso As String
End Type

Public Sub RibbonInvalidate()

  On Local Error Resume Next
  
  gobjRibbon.Invalidate

End Sub

Public Sub RibbonInvalidateControl(AControlID As String)

  On Local Error Resume Next
  
  gobjRibbon.InvalidateControl AControlID

End Sub
'[..]

Open in new window


This makes calling it easier. But now we run into a conceptual problem: After calling invalidate, the ribbon executes the GetEnabled method. But it does not know anything about the context. Thus this method does not know whether it should enable or disable a control.

Here we need to inject that information. There are three ways to do this:

1. Store the required state in a table.
2. Store the required state in a variable (TempVars).
3. Ask the active form for that information.

The first two approaches require that you precalculate the state, while the third is only called when necessary, which I prefer. Cause this allows the ribbon to be "more" context sensitive.

So the GetEnabled method in basRibbonCallbacks must now query the value:

Public Sub GetEnabled(AControl As IRibbonControl, ByRef AEnabled)

  On Local Error Resume Next

  Dim Form As Access.Form

  AEnabled = False
  AEnabled = Screen.ActiveForm.RibbonGetEnabled(AControl.ID)

End Sub

Open in new window


and the form itself provides the necessary state:

Public Function RibbonGetEnabled(AControlID As String) As Boolean
  
  On Local Error Resume Next

  Dim DynamicControls As Boolean
  
  DynamicControls = _
    m_Update And (AControlID = RIBBON_CONTROL_ID_UPDATEFILES_UPDATE)

  RibbonGetEnabled = False
  RibbonGetEnabled = DynamicControls And (Screen.ActiveForm.Form.Name = Me.Form.Name)

End Function

Open in new window


Now you can make the ribbon interactive by calling invalidate in the activate/deactivate events of the form. See Ribbon-1.accdb.
I prefer a simplified version, with lesser noise, see Ribbon-2.accdb. Here I invalidate the entire ribbon. Drawback it requires more time to redraw the ribbon, which can be seen in some cases. But it solves cross-state problems.
Ribbon-1.accdb
Ribbon-2.accdb
0
John TsioumprisSoftware & Systems EngineerCommented:
Check my attachment based on your application
TestRibbonCreatorTest.zip
0
chesteraAuthor Commented:
Ste5an

Thank you very much for this information much appreciated

Alan
0
chesteraAuthor Commented:
John Tsioumpris

Thank you John for your help

Alan
0
chesteraAuthor Commented:
Ste5an

All my db's have

Option Compare Database
Option Explicit

Done this test dB in a hurry

Alan
0
chesteraAuthor Commented:
John Tsioumpris

Hi John when I run your attachment i get the message "Cannot run callback function onRibbonLoad and GetVisible. I have had a quick look but can't see why it shouldn't run

Alan
0
chesteraAuthor Commented:
Ste5an

Had a look at Ribbon-1 and 2. It enables and disables btn4 perfectly. When I first run, all buttons are disabled, what drives this. Not sure if this is another question but my working app has  15 buttons under Administrator would this be a problem

Alan
0
ste5anSenior DeveloperCommented:
The ribbon XML. Those buttons have also a GetEnabled call.

If the need to be enabled all the time, then remove that call from the XML. E.g.

<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="OnRibbonLoad" loadImage="LoadImages">
  <ribbon startFromScratch="true">
    <tabs>
      <tab id="tab0" label="HOME" >
        <group id="grp0" label="Files" >
          <button id="btn1" size="normal" label="Button1" imageMso="AppointmentColor1" onAction="OnActionButton" />
          <button id="btn2" size="normal" label="Button 2" imageMso="AppointmentColor2" onAction="OnActionButton" />
          <button id="btn3" size="normal" label="Button 3" imageMso="AppointmentColor6" onAction="OnActionButton" />
        </group>
      </tab>

      <tab id="Tab1" label="Admin" >
        <group id="grp1" label="Update Files" >
          <button id="btn4" size="large" label="Update" imageMso="UpdateAsScheduled" onAction="OnActionButton" getEnabled="GetEnabled" />
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>

Open in new window


As you see, there is a lot of control information in the ribbon from the generator, which is not always necessary.
0
chesteraAuthor Commented:
Ste5an

Off course I kept looking at that but didn't see. Thank you for your time

Alan
0
chesteraAuthor Commented:
Ste5an

Working through your example a couple of questions
1 On your basRibbonCallbacks are Public SubRibbonInvalidate() and PublicInvalidateControl(AControlID etc). Did you add these because they are not on my original.

2. Sub GetEnabled(Control as etc)

Your copy
Sub GetEnabled(AControl As IRibbonControl, ByRef AEnabled)
    ' Callbackname in XML File "getEnabled"
   
  On Local Error Resume Next

  Dim Form As Access.Form

  AEnabled = False
  AEnabled = Screen.ActiveForm.RibbonGetEnabled(AControl.ID)

End Sub

My copy on my original

Sub GetEnabled(control As IRibbonControl, ByRef enabled)
    ' Callbackname in XML File "getEnabled"
   
   

    Select Case control.ID
       
        Case "btn1", "btn2", "btn3", "btn4"
        enabled = bolEnabled

   

    End Select

End Sub

My XML and Callbacks were generated by IDBE Ribbon creator. Hope you don't mind I am trying to get a better understanding of how it all works. Microsoft couldn't have made it any more complicated.

Alan
0
ste5anSenior DeveloperCommented:
1. I added these methods, cause I made gobjRibbon private. Public (global) variables are evil. And cause it's private, I need now some local methods, which can access the private gobjRibbon variable. Thus those two methods.

2. The method GetEnabled() is called when the ribbon needs to determine the enabled state of a control. If you now have any hard-coded value in your method, then this means the controls status are static. Also, you cannot pass-in context-dependent information.
As the ribbon should be context sensitive, thus I simply ask the current active form (Screen.ActiveForm) for the enabled value. So my changed GetEnabled() methods set enabled to false, then looks for an active form and executes the RibbonGetEnabled(AControl.ID) of this form (when it exists) for the actual enabled value. Now the active form can provide the state value.
This decoupling allows to store the enabled status evaluation code to be placed where it belongs, instead to get on a large if-then-else tree in the original GetEnabled() method.

Microsoft couldn't have made it any more complicated.
In the end it is a kind of (ugly) event-like system with global only hooks.
0
chesteraAuthor Commented:
Ste5an

Thank you. Your Ribbon2 when open all buttons are disabled and can then Enable and disable the Admin button the other buttons never change. When I adjust to open dB with all buttons  enabled the enable and disable Admin button doesn't work. I am now working through this to see why

Your help has given me a better understanding of how it all works.

Alan
0
ste5anSenior DeveloperCommented:
The admin button will be only enabled when the corresponding form is open and you pressed enabled.
When you want a different lifetime of the states, then you need a persistence layer like TempVars.

Sub GetEnabled(AControl As IRibbonControl, ByRef AEnabled)
    ' Callbackname in XML File "getEnabled"
    
  On Local Error Resume Next

  Dim Form As Access.Form

  AEnabled = False
  AEnabled = CBool(TempVars(AControl.ID))

End Sub

Open in new window

and

Private Sub btnDisable_Click()
  
  TempVars.Add "btn4", "False"
  RibbonInvalidateControl "btn4"

End Sub

Private Sub btnEnable_Click()
  
  TempVars.Add "btn4", "True"
  RibbonInvalidateControl "btn4"

End Sub

Open in new window

In this case, it is also sufficient to only invalidate the actual control directly.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
chesteraAuthor Commented:
Ste5an

Once again thank you. I have enough info now
0
chesteraAuthor Commented:
Ste5an

Stupid question How do I close the question and give you points. I don't use EE very often but when I do it seems to change
0
chesteraAuthor Commented:
ste5an
Thank you for all your help it was most appreciated.

Alan
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Access

From novice to tech pro — start learning today.