Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
Discussion GroupsFormsForms ProgrammingQueriesModules / DAO / VBAReports / PrintingMacrosDatabase DesignSecurityConversionImporting / LinkingSQL Server / ADPMultiuser / NetworkingReplicationSetup / ConfigurationDeveloper ToolkitsActiveX ControlsNew UsersGeneral 1General 2
Access DirectoryToolsTutorialsUser Groups
Related Topics
SQL ServerOther DB ProductsMS OfficeMore Topics ...

MS Access Forum / Forms / May 2007

Tip: Looking for answers? Try searching our database.

Microsoft Access 2007 Ribbon

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Guy - 23 May 2007 06:39 GMT
Hi All,

I am upgrading a large .ADP application from Office 2003 to Office 2007 and
would like some feedback on the best way to manage Ribbons in Access.

I am in the process of converting all the old custom menubar/toolbar
functionality into equivalent/similar/improved functionality using the Ribbon

Currently I have over 20 Ribbons (and growing) all use the "Start from
Scratch=True" attribute. Obviously I would prefer to utilise less Ribbons,
but by the same token the prospect of dynamically applying lots of
customisation to fewer Ribbons when each form or report is opened appears
equally onerous. Needless to say I am still learning about how to best manage
the Ribbon and hence the reason for this posting

The biggest problems I currently have are simple issues such as
enabling/disabling controls on the Ribbon dynamically according to actions a
user might take on any given form.

To force a ribbon control refresh requires you to "Invalidate" that control
on the ribbon, however, this requires a reference to the ribbon and I have
over 20 of them.

I have tried storing the references in a collection using the Ribbon
"OnLoad" callback:

Public mcolRibbon As New Collection

Public Function OnLoad(ByVal Ribbon As IRibbonUI)
 mcolRibbon.Add Ribbon, Screen.ActiveForm.RibbonName
End Function

However when I try to retrieve the reference perhaps in the Form_Open event

Dim objRibbon as IRibbonUI
Set objRibbon = GetRibbon(me.RibbonName)

Public Function GetRibbon(strRibbonName as String) As IRibbonUI
 GetRibbon = mcolRibbon.Item(strRibbonName)
End Function

or invalidate the control from the from by calling:

Public Sub gsubInvalidateControl(strRibbonName As String, strControl As
String)
 mcolRibbon.Item(strRibbonName).InvalidateControl (strControl)
End Sub

it fails.

There appear to be two issues:

1) If the Ribbon has not yet been loaded the open Form action causes the
Ribbon to Load, however, the Form_Open event occurs before the Ribbon_OnLoad
event meaning that a Null Reference is returned.

2) I don't use custom collections very often and I may have the syntax
incorrect, but I am also experiencing problems accessing the Ribbon object
from within the collection.

Can anyone assist in either resolving the issues I am having or suggest a
better approach.

Your Assistance Appreciated
Guy
Albert D. Kallal - 23 May 2007 14:33 GMT
Ah...

I like you idea of building a collection.

I really feel bad that I don't have more time to write up a nice article.

However, a few things:

The active screen is not set until AFTER both on-open , and then on-load ARE
COMPLETE.
(I know this, because I have for years use the on-open, or even the on-load
event of the form
to pick up the PREVIOUS form by going:

set frmPrevous = screen.ActiveForm.

So, that puts a bit of damper on your approach to pick up the active
screen...(you can't do that from on-load, or on-open events of a form...you
get the previous screen!!!

and, normally, a *great* trick is to place the public function in a form, as
menu items will call that first before the same function name in a public
module (but, again..the form is not yet consider loaded. -- DO NOTE that
once the ribbon is loaded, then routines declared public in your form that
HAS THE FOCUS will be called. This allows some really nice custom code for
each form, but the SAME ribbon to be used.

Anyway, you can't use screen.active.

2nd thing:

>  mcolRibbon.Item(strRibbonName).InvalidateControl (strControl)

You do realize that invalidate method only causes the xml mark-up text to be
re-executed for that control. So, for setting the controls visible property,
you actually have to maintain that value in memory (in the old menu bars,
the menu control it self held the state of the control. This is NOT the case
with a ribbon. (it possible you realize this...as you not posted any xml
ribbon code)..

So, what this means is that you can hard code the mark-up, but that is of no
use. The solution is thus to use a function, such as:

         <button id="button1" label="Buttion1"
                           getVisible="MyVisible"
                           onAction="=MyTest1()"/>

While we at this, it important to note that the mark-up text IS CASE
sensitive (I read 20+ articles about the ribbon, and NOT ONE has mentioned
this -- I just trying to save you some lessons that I learned!!!.

Note that we have a function called MyVisble.  In theory, you need a
variable declared for EACH control that you plan to have visible, or not
visible.

The ONLY difference between using

mcoIRibbion.Invalide

and

mcoRibbion.InvalideCotnorl(strContlr)

Is that the 2nd example only runs the code for the ONE control. THIS IS THE
ONLY DIFFERENCE. In other words, the same piece code would get called in
both cases, but the InvalideContrll is better code because it will not call
each and every piece of code for each button on the ribbon.

So, keep in mind, the invalide call does not actually make the control
visible, or invisible...it is the setting for the function in the he call
back that determines this. and *further* keep in mind that the code called
will be the same both cases, but it better to use the Invalidecontrfll as
you not wind up having every control on the bar get called. (however, you
code MUST STILL work for that case).

eg:

Public Sub MyVisible(control As IRibbonControl, _
                         ByRef visible As Variant)

  static m_buttion1       as boolean
  static m_buttion2        as boolean
  select case contorl.id

    case "buttion1"

        visible = m_buttion1

  case "buttion2"

End Sub

In the above, "id" will be text string (buttion1 from above) in BOTH cases

eg:

mcoribbion.Invalide
or
mcoribbion.InvalideContorl("buttion1")

It is the *system* that provong the value for contorl.ID in the above. For a
small robbionk you could always use the Invalide, and never have to use the
InvalideContrl method.

So, what the above means is that you need a Boolean variable for each
control that I plan to turn off, and turn on to hold the state of that
control.

(or a separate "getVisible" function for each control that you plan to make
visible/non visible).

Without question, building a custom collection is a great idea. with a bit
of work you could near the same syntax of what we been using for years....

I have to run out the door right now. I *really* need to write an article on
this......

Signature

Albert D. Kallal    (Access MVP)
Edmonton, Alberta Canada
pleaseNOOSpamKallal@msn.com

Guy - 24 May 2007 03:25 GMT
Albert,

Just thought of another good reason why I can use Screen.ActiveForm, apart
from the fact that it doesn't work, it maybe a report that is causing the
Ribbon "OnLoad" event.

Guy

> Ah...
>
[quoted text clipped - 112 lines]
> I have to run out the door right now. I *really* need to write an article on
> this......
Guy - 24 May 2007 02:26 GMT
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.

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.

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.

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.

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?

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.

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

If I can't use the Screen.ActiveForm mechanism to determine which Ribbon is
calling the "OnLoad" callback then how do I uniquely "Key" the collection,
the "Tag" XML attribute doesn't appear to be supported at the CustomUI level?

Is there no method by which the active form can refer to the active ribbon
without trying to access a stored reference passed by the "Onload" callback?

If I can populate the Collection then I also find that I am having problems
accessing the Ribbon IRibbonUI object from within the collection. It maybe
that I haven't populated the collection correctly or it maybe that I am not
using the incorrect syntax.

Like yourself I have searched Microsoft and the Web and looked at a number
of sample databases using RibbonX, but none of them tackle the issues of
multiple (more than one) Ribbon references. Which leads me to conclude that
perhaps 1 Ribbon and lots of customisation maybe the better method, although
onerous.

Yes I can think of solutions without using a collection such as a unique
Onload callback for each Ribbon along with a separate variable to maintain
each reference, but this
feels like a "kludge" option.

Are you able to assist with any further thoughts or code samples.

Your assistance appreciated

Guy

> Hi All,
>
[quoted text clipped - 61 lines]
> Your Assistance Appreciated
> Guy
Albert D. Kallal - 24 May 2007 12:26 GMT
> Hi Albert,
>
[quoted text clipped - 3 lines]
> 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:
>
[quoted text clipped - 3 lines]
> 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
[quoted text clipped - 4 lines]
> 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,
[quoted text clipped - 3 lines]
> 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!).

Signature

Albert D. Kallal    (Access MVP)
Edmonton, Alberta Canada
pleaseNOOSpamKallal@msn.com

Guy - 25 May 2007 02:15 GMT
Albert,

Thank you once again for your comprehensive response. Lots of food for
thought... Good work.

I was aware of many of the gotcha’s eg. the ribbon load only occurs once and
you never know which ribbon is going to get loaded first because that is
dependant on user action ie. Which form they open.

While I digest your posting… I was also thinking about how you could reduce
the amount of form Ribbons perhaps by using the form activate and deactivate
events.

The activate event would customise the Ribbon for that form and the
deactivate event would remove the customisations, ensuring that the “1”
Ribbon was always left in a consistant state from which the new “active” form
could apply it changes.

However, I haven’t put any of my thoughts into code yet so I don’t know how
feasible this is.

Still plenty to work to do so I had best get on with it.

Your assistance much appreciated and I’m sure that most developers who read
this thread will have learnt something new.

Kind Regards,
Guy

> > Hi Albert,
> >
[quoted text clipped - 281 lines]
>
> Public Property Let visible(bol As Boolean)
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2009 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.