WΛЯИING's blog

The deepest secrets of Visual Basic ...

C#: Adding many SortField to a CrystalReportViewer using reflection

April 9, 2009 22:16 by WΛЯИІNG

I'm currently working on a project for a client which consists in translating an important management application initially developed in VB6, into C# 3.5, by using decompilation since the client lost his source code. This is my first .Net related post.

The application uses many external components, including the powerful Crystal Report component.

During the development of that new version I faced a problem with the .Net implementation of Crystal Report.

Actually, with Visual Basic 6.0 it was an easy task to add one or many sort fields to the report document in order to sort the result by field (for example by "Lastname", "Firstname", and "ID").

It's not the case with .Net implementation of Crystal Report Document, on which you cannot dynamically add sort field to document. You must add the sort field when you're editing the document. It's not really convenient when you don't know how many fields must be sorted when creating the document.

The reason of this missing is not really clear since it's an existing feature on Visual Basic 6.0. We will learn here how to add many SortField to the ReportDocument of a CrystalReportViewer, using reflection.

Imagine you have to sort the request's result by "Firstname", "Lastname", and "ID" fields, using the Crystal report document with the variable "Filename" as the report document path, and the variable "srcDataSet" dataset as the data source:

string[] sortFieldNames = new string[] { "Firstname", "Lastname", "ID" };

ReportDocument
reportDocument = new ReportDocument();

reportDocument.FileName = String.Format("rassdk://{0}", Filename);

reportDocument.SetDataSource(srcDataSet.Tables[0]);

SortFields targetSortField = reportDocument.DataDefinition.SortFields;

Here we can modify the sort field (only if existing in the document):

DatabaseFieldDefinition fieldDef = reportDocument.Database.Tables["REPORTS"].Fields["FIRSTNAME"];

targetSortField[0].Field = fieldDef;

targetSortField[0].SortDirection = CrystalDecisions.Shared.SortDirection.AscendingOrder;

But impossible to add directly a new field to that SortFields collection because no method allows performing action:

pic1

Actually, it's possible to add a new sort field using the mother inherited class of each used component.

First we get the "SortFields" mother's class of type (CrystalDecisions.ReportAppServer.DataDefModel.SortsClass ) by using the RasSorts private accessor of the SortFields object:

MethodInfo getRasSorts = targetSortField.GetType().GetMethod("get_RasSorts", BindingFlags.NonPublic | BindingFlags.Instance);

object rasSorts = getRasSorts.Invoke(targetSortField, System.Type.EmptyTypes);

It return an object of type CrystalDecisions.ReportAppServer.DataDefModel.SortsClass.

Now we can access to the private "Add" method of the mother's class:

MethodInfo addSort = rasSorts.GetType().GetMethod("Add");

The description of that method show that it accept one parameter of type CrystalDecisions.ReportAppServer.DataDefModel.ISCRSort.

pic2 

ISCRSort is an interface, so we have to create a new instance of a class that implement the ISCRSort interface. That class is called "SortClass" and come from the "CrystalDecisions.ReportAppServer.DataDefModel" assembly.

Assembly rasAssembly = getRasSorts.ReturnType.Assembly;

ConstructorInfo ciRasSort = rasAssembly.GetType("CrystalDecisions.ReportAppServer.DataDefModel.SortClass").GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, System.Type.EmptyTypes, null);

object rasSort = ciRasSort.Invoke(System.Type.EmptyTypes);

Our "rasSort" object now instantiate the SortClass class, implementing ISCRSort.
Once the SortClass object is created, we must complete it with valid field, else it will return an error on execution.

The SortClass class uses the "RasField" accessor to define the field which will be sorted.

MethodInfo setSortField = rasSort.GetType().GetMethod("set_SortField", BindingFlags.Public | BindingFlags.Instance);

But this method only accepts an object implementing the ISCRField interface:
Void set_SortField(CrystalDecisions.ReportAppServer.DataDefModel.ISCRField);

So we have to extract the object of our field object (called fieldDef) that implement the ISCRField interface, it can be done with the "RasField" accessor of our DatabaseFieldDefinition object called "fieldDef":

MethodInfo getRasField = fieldDef.GetType().GetMethod("get_RasField", BindingFlags.NonPublic | BindingFlags.Instance);

object rasField = getRasField.Invoke(fieldDef, System.Type.EmptyTypes);

And now, the final touch, we add it to the collection:

addSort.Invoke(rasSorts, new object[] { rasSort });

Your new sort field has been adding to the SortFields collection, and you can now modify it:

int n = reportDocument.DataDefinition.SortFields.Count - 1;

reportDocument.DataDefinition.SortFields[n].SortDirection = CrystalDecisions.Shared.SortDirection.AscendingOrder;
reportDocument.DataDefinition.SortFields[n].Field = fieldDef;

That's all.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Comments

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading