Sunday, January 24, 2010

ActiveReports6 : Viewer ToolBar Secrets.

One of the cool new features of ActiveReports6 is the updated End User Designer toolbar architecture. Now the public API exposes the methods which allow to get the designer's toolbar groups and they are ToolStrips. Which opens the wide capabilities of the toolbar customization. That is fine but the question which we recently got from the clients is "What about ActiveReports6 Windows Viewer toolbar?". The way to customize the viewer toolbar seems to be unchangeable since from ActiveReports for .NET 1.0 times! ActiveReports still allows to customize the viewer toolbar using the "Toolbar API" only. Toolbar API is not the best solution ever, it is pretty limited. Fortunately a bit of digging around ActiveReporrs6 viewer internals can provide us with more comfortable way of toolbar customization.

Viewer Toolbar is actually ToolStrip!
Let's see which child controls are hosted by ActiveReports6 Viewer control. Here is the simple code which performs that:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
foreach(Control child in viewer.Controls)
   Debug.WriteLine(string.Format("name:{0}, type{1}", child.Name, child.GetType()));

That code emits the following output:

name:, type#op.#7s
name:tocSplitter, typeSystem.Windows.Forms.Splitter
name:TableOfContents, typeDataDynamics.ActiveReports.Viewer.TableOfContents
name:, typeSystem.Windows.Forms.ToolStrip

The first control is one which shows the report pages, the third control is one which shows the TOC, the second control is the separator between the 1st and 3rd controls and 4th control is...yes, that is viewer toolbar! In ActiveReports6 the Windows Viewer toolbar is internally implemented using ToolStrip API. Cool, let's write the simple extension method which would provide us with access to viewer's Toolbar which is the ToolStrip:

public static class ViewerHelper
   public static ToolStrip CreateToolStrip(this Viewer viewer)
      foreach (var control in viewer.Controls)
         if (control is ToolStrip)
            return (control as ToolStrip);
      return null;

Now we can access to viewer's toolbar using the code like:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();

Let's see how we can implement several customization scenarios using this "secret" feature.

Scenario #1 - Composing the complex viewer toolbar
The following code builds the top panel of the tool strip container by putting File Menu and Viewer Toolbar together:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
var ms = new MenuStrip {Dock = DockStyle.Top};
var fileMenu = new ToolStripMenuItem("File");
var fileOpenMenu = new ToolStripMenuItem("Open", null, delegate{/*opening RDF file happens here*/});
var fileExitMenu = new ToolStripMenuItem("Exit", null, delegate {Application.Exit(); });
ms.MdiWindowListItem = fileMenu;
toolStripContainer1.TopToolStripPanel.Join(viewerToolStrip, 2);

Scenario #2 - Customizing the native toolbar items
The following code removes the text from Print, Backward and Forward buttons and also removes Annotations button from the viewer toolbar:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
viewerToolStrip.Items[2].Text = viewerToolStrip.Items[20].Text = viewerToolStrip.Items[21].Text = string.Empty;

Advanced scenario.
In the last code sample I used the magic numbers 2,20,21 and 23 to access to the toolbar items. How do I know those magic numbers? I got them by running the simple code which goes through the toolbar items and outputs the index, text and type of each item. Here is the resulting list:

0 Table Of Contents:System.Windows.Forms.ToolStripButton
1 :System.Windows.Forms.ToolStripSeparator
2 &Print...:System.Windows.Forms.ToolStripButton
3 :System.Windows.Forms.ToolStripSeparator
4 Copy:System.Windows.Forms.ToolStripButton
5 :System.Windows.Forms.ToolStripSeparator
6 &Find:System.Windows.Forms.ToolStripButton
7 :System.Windows.Forms.ToolStripSeparator
8 Single Page View:System.Windows.Forms.ToolStripButton
9 Multiple Page View:#is.#5ml
10 Continuous Scroll:System.Windows.Forms.ToolStripButton
11 :System.Windows.Forms.ToolStripSeparator
12 Zoom Out:System.Windows.Forms.ToolStripButton
13 Zoom In:System.Windows.Forms.ToolStripButton
14 100 %:System.Windows.Forms.ToolStripComboBox
15 :System.Windows.Forms.ToolStripSeparator
16 Previous Page:System.Windows.Forms.ToolStripButton
17 Next Page:System.Windows.Forms.ToolStripButton
18 1/1:#is.#rs+#U.#ss
19 :System.Windows.Forms.ToolStripSeparator
20 &Backward:System.Windows.Forms.ToolStripButton
21 &Forward:System.Windows.Forms.ToolStripButton
22 :System.Windows.Forms.ToolStripSeparator
23 Annotations:System.Windows.Forms.ToolStripDropDownButton

As you can see most of the toolbar items are buttons and separators. There are two mysterious items though in the list. Item 9 is one which allows to set the mode of multi-page view in the viewer. I will not focus on this element for now.
Item 18 is the text box which shows the current page number and the page total of the report which is shown by viewer. Access to the properties and events of that text box is sometimes needed. As you can see this toolbar item is the instance of some internal class of Viewer assembly. We can't access to the properties and events of that internal class, but we can try cast it to ToolStripTextBox:

var viewer = new DataDynamics.ActiveReports.Viewer.Viewer();
ToolStrip viewerToolStrip = viewer.CreateToolStrip();
var pagesEditor = viewerToolStrip.Items[18] as ToolStripTextBox;
pagesEditor.TextChanged += delegate { Debug.WriteLine("The current page was changed"); };

..and that works.

The features which are described in this article are not officially supported. They are not tested and documented. They can change in further versions. Use them at your own risk.


  1. Great! Maybe you can write a different article on how to do the same with Data Dynamics Reports?

  2. Unfortunately the only way to customize the toolbar of the viewer and designer in DDR is total replacing of the standard toolbar with the custom one. We don't use the .NET ToolStrip for DDR controls toolbars/menus.