MindFusion WinForms Programmer's Guide
ReportEditor Control

This ReportEditor control can be used to create, open, save, preview and print MindFusion.Reporting reports. It can be integrated in any WinForms application and provide it with the ability to edit reports. This can be useful for full-scale reporting applications as well as smaller applications that let the end users modify existing reports in a visual environment. For example, an application hosting the ReportEditor control can be used to allow end-users to adjust preexisting report templates according to their needs.

 Note

The ReportEditor class is located in the MindFusion.Reporting.Editor.dll assembly.

 Important

The minimum required "Target framework" of applications using the Report Editor is ".NET Framework 4".

The Editor Components

The editor consists of four major components - designer area, property grid, toolbox and preview panel. The following image illustrates the default appearance of the ReportEditor control.

The designer area is located in the middle with the properties grid and the toolbox located to the left and right respectively. The preview panel can be switched to from the tab at the bottom of the control. All of those components can be hidden through the ShowPropertiesWindow, ShowToolboxWindow and ShowPreview properties respectively. To programmatically toggle between the Design and Preview tabs, use the ActiveTabPage property.

Working With the Editor

User Experience

Because the ReportEditor control is based on the same components and services that are used for implementing the design-time support of MindFusion.Reporting reports in Visual Studio, creating reports using the ReportEditor control and creating reports in Visual Studio is nearly identical from user's point of view. To learn the basics about how to design reports interactively, check the Report Designer topic.

Creating, Opening and Saving Reports

The Report instance displayed inside the editor can be accessed through the Report property. Note, that this property is read-only. Because of this, to open an existing report for editing, first save the report using its SaveToXml method, then open the report inside the editor through the OpenReport method. Opening the report directly by calling LoadFromXml on the Report itself is not advisable. More generally, modifying the edited report programmatically in any way needs to be done with caution. Check the Modifying the Edited Report Programmatically section at the end of this topic for tips how to do this.

To create a new report or save the current report, use the NewReport and SaveReport methods respectively. To check if the current report is modified since it was opened or last saved, use the IsReportDirty property. This property is automatically updated whenever the report is changed, opened or saved through the editor.

Commands

The editor supports a variety of standard menu commands that can be invoked through the InvokeCommand method. For example, to align the left sides of the report items currently selected in the editor, invoke the standard command AlignLeft using the following code:

C#  Copy Code

reportEditor.InvokeCommand(StandardCommands.Align);

Visual Basic  Copy Code

reportEditor.InvokeCommand(StandardCommands.Align)

Alternatively, commands can be invoked through the MenuCommandService, which can be obtained from the editor through its GetService method.

The following table lists the majority of the standard menu commands supported by the editor:

CommandID

Description

AlignBottom

Aligns the bottom sides of the currently selected components.

AlignHorizontalCenters

Aligns the horizontal centers of the currently selected components.

AlignLeft

Aligns the left sides of the currently selected components.

AlignRight

Aligns the right sides of the currently selected components.

AlignTop

Aligns the top sides of the currently selected components.

AlignVerticalCenters

Aligns the vertical centers of the currently selected components.

BringToFront

Brings the currently selected components at the top of the z-order.

CenterHorizontally

Centers the currently selected components horizontally inside their respective parents.

CenterVertically

Centers the currently selected components vertically inside their respective parents.

Copy

Copies the selected components to the clipboard.

Cut

Cuts the selected components.

Delete

Deletes the selected components.

HorizSpaceConcatenate

Removes the horizontal spacing between the currently selected components. The primary selected component remains stationary.

HorizSpaceDecrease

Decreases the horizontal spacing between the currently selected components. The primary selected component remains stationary.

HorizSpaceIncrease

Increases the horizontal spacing between the currently selected components. The primary selected component remains stationary.

HorizSpaceMakeEqual

Makes the horizontal spacing between the currently selected components equal. The leftmost and rightmost components remain stationary.

Paste

Pastes the components in the clipboard.

PropertiesWindow

Displays and focuses the Properties window.

Redo

Redoes the last undone operation.

SendToBack

Sends the currently selected components at the bottom of the z-order.

SizeToControl

Makes the size of the currently selected components the same as the size of the primary selected component.

SizeToControlHeight

Makes the height of the currently selected components the same as the height of the primary selected component.

SizeToControlWidth

Makes the width of the currently selected components the same as the width of the primary selected component.

Undo

Undoes the most recent operation.

VertSpaceConcatenate

Removes the vertical spacing between the currently selected components. The primary selected component remains stationary.

VertSpaceDecrease

Decreases the vertical spacing between the currently selected components. The primary selected component remains stationary.

VertSpaceIncrease

Increases the vertical spacing between the currently selected components. The primary selected component remains stationary.

VertSpaceMakeEqual

Makes the vertical spacing between the currently selected components equal. The topmost and bottommost components remain stationary.

In addition, the editor supports the custom menu commands from the MenuCommands class.

Associating UI Elements with Commands

The report editor provides means to listen to changes of the supported commands. This can be useful when a UI element in the application needs to be associated with a specific command. When the command not enabled, the element needs to be automatically grayed out (or disabled) to indicate this. For example, a button in the application that triggers the Undo command should be disabled when Undo is not available.

To listen to command changes, pass an instance of a class implementing the ICommandListener interface (along with the ID of the respective command) to the RegisterCommandListener method. When the state of the command with the specified ID changes, the specified listener is notified through its UpdateState method.

The implementation of the ICommandListener interface depends on the type of the user elements that need to be updated. For example, let's examine an application, which provides a ToolStripButton components to trigger various commands. Here is a sample implementation of the ICommandListener interface for this application:

C#  Copy Code

private class ToolStripItemCommandListener : ICommandListener
{
    public CommandListener(ToolStripItem item)
    {
        this.item = item;
    }

    public void UpdateState(bool enabled, bool visible)
    {
        item.Enabled = enabled;
        item.Visible = visible;
    }

    private ToolStripItem item;
}

Visual Basic  Copy Code

Private Class CommandListener
    Implements ICommandListener

    Public Sub New(item As ToolStripItem)
        Me.item = item
    End Sub

    Public Sub UpdateState(enabled As Boolean, visible As Boolean) Implements ICommandListener.UpdateState
        item.Enabled = enabled
        item.Visible = visible
    End Sub

    Private item As ToolStripItem

End Class

Let's assume that undoButton is an existing ToolStripButton inside the application that should trigger the Undo command. Then the following code will associate undoButton with the Undo command:

C#  Copy Code

reportEditor.RegisterCommandListener(StandardCommands.Undo, new CommandListener(undoButton));

Visual Basic  Copy Code

reportEditor.RegisterCommandListener(StandardCommands.Undo, New CommandListener(undoButton))

Now, when Undo is available, the button will be enabled, when Undo is not available, the button will be disabled.

Registering Data Sources

The data sources represent data objects that the report elements can be bound to. Any object can be used as a data source, including DataSet, array of business objects, custom objects, and so on. The data sources can be added to the editor through one of the AddDataSource overloads. The newly added data sources appear as components in a small tray window at the bottom of the designer area and can be subsequently selected inside the property grid as DataSource for DataRange and Chart report items. Adding a data source to the editor does not cause the edited report to become dirty nor does it change the report in any way, until the data source is actually selected as DataSource for a report item. Creating a new report (through NewReport) or closing the current report (through CloseReport) do not cause the registered data sources to disappear.

When a data source is added to the editor it is associated with a unique name. This name is serialized when the report is saved and is used during deserialization to identify the data source an item is bound to. The name of the data source can be specified explicitly by using the AddDataSource overload or generated automatically by the editor by using the AddDataSource overload. Both overloads return an IComponent object, which is the component representing the data source inside the editor. In the case when the editor automatically generated a name for the data source, this name can be obtained through the IComponent.Site.Name property of the returned object.

DataRange objects can be automatically created from registered data sources. To do this, right-click on a page inside the designer area and select 'Create DataRange from Data Source...' item in the context menu. This will display a dialog with all registered data sources and their fields or properties. The user can select the members to include in the generated DataRange.

Modifying the Report Programmatically

The edited report should not be modified directly. More specifically, new items should not be created through constructors and added to the report or to item containers, and existing items should not be removed or modified through property assignment. Direct modifications are not tracked by the undo engine, do not update the designer surface and the UI elements automatically, and will often cause exceptions. For example, the following modifications are not recommended:

C#  Copy Code

reportEditor.Report.Pages.Add(new Page());
reportEditor.Report.Pages[0].Items[0].Location = new Location(10, 10);
reportEditor.Report.Pages.RemoveAt(0);

Visual Basic  Copy Code

reportEditor.Report.Pages.Add(New Page())
reportEditor.Report.Pages[0].Items[0].Location = New Location(10, 10)
reportEditor.Report.Pages.RemoveAt(0)

Creating Items

New items should be created through the IDesignerHost service. A reference to this service can be obtained from the editor by calling the GetService method. The following code illustrates how to create a new page:

C#  Copy Code

IDesignerHost designerHost = reportEditor.GetService<IDesignerHost>();
Page newPage = designerHost.CreateComponent(typeof(Page)) as Page;

Visual Basic  Copy Code

Dim designerHost As IDesignerHost = reportEditor.GetService(Of IDesignerHost)()
Dim newPage As Page = CType(designerHost.CreateComponent(GetType(Page)), Page)

Adding Items

Adding new item to a container should be wrapped in calls to the OnComponentChanging and OnComponentChanged methods of the IComponentChangeService. A reference to this service can be obtained through the GetService method as well. The following code illustrates how to add the previously created page instance to the report:

C#  Copy Code

IComponentChangeService componentChange = reportEditor.GetService<IComponentChangeService>();
componentChange.OnComponentChanging(reportEditor.Report, null);
reportEditor.Report.Pages.Add(newPage);
componentChange.OnComponentChanged(reportEditor.Report, null, null, null);

Visual Basic  Copy Code

Dim componentChange As IComponentChangeService = reportEditor.GetService(Of IComponentChangeService)()
componentChange.OnComponentChanging(reportEditor.Report, Nothing)
reportEditor.Report.Pages.Add(newPage)
componentChange.OnComponentChanged(reportEditor.Report, Nothing, Nothing, Nothing);

Modifying Items

Items can be modified by wrapping the property assignment in an OnComponentChanging/OnComponentChanged block, similarly to how items are added. Alternatively, items can be modified through PropertyDescriptor objects. The following code demonstrates how to change the location of an existing item using the second approach:

C#  Copy Code

PropertyDescriptor locationProperty = TypeDescriptor.GetProperties(item)["Location"];
locationProperty.SetValue(item, new Location(0, 0));

Visual Basic  Copy Code

Dim locationProperty As PropertyDescriptor = TypeDescriptor.GetProperties(item)("Location")
locationProperty.SetValue(item, New Location(0, 0))

Removing Items

Items are removed similarly to how they are added. The removed items also need be destroyed through the DestroyComponent method of the IDesignerHost service. The following snippet removes the first page of the edited report:

C#  Copy Code

IDesignerHost designerHost = reportEditor.GetService<IDesignerHost>();
IComponentChangeService componentChange = reportEditor.GetService<IComponentChangeService>();
componentChange.OnComponentChanging(reportEditor.Report, null);
Page removedPage = reportEditor.Report.Pages[0];
reportEditor.Report.Pages.RemoveAt(0);
componentChange.OnComponentChanged(reportEditor.Report, null, null, null);
designerHost.DestroyComponent(removedPage);

Visual Basic  Copy Code

Dim designerHost As IDesignerHost = reportEditor.GetService(Of IDesignerHost)()
Dim componentChange As IComponentChangeService = reportEditor.GetService(Of IComponentChangeService)()
componentChange.OnComponentChanging(reportEditor.Report, Nothing)
Dim removedPage as Page = reportEditor.Report.Pages(0)
reportEditor.Report.Pages.RemoveAt(0)
componentChange.OnComponentChanged(reportEditor.Report, Nothing, Nothing, Nothing)
designerHost.DestroyComponent(removedPage)

 Note

Keep in mind that reports need to have at least one page. Removing the last page of the report will result in an exception.

Using Transactions

Each modification to the report results in an undo record i the undo history. Some modifications produce even more than one record due to various indirect modifications, such as property initializations of newly created objects. To wrap up several modifications as a single undo record, they can be performed inside a transaction. The following example modifies the Location and Size of an existing item inside a transaction:

C#  Copy Code

IDesignerHost designerHost = reportEditor.GetService<IDesignerHost>();
using (DesignerTransaction transaction = designerHost.CreateTransaction("Modifying item"))
{
    PropertyDescriptor locationProperty = TypeDescriptor.GetProperties(item)["Location"];
    locationProperty.SetValue(item, new Location(0, 0));
    PropertyDescriptor sizeProperty = TypeDescriptor.GetProperties(item)["Size"];
    sizeProperty.SetValue(item, new Dimension(20, 20));
    transaction.Commit();
}

Visual Basic  Copy Code

Dim designerHost As IDesignerHost = reportEditor.GetService(Of IDesignerHost)()
Using transaction As DesignerTransaction = designerHost.CreateTransaction("Modifying item")

    Dim locationProperty As PropertyDescriptor = TypeDescriptor.GetProperties(item)("Location")
    locationProperty.SetValue(item, New Location(0, 0))
    Dim sizeProperty As PropertyDescriptor = TypeDescriptor.GetProperties(item)("Size")
    sizeProperty.SetValue(item, New Dimension(20, 20))
    transaction.Commit()

End Using

It is also possible to nest transactions.