RibbonX for Dummies: Chapter 6 (Part 1) (
Page 4 of 4 )
Working with dates
Unlike other elements in this application, dates can appear anywhere in the document and the user will very likely need more than the current date. However, the application can still help the user with correctness, formatting, and placement. The application accomplishes correctness by displaying several common date options as part of the split button, as shown in Figure 6-7. Notice that in this case (unlike the Style split button mentioned earlier), the default option does have an actual purpose: inserting today’s date.
Figure 6-6: Styles play an important part in positioning data using this application.
Figure 6-7: Effective use of menu options can serve to reduce user error.
The options don’t have icons, for the most part, because they insert the date directly. After all, the application can make the required calculation for the user. However, the View Calendar option does display a special selection dialog box, as shown in Figure 6-8, because the user must choose a date. The use of this dialog box does, however, ensure that the result is a correctly formatted date, and tends to reduce errors (such as choosing a Sunday when your business isn’t open on Sunday).
No matter which date option the user selects, the application displays a positioning dialog box, as shown in Figure 6-9. The positioning dialog box offers the user three alternatives for inserting the date. The code automatically places the date and formats it as required after the user makes a selection. The callbacks for the date are relatively simple. All you really need to do, in most cases, is add the appropriate time to the existing DateTime.Date property. Listing 6-6 shows the code for the most complex example, figuring out precisely one month from the current date.
Figure 6-8: Use a calendar control to reduce user errors.
Figure 6-9: Careful planning ensures that the workflow isn’t broken by formatting issues.

Listing 6-6: Creating, Formatting, and Inserting a Date
'Callback for NextMonth onAction
Sub DateNextMonth(control As IRibbonControl)
' Obtain the current month
Dim Month As Integer
Month = DateTime.Month(DateTime.Now) + 1
' Create a date string.
Dim DateStr As String
DateStr = _
CStr(Month) + “/” + _
CStr(DateTime.Day(DateTime.Now)) + “/” + _
CStr(DateTime.Year(DateTime.Now))
' Create a new date.
Dim NewDate As Date
NewDate = DateTime.DateValue(DateStr)
' Insert the required date.
InsertDate CStr(NewDate)
End Sub
Sub InsertDate(TheDate As String)
' Lets the user set the date position.
Dim GetPosition As ChoosePosition
Set GetPosition = New ChoosePosition
' Obtain the current pane object.
Dim CurrPane As Pane
Set CurrPane = Application.ActiveWindow.ActivePane
' Get the user input.
GetPosition.Show
' Determine the date position.
Select Case GetPosition.Result
Case PositionResult.Left
' Add a Date Left paragraph.
CurrPane.Selection.InsertParagraph
CurrPane.Selection.Style = “Date Left”
' Insert the date.
CurrPane.Selection.EndKey
CurrPane.Selection.Text = TheDate
CurrPane.Selection.GoToNext wdGoToLine
Case PositionResult.Right
' Add a Date Left paragraph.
CurrPane.Selection.InsertParagraph
CurrPane.Selection.Style = “Date Right”
' Insert the date.
CurrPane.Selection.EndKey
CurrPane.Selection.Text = TheDate
CurrPane.Selection.GoToNext wdGoToLine
Case PositionResult.Inline
' Insert the date.
CurrPane.Selection.Text = TheDate
CurrPane.Selection.EndKey
Case PositionResult.Cancel
' Don’t do anything.
End Select
End Sub
The example begins by obtaining the current month, adding a month to it, and using the result to create a date string. It then uses the DateTime. DateValue() method to create a date and pass it to the InsertDate Sub. The InsertDate Sub begins by creating and displaying the Position the Date dialog box, as shown in Figure 6-9. After the user makes a choice, the code inserts the date using one of three methods. The first two methods create a new paragraph and assign a style to it. All three methods insert the date at the insertion point.
Adding the sender
From the user’s perspective, the buttons in the Sender group act much like those in the Recipient group. The user clicks Your Name first, and then clicks any of the other informational buttons as needed. All of the output is positional, like the recipient information. In this case, the application assumes that the user is moving from left to right; it inserts the sender name at the end of the document because that’s the next position in the workflow to place data. You could base the position on other criteria, such as placing the username immediately after the date. Using unique styles for each data type means you have flexibility in determining where to place the next data item.
The code is different from the recipient code for a number of reasons. The first reason is that you already know the user’s name because it appears as part of the Word configuration information. Accessing the name and address is very easy, as shown in Listing 6-7. However, since Word only provides access to the user’s name and address, you must supply some other means of obtaining other user information such as telephone number and office location. The example relies on a user Outlook address-book entry, which seems a reasonable choice because the user will probably want to add his or her address entry to e-mails and other Outlook objects.
Listing 6-7: Obtaining the Sender Information
'Callback for SendName onAction
Sub SenderName(control As IRibbonControl)
' Obtain the current pane object.
Dim CurrPane As Pane
Set CurrPane = Application.ActiveWindow.ActivePane
' Go to the end of the document.
CurrPane.Selection.GoTo wdGoToLine, wdGoToLast
' Add a Sender Name paragraph.
CurrPane.Selection.InsertParagraph
CurrPane.Selection.Style = “Sender Name”
' Insert the sender’s name.
CurrPane.Selection.EndKey
CurrPane.Selection.Text = Application.UserName
CurrPane.Selection.GoToNext wdGoToLine
' Look up the sender in Outlook. Begin with the
' first name and go from there.
Dim CheckSender As AddressEntry
Set CheckSender = _
Outlook.Application.Session.AddressLists. _
Item(1).AddressEntries.GetFirst
' Check the name.
If CheckSender.Name = Application.UserName Then
Exit Sub
End If
' If this isn’t the right user, keep searching.
For Each CheckSender In _
Outlook.Application.Session.AddressLists. _
Item(1).AddressEntries
' Check the entry name.
If CheckSender.Name = Application.UserName Then
Exit For
End If
Next
' Determine whether we have an ID to use.
If Not CheckSender Is Nothing Then
Set ThisSender = _
Outlook.Session.GetRecipientFromID( _
CheckSender.ID)
End If
End Sub The code begins by inserting the paragraph at the end of the document and setting it to use the Sender Name style. The code then accesses the user’s name with the Application.UserName property. Using this approach means that you don’t have to ask the user for any of the required sender information for a properly configured system.
As previously mentioned, however, you can’t obtain the rest of the sender information that the application requires from Word because Word tracks only the username and address. The code creates a new Outlook AddressEntry object, CheckSender, to locate the user’s information in the Outlook address list.
The Outlook.Application.Session.AddressLists.Item(1). AddressEntries collection contains all of the AddressEntry objects for the selected address book (as indicated by the Item(1) index). The For Each loop checks each collection entry in turn until it locates an entry where the Name property matches the Application.UserName.
The check depends on a user creating an address entry with precisely the same name as his or her username in Word. This is a potential point of failure, and you’ll probably want to set up configuration policies to guard against it.
When the code finds an address entry, it creates a Recipient object, ThisSender, that functions precisely the same as ThisRecipient. The code uses the GetRecipientFromID() method to obtain the Recipient object. Notice that it relies on the CheckSender.ID property to obtain the required entry ID value.
Greeting the recipient and adding a signature
The Greeting and Signature groups are the easiest part of this application to understand. All they do is insert standard text snippets, using the same techniques as many of the other entries described for this article.
In fact, some developers would probably question the need for these entries. However, they serve an important role when you create a workflow application. It’s essential to maintain the perception that everything the user needs appears on the Ribbon. In addition, your organization may actually have standardized greetings and signatures that it requires authors to use.
Of course, you might also make the argument that these bits of text could actually appear in the template. That’s true if you have only one greeting and signature, but again, you run into the problem of maintaining the workflow. You don’t want the user to see the text until it’s time to add it to the document. Consequently, adding the two groups and their associated controls to the Ribbon is the best way to approach the problem.
Considering the CC, routing, and approval requirements
The example has shown many kinds of entries so far. When you select a style, the application removes all of the entries from the previous style first, and then adds the new style changes. Some elements are positional and others appear at the current insertion point. There are good reasons to use each of these entries. The CC, Routing Slip, and Get Approvals check boxes show another kind of entry. In this case, the entries must always appear at the bottom of the document. In addition, the user will expect some type of on/off functionality. The application provides all this required functionality.
The basic functionality requires two callbacks. The first callback performs the application-specific tasks of adding the required data to the document. The second callback changes the appearance of the check box on the Ribbon. Remember that you can’t change the check yourself, but must ask Office to perform the task for you. Listing 6-8 shows the code for these two callbacks. Although this code shows the CC check box, the other two check boxes work the same.
Listing 6-8: Adding CC Information to the Document
'Callback for RtCC onAction
Sub RouteCC(control As IRibbonControl, _
pressed As Boolean)
' Obtain the current pane object.
Dim CurrPane As Pane
Set CurrPane = Application.ActiveWindow.ActivePane
' Perform an action based on the current value.
If CCPressed Then
' Create a search variable.
Dim DoSearch As Find
Set DoSearch = CurrPane.Selection.Find
' Look for the lines with the correct style.
DoSearch.Style = “CC”
' Look everywhere in the document.
DoSearch.Wrap = wdFindContinue
DoSearch.Text = “”
' Keep looking for CC lines until they’re gone.
While DoSearch.Execute()
' Remove the CC lines.
CurrPane.Selection.Delete
Wend
' Add a CC.
Else
' Go to the end of the document.
CurrPane.Selection.GoTo wdGoToLine, wdGoToLast
' Add a CC paragraph.
CurrPane.Selection.InsertParagraph
CurrPane.Selection.Style = “CC”
' Insert the default text.
CurrPane.Selection.EndKey
CurrPane.Selection.Text = “CC: “
CurrPane.Selection.EndKey
End If
' Change the pressed value.
CCPressed = pressed
End Sub
'Callback for RtCC getPressed
Sub RoutePressedCC(control As IRibbonControl, _
ByRef returnedVal)
' Return the current pressed status.
returnedVal = CCPressed
End Sub
The RouteCC code begins by obtaining a reference to the current document pane. It then detects whether the user has checked the check box or cleared it. When the user checks the check box, the code searches for the end of the document and adds the required text. Likewise, when the user clears the check box, the code locates the existing entry by style, and removes it.
Notice that the code handles the situation where the user has created multiple paragraphs with the required style. The user could create such an entry accidentally or to separate the list of names.
One mistake that you might make when searching for a style is forgetting to clear the text entry. You must set DoSearch.Text = “” to clear the text or the search could fail in unexpected ways. Worse yet, it could succeed in unexpected ways and produce odd results without displaying an error message.
The RoutePressedCC code may almost look too simple when contrasted to other callbacks for this application. Check Listings 6-1 through 6-3 for comparison purposes. However, unlike the Style group, the Ribbon knows that the user has interacted with the CC check box, so you can simply add a getPressed attribute, as shown here:
<group id=”Routing” label=”Routing”>
<checkBox id=”RtCC” label=”CC”
onAction=”RouteCC”
getPressed=”RoutePressedCC”/>
<checkBox id=”RtRouting” label=”Routing Slip”
onAction=”RouteRouting”
getPressed=”RoutePressedRouting”/>
<checkBox id=”RtApproval” label=”Get Approvals”
onAction=”RouteApproval”
getPressed=”RoutePressedApproval”/>
</group>
Simply returning the current state of the check box is enough in this case. Office doesn’t require that you invalidate the control separately or perform any other odd programming tasks. However, you do have to consider the startup state of the Ribbon for these controls, which means adding code to the OnLoad callback. Listing 6-9 shows the code used to detect the current checkbox state.
Listing 6-9: Determining the Initial Checkbox State
'Create a search variable.
Dim DoSearch As Find
Set DoSearch = ThisSelect.Find
' Preset the routing checkboxes.
CCPressed = False
RoutingPressed = False
ApprovalPressed = False
'Determine the state of each routing checkbox.
DoSearch.Wrap = wdFindContinue
DoSearch.Text = “”
DoSearch.Style = “CC”
If DoSearch.Execute() Then
CCPressed = True
End If
DoSearch.Wrap = wdFindContinue
DoSearch.Text = “”
DoSearch.Style = “Routing”
If DoSearch.Execute() Then
RoutingPressed = True
End If
DoSearch.Wrap = wdFindContinue
DoSearch.Text = “”
DoSearch.Style = “Approval”
If DoSearch.Execute() Then
ApprovalPressed = True
End If
The example uses the same technique as always to locate particular bits of information in the document. The content doesn’t matter, but the style does because the style determines the information type. The code begins by creating a search and setting each of the global check-box status variables to false (meaning the user hasn’t checked them).
When the code finds a paragraph of the right type, it sets the associate variable to true. Notice that each checkbox has its own style so the code doesn’t confuse the entries. In addition, you must reset the DoSearch.Wrap property to wdFindContinue every time you begin another search.