Hyderkhan asked me how to automate the Sandbar-Ribbon controls. While the old version of these controls is dealt with in the .Net add-in extensibility help files, I decided to address the subject for two reasons:
A. This could be a good chance to explain how to explore and automate unknown custom .Net controls.
B. I absolutely hate using extensibilities. I think they cost too much to write on your own, and prewritten extensibilities always lack the needed depth and scope to completely automate the application.
Even though I’ll be demonstrating how to automate the Sandbar-Ribbon, the procedure described could help you deal with any set of custom controls. I’ve written a small demo-app for the automation, but this is only to have tooltips. The code snippets will work just as well with the out-of-the-box Sandbar-Ribbon demo. So, download the demo-app, the Sand-Ribbon Demo (the app won’t work without the demo installation), and let’s go.
Set the goals
The first thing you should do is set a list of goals you want to achieve (i.e. features to automate). It doesn’t have to be complete or exhaustive in any way – one or two features will make a nice starting point. With the goals in mind, we could conduct a much more focused exploration, and learn how to automate the control more quickly and effectively.
In our case, the two natural automation goals are to navigate to a specific tab, and to click a desired button.
Explore the controls
The first actual thing I do with a custom control is to explore it. Even when I have the source-code documentation (more on that later), I learn the most from hands-on exploration. So, I first do a superficial sweep with our friend, the object spy.
Since it’s a custom control, the TO tab will tell us almost nothing, so we head straight to the RO tab. There, we search for blank properties. Yes, you heard me– the most valuable properties a custom .Net control can have are those which appear as blanks. The reason we’re so interested in them is that the blank value in the object-spy is actually a complex object (usually anyway). And as you’ll come to see, complex objects are the best windows into a custom control’s heart.
We peek in our goals list, and see that the first order of business is tab navigation, which means will have to loop through the ribbon’s tabs and find the one we need. So, one blank property which pops right up is Tabs. Since the control can host many tabs, this will probably be a collection, storing the Tab objects, enumerating them, and allowing access to their inner structure.
Unfortunately, the Object-Spy is too limited, and can’t drill down the inner object structure. So, we immediately turn to our friend, the debug view. We can pause the script, and use the debug view to drill down the object structure (though there’s no auto-complete, so we’ll have to guess it as we go along).
Working with collections
Accessing a collection object is usually done by oCollection.Item(iIndex) / oCollection.Items.Item(iIndex), or similar syntax. A quick check through the debug view turns out that oCollection.Item(iIndex) is the correct method for this collection. Now we only need to know how many items the collection has, and then we could begin to write our loop code.
Usually the collections holding tabs, combo-items, buttons etc. will have a .Count property, which can tell us how many items to expect. Unfortunately, the debug viewer tells us the collection has no such property. So, this means that looping through that tabs will not be a graceful For loop, but a rather ugly While loop, as follows:
bFound = False ' Did we find the needed tab?
i = 0
'If we overshot the number of tabs, the next one won’t be an object
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
'Search Code Here
i = i+1
Wend
Knowing when to stop
OK, it’s ugly, but we can live with it. The question is, when we loop through the tabs, how can we tell we’ve reached to correct one? The most common search criteria is by tab name, so let’s try to find how to access the header of the tab. Since we don’t have a debug auto-complete, we have to guess the property name. The usual suspected are .Text, .Title, .Name, etc.
This can be a good place to open the source-code documentation (if you have it), and look for the property name, instead of guessing it. The Sand-Ribbon demo comes with complete object properties scheme, but I’ll keep mentioning how I would try to do things without it. Anyway, the correct property is .Text.
A quick check via the debug view shows a problem. The first tab name is "Tab 1″, but the .text property is "&Tab 1″. This is actually pretty common in tabs and menu controls – their name can holds short-cut information and other junk. But that’s OK, we just have to take that into consideration, and we could write our tab search loop:
i = 0
Set oTab = oRibbon.Object.Tabs.Item(i) 'Just to make things clearer
'Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
'The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
Switching the tab
OK, now we got the index of the relevant tab. How can we do the actual tab switching? We could use several methods – calculating the X,Y coordinates of the header and clicking it, use keyboard shortcuts, or manipulate the RO properties of the ribbon. This time (mostly for demonstration), we’ll go with RO manipulation.
Usually, a custom control would have SelectedIndex, SelectedItem or SelectedTab property, which could do the trick. The Sandbar-Ribbon control does have a SelectedTab property, but it seems to hold the actual tab object, not its index. Likewise, the TabIndex property doesn’t seem to do the trick either (changing it has no effect of the application).
So, it’s time to leave the Properties tab of the object-spy, and proceed to the Methods tab. There we can find the AdvanceSelectedTab method, which looks promising. A little more tweaking and experimenting, and we get the full blackbox method for switching tabs:
Function SelectRibbonTab(oRibbon, sTabName)
Dim iCurrentTab
Dim i
Dim oTab
Dim bFound
Dim iSteps
iCurrentTab = oRibbon.Object.Tabs.IndexOf(oRibbon.Object.SelectedTab)
'What tab are we in?
i = 0
Set oTab = oRibbon.Object.Tabs.Item(i)
'Start searching for tab with name = sTabName
bFound = False
While (IsObject(oRibbon.Object.Tabs.Item(i))) And (bFound = False)
If Replace(oTab.Text, "&","") = sTabName Then bFound = True
'The replace is there because the headers have short-keys information
i = i+1
If IsObject(oRibbon.Object.Tabs.Item(i)) Then _
Set oTab = oRibbon.Object.Tabs.Item(i)
Wend
i=i-1 'We overpromoted i
If bFound = True Then
iSteps = (i - iCurrentTab) 'Where do we need to go?
For i = 1 to abs(iSteps) 'iSteps might be negative
'AdvanceSelectedTab promotes by 1/-1 only
oRibbon.Object.AdvanceSelectedTab iSteps
Next
End If
Set oTab = Nothing 'Release pointers
SelectRibbonTab = bFound 'Return pass/Fail
End Function
And now all that’s left is to actually call the function:
Set oRibbon = SWFWindow("swfname:=AdvancedQTPRibbon").SWFObject("swfname:=ribbon1″)
Call SelectRibbonTab(oRibbon, "Tab 1″)
Next time we’ll implement our second goal: pressing a specific button within a tab.
Posted in .Net



Yaron Assa







April 21st, 2008 at 8:42 am
I am getting error while installing the Ribbon demo application as “Application has generated an exception that could not be handled”. Can you please upload the new same apps.
April 28th, 2008 at 1:59 pm
[…] to immediately expand any node in a DevExpress .Net tree.It’s a wonderful implementation of .Net custom controls analysis and manipulation, and involves a cool little hack involving node […]
December 18th, 2008 at 7:43 am
[…] can read more about RO manipulating techniques in our article about automating custom controls. Was this article useful? Rate […]