Guy said:
Hi Albert,
Thank you for your comprehensive response.
I wasn't aware that once the form was loaded/opened the forms public
functions would get called in preference to module public functions. This
could be useful so I will investigate this aspect further.
Actually, a97 to a2003 worked this way also. So, most of my custom menu bars
in previous versions DID in fact call code in the current active form.
This does man that some forms did share the same menu bar. However, in all
practical use, most of my complex forms did have a custom menu bar, but that
menu bar for the most part did call code in the current active form (it
makes sense to place code for a form in the form code module).
Ideally I would like to use just 3 Ribbons:
1) Home - Which allows quick access to key forms, reports and tools such
as
database backup within the application and is shown only when the Main
Menu
is active.
I think in the above case, I would simply build a xml ribbon,, and then
specify this ribbon in the forms "other" tab. This will load up the ribbon
for you, and you not need any code.
2) Form - Which shows for each form the "Home" button which takes you back
to the Main Menu and Home Ribbon and the standard Records/Filters/Cut and
Paste groups plus custom Command actions that are relevant to that form.
So
for instance if it was the "Customer Invoice" form you could print the
Customer Invoice or if it was the "Budget" form you could export the
budget
to Excel. Some forms would have many Command actions available.
Sure, to include the standard paste, cut etc, you can either build you own
"group" that you add to each ribbon. However, it is MUCH easier to include
the standard cut/paste menu "group" in your ribbon that you build for the
current form.
To make you "own" clipboard, you could use:
<group id="Clipboard" label="Clipboard">
<control idMso = "Copy"/>
<control idMso = "Cut"/>
<control idMso = "Paste"/>
</group>
however, it much easier to include the built in ones. For example, lets
include both built in clipboard, and the "find" and filter group. You can
go:
<group idMso="GroupClipboard" />
<group idMso="GroupFindAccess" />
<group idMso="GroupSortAndFilter" />
So, it is really easy to add the standard cut/copy to your existing ribbon.
3) Report - Which shows for each report the "Home" button plus the
standard
print/export/send mail groups and so on. It is unlikely that many custom
Command actions would be required for this Ribbon.
Right...and again, I would build a new ribbon, but simply include some of
the report groups. You do know how to "find" the above group names? (and as
I mentioned...remember...they are case sensitive).
To find the id of a command, or group, simply go:
office button->access options (lower right)
customize
Note in the customizing window if you hold your mouse over a command button,
you get the isoID of that button (it that last value enclosed in ()).
NOTE VERY clearly how in addition to the buttons, the "groups" for each of
those buttons are ALSO IN that list. So, you can easily grab the group for
your report....
Currently I have about 20 individual Form Ribbons, however, in your
opinion
would it be better to have just 1 Form Ribbon and customise it
appropriately
when each form becomes active?
I actually think it ok to have a ribbon for each form, especially if that
ribbon has form specify commands. so, sure, "include" the standard clip
group, and perhaps a general close button group for each form. but, I would
certainly continue to build a ribbon for each form.
Assuming that there are pros/cons to both the above approaches and that we
are happy to continue with the multiple Form Ribbon method. The next
question
is maintaining references to each Ribbon so that we can hide/show or
enable/disable etc the Form Command actions.
Ok, this is tough part! I just finished coding a solution that allows me to
use a collection to "enable" and "disable" a button by name. However, the
code is not finished, has some problems. However, it is *perfect* for
migration existing applications, and you can simple code in the form as you
always done.
eg:
MyRibbon.MyControls("button1").Enabled = True
or
MyRibbon.MyControls("button1").Visible = True
or
MyRibbon.MyControls("button1").Label = True
The MyContorls collection is actually automatically populated by the ribbon
load command I have. So, right now, this code is running as I write this,
but there are still some design issues that need to be addressed.
However, since my code is not ready, then your going to have to write code
for each button that you want to enable, and disable. And further, you going
to have to place that code in a standard code module (not the forms code
module). And, worse, you going to have to setup a piece of code for each
control that you need to enable and disable. Remember, the ribbon does NOT
store the state of your enabled/disabled button.
It was easy before, since if you enabled, or displayed a control in a menu
bar, you could, or change that value at will. In the case of a ribbon, your
code must hold these values in variables (which is why I love my code
example...since you don't have to write ANY additional code, but use the
above familiar syntax.
As previously stated I have tried to maintain the Ribbon references in a
custom Collection which is loaded via the Ribbon "OnLoad" event. However,
I
am having problems with:
1) Event timing
2) Populating and Accessing the reference in the Collection
Yes. I just wrote that code last night.
A few got ya's that I just learned:
The ribbon load event only fires once, and that is when the form loads.
Additional loads of the form does NOT fire the load event. so, I do need a
global collection of ribbons, and further, I need a routine that checks if
the ribbon is already loaded.
in a standard code module, I have for the global enabled, and disabled, I
have
Public Sub MyVisible(control As IRibbonControl, _
ByRef visible As Variant)
Dim f As Form
If Forms.Count > 0 Then
Set f = Screen.ActiveForm
visible = f.MyRibbon.MyControls(control.ID).visible
End If
End Sub
Public Sub MyEnable(control As IRibbonControl, _
ByRef visible As Variant)
Dim f As Form
Set f = Screen.ActiveForm
visible = f.MyRibbon.MyControls(control.ID).Enabled
End Sub
In addtion, my global load code in this same module is:
Public Sub SetMyRib(frm As Form, Optional strRibbonName As String = "")
Set f = frm
If strRibbonName = "" Then
strRibbonName = f.name
End If
f.RibbonName = strRibbonName
' the above code causes the ribbom to load, and fires the code below
End Sub
Public Sub MyRibbonLoad(ByRef nribbonUI As Office.IRibbonUI)
Set f.MyRibbon.m_ribbon = nribbonUI
Set f = Nothing
End Sub
In the above, I should be saving the ribbon into a global collection in
addition to the "myRibbom"
MyRibbom is a class object I built in code, and every form you make is going
to define this. So, in each form that needs a custom ribbbom, I go:
Option Compare Database
Option Explicit
Public MyRibbon As New clsRibbon
I leave the ribbon setting in he "other" tab blank, because I need to pass
the form name (the problem of screen focus).
(so, the callig code just sets the ribbom value for the form...it less
code).
So in my forms on-load event, I go:
Call SetMyRib(Me, "test1")
me is the form, and "test1" is the name of the ribbom I want to load
then, I have a class module for the robbom, it simply a colleciton of
contorls that gets auto built for me
clsRibbon
Option Compare Database
Option Explicit
Dim colControls As New Collection
Public m_ribbon As IRibbonUI
Public Property Get MyControls(strC As String) As clsRibContorl
Dim i As Integer
Dim intGotOne As Integer
Dim NewControl As New clsRibContorl
' look for contorl in colleciton, if not in, then add...
For i = 1 To colControls.Count
If strC = colControls(i).name Then
intGotOne = i
Exit For
End If
Next i
If intGotOne = 0 Then
' add contorl, set defaults
NewControl.Enabled = True
NewControl.visible = True
NewControl.Label = ""
NewControl.name = strC
colControls.Add NewControl, strC
Set MyControls = NewControl
Else
Set MyControls = colControls(intGotOne)
Me.m_ribbon.InvalidateControl (strC)
End If
End Property
And, for each member of the control collection, we do need to "save" those
values, so, I built a custom class control
to STORE values for each ribbom contorl (since the robbion don't store
them).
it is:
clsRibContorl
Option Compare Database
Option Explicit
Dim m_enabled As Boolean
Dim m_visible As Boolean
Dim m_Label As String
Dim m_name As String
Public Property Let Enabled(bol As Boolean)
m_enabled = bol
End Property
Public Property Get Enabled() As Boolean
Enabled = m_enabled
End Property
'-----------
Public Property Let visible(bol As Boolean)
m_visible = bol
End Property
Public Property Get visible() As Boolean
visible = m_visible
End Property
Public Property Let Label(str As String)
m_Label = str
End Property
Public Property Get Label() As String
m_Label = m_Label
End Property
Public Property Get name() As String
name = m_name
End Property
Public Property Let name(str As String)
m_name = str
End Property
That is it. The whole thing thus gives me a re-usable collection that is
auto populated.
You have to add the enabled, and visible call-backs to ONLY THOSE CONTROLS
that you need this feature:
eg:
<button id="button1" label="Buttion1"
getVisible="MyVisible"
getEnabled="MyEnable"
onAction="=MyTest1()"/>
<button id="button2" label="button2" onAction="=Mytest2()"/>
In the above, you can see that for buttion2, I never enable, or disable the
button, so I did NOT add the call-backs for the getvisble and get enabled.
And, I suppose you could always just include the above two call-backs for
all controls, but it will load up the custom collection.
The above is not a lot of code, as is something I threw together last night
in a very short time (I am just leaning this stuff too, and this is my first
try at this ribbon thing).
So, each form will have it own custom "ribbon" collection with the enable,
visible, and label property able to be set. While this means that you have
to declare that "myrobbon" for each form you use, it only one var and the
rest of the code will make this whole process automatic.
As mentioned, in testing,, the code loads, and runs....however, when you
un-load a form, and then re-load the form, it breaks the code because the
ribbon load event does not re-fire (this is good from a performance point of
view, but since the ribbon does NOT re-fire, I lost the reference to the
internal ribbon that is already loaded. So, either I build a collector for
this, or find a command to force the ribbon to re-fire the load event....
Really, less then 1 hour more on this...and it should be ready to go...
(however, I not have that 1 hour until about Monday!).