Share via


Finding a New Purpose

Today's Guest Writer: Savraj Dhanjal

Savraj is a Program Manager on the Office User Experience team focused on user interface extensibility for Office developers.

Today we're going to take a break from control types to highlight one of the more powerful features exposed by RibbonX: Command Repurposing.

In our research to understand how people were using extensibility in Office 2003, we found that developers at corporations often modified the built-in UI. Beyond adding a few buttons to the menus and toolbars, developers fundamentally changed the behavior of built-in buttons. If you're not familiar with Office development, you might ask, "Why in the world would someone do that?"

Imagine the international law firm of Faller, Mogilevsky, and Luu. FML doesn't want its employees printing Word documents or PowerPoint presentations unless they conform to the firm's strict standards for document formatting and style. To ensure standards are met, FML's software development team builds, as one of their many add-ins, a solution that overrides a click on the Print button. Clicking on Print now runs FML's code that checks the document for errors. If there are any errors, the user is notified. If not, the code sends the document to the printer.

You can imagine this same scenario applying to the Save button. FML wants to make sure all the metadata attached to a document is properly formatted before the user saves it. So FML takes over the Save button as well. When the user clicks Save, FML's code runs, updates metadata, and permits Office to continue with the built-in Save operation.

A few ways to do it today

There are a few ways to do this in Office 2003. The first way is through specially named macros in Word. Hit Alt-F11, select Insert > Module in the Visual Basic Editor, and paste the following macro into the code window.

Sub FileSave()MsgBox ("Surprise! This works!")End Sub

Test the macro by pressing F5 while your insertion point is inside the macro code. If you see the message box, it works. Now select Save from Word's File Menu. Surprise, it really does work. Every time you select "Save" Word will run your Macro and display a message box, instead of the built-in Save command. As yet another testament to Office as a platform, this behavior was introduced well before 1996, as part of WordBasic. The Word MVP's have a nice write-up on this behavior, and there are a number of other special macro names. Since this functionality pre-dated CommandBars, it only works in Word, and it hooks all instances of the Save button, including the one on the toolbar, as well as the control key shortcuts. So if you hit CTRL-S, the macro runs. The same goes for a click on the floppy disk icon in the Standard Toolbar. Isn't that neat? With three lines of VBA code (in Word) you can change the behavior of Save.

If you wanted to match this precise behavior using CommandBars, you would need to find each instance of the Save button and give it a new "onAction" macro, and the user might have customized the interface, so you'll have to find all instances of it. The following code snippet changes the onAction of the Save button on just the Standard Toolbar. After you run the Macro below, the Save button in the Toolbar and the Save button in the File Menu will do different things, and this method doesn't capture CTRL-S.

Sub newSaveAction()Application.CommandBars("Standard").Controls("Save").OnAction = "surprise"End SubSub surprise()MsgBox ("Surprise!")End Sub

Run the first macro above, and then a click on the Save button will call the second one. This general idea works in all of the Applications with CommandBars.

But wait, there's more!

It shouldn't surprise you that there's one more way to do this. If I want to take over the Print button in Office 2003, I can listen for the button click event. Once the right control comes through this event, I can run some code and decide whether the built-in action should continue. The following code repurposes the Print button on Word's File Menu. Just copy and paste the code below into the Visual Basic Editor (Alt-F11), and run the first Macro.

Private WithEvents buttonEvent As CommandBarButtonSub RunMe()Set buttonEvent = Application.CommandBars(  "Menu Bar").Controls("File").Controls("Print...")End SubPrivate Sub buttonEvent_Click(ByVal Ctrl As  CommandBarButton, CancelDefault As Boolean)MsgBox ("File has errors.")CancelDefault = TrueEnd Sub

After you run the macro called RunMe a click on the Print button in the File Menu will yield a message box that says "File has errors." The code above sets cancelDefault to true to cancel the default action, in this case, printing. If you set CancelDefault to false the built-in action will continue after you close the message box. With the event-based method, you get an additional feature of continuing or stopping the built-in event, and this works in VBA and COM for all of the applications.

Enter RibbonX...

The sample code to repurpose the built-in Save and Print buttons, in RibbonX, looks like this:

CustomUI Markup

<customUI xmlns="https://schemas.microsoft.com/office/2006/01/customui">``<commands>  <command idMso="Save" onAction="mySave"/>  <command idMso="Print" onAction="myPrint"/></commands></customUI>

C# Code

public void mySave(IRibbonControl control, ref bool    CancelDefault){    MessageBox.Show("Hello!");    CancelDefault = true;}public void myPrint(IRibbonControl control, ref bool    CancelDefault){    MessageBox.Show("Hello!");    CancelDefault = false;}

As you can see from the markup, we've endeavored to make it as simple as possible. Just tell us the control IDs of the built-in controls you would like to repurpose in the <commands> section. A click on that command, wherever it appears in the user interface, will trigger the onAction callback you specify. Your onAction callback can then, just like the buttonClick_event, decide whether to continue with the built-in functionality.

We've also added a little more functionality -- control over state. You can specify if this control should always be disabled, or give us a callback to manage the enabled state.

CustomUI Markup

<customUI xmlns="https://schemas.microsoft.com/office/2006/01/customui"><commands>  <command idMso="Save" enabled="false"/>  <command idMso="Print" getEnabled="myState"/></commands></customUI>

In the example above, the Save button is permanently disabled, and the Print button gets its state from the "AND" of its built-in state and the getEnabled callback "myState." When both Office says it's enabled, AND when your callback says it's enabled, the control is enabled. If either your code or Office says the Print button should be disabled, it's disabled.

In RibbonX, just give us one line of XML and you completely own what happens when the control is clicked. Best of all, in Word and PowerPoint, keyboard shortcuts are also captured, a marked improvement over Office 2003 events behavior.

Comments

  • Anonymous
    April 20, 2006
    Today without screenshots?? Somehow unusual... ;)
    PS: a good article!

  • Anonymous
    April 20, 2006
    Under what circumstances would I want a control to be always disabled?

    Let's say I'm using Word on a public access kiosk so I don't want people to be able to save documents. I would want to hide the save button altogether, not just disable it.

  • Anonymous
    April 20, 2006
    HELP! I performed those things mentioned in this blog and now I can't use my 'Save' icon from the toolbar because it says 'Macro cannot be found or disabled because of your Macro security settings' ... Luckily, my Ctrl-S and File-Save are still working. How do I get around this though? ...:P thanks

  • Anonymous
    April 20, 2006
    The comment has been removed

  • Anonymous
    April 20, 2006
    The comment has been removed

  • Anonymous
    April 21, 2006
    A great article.  I found myself wondering about security, though....it seems this could easily be hijacked and lead to quite disastrous results.  It might be good to have an write-up on what the team has done to improve security.

  • Anonymous
    April 21, 2006
    Hey everyone, here are some answers:

    1) Under what circumstances would you want a control to always be disabled?

    There are a bunch of scenarios, but a good one is the document-based UI scenario.  You can make document-level solutions with RibbonX by including a customUI part in your document. Perhaps your document requires that certain controls are always disabled. When your doc is opened the controls disabled.  In Office 2003, people achieved this by rummaging through the commandBars OM.

    2) Help! I messed up my UI with your instructions!

    Oh, right, should have included the undo instructions as well. :) The best way to fix this is by opening the Tools > Customize dialog.  Now, right-click on the Save button, and select "RESET." That should do it!

    3) How can we hide / delete elements?

    For corporate developers that want to streamline the UI, we've made it so you can hide tabs, groups, and individual controls in the file menu by setting their visibility to false.

    4) Deployment and RibbonX - where does the XML reside?

    In an earlier <a href="http://blogs.msdn.com/jensenh/archive/2006/03/23/558886.aspx">post</a> I briefly mentioned where the XML resides in COM and VBA solutions.  Basically, Office asks your COM add-in for XML via getCustomUI method on the IRibbonExtensibility interface, that you implement in your Connect class.  Your COM add-in can get the XML file from anywhere -- it could be part of the add-in or on a share somewhere -- and you can return it to us when your add-in loads.   It's up to you.  You'll see more documentation about this with Beta 2.

    5) Yes, as far as conflicts go, the last add-in wins.

    6) Protection bits on customization?

    With the new UI there are no protection bits. End users can move your custom buttons to the QAT. Since the Ribbon will be customized by your add-in, you still will have a 'default' command set.

    7) Application.CommandBars("Menu Bar").Controls("File").Controls("Print...") doesn't localize.

    Yes, you are right, this only works in English.  We fixed this problem with RibbonX -- you used the fixed, named control IDs and they work across languages.  We are publishing the Command IDs, and in the Beta 2 build they will be visible in the QAT customization dialog, when you hover over controls.

    Thanks for the interest. I'll ping the security team to see if they want to chime in as well. ;)

  • Anonymous
    May 22, 2006
    >> Yes, as far as conflicts go, the last add-in wins.
    Last installed addin, last loaded addin, alphabatically last or something else ..?

    I have two questions with me my TLs my EEs struggled like anything.. finally with the answer that "predection of addin call is non-deterministic by nature", so please let me know what changed with 2007.

    1) If i want' to insure that my addin gets called last what do i do ?
    2) Is there anything like AfterSave in 2007 , i have seen many issues where people are trying to use something like that, although we did gave them workarounds  but they were non dependable as such.

  • Anonymous
    June 10, 2006
    Thanks!!! http://www.insurance-top.com/company/">http://www.insurance-top.com/company/ auto site insurance. [URL=http://www.insurance-top.com]home insurance[/URL]: car site insurance, The autos insurance company, compare car insurance. Also [url=http://www.insurance-top.com]cars insurance[/url] from website .

  • Anonymous
    December 29, 2007
    PingBack from http://cars.oneadayvitamin.info/?p=991

  • Anonymous
    December 31, 2007
    PingBack from http://restaurants.247blogging.info/?p=1260

  • Anonymous
    October 27, 2008
    PingBack from http://mstechnews.info/2008/10/the-office-2007-ui-bible/

  • Anonymous
    June 14, 2009
    PingBack from http://adirondackchairshub.info/story.php?id=2594

  • Anonymous
    June 15, 2009
    PingBack from http://unemploymentofficeresource.info/story.php?id=10378