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.