Ad-Hoc reporting is a very common requirement if we talk about reporting. It is very common for a developer to handle the report data and layout, and it can become quite a tedious task if the reporting tool is not flexible enough. Thanks to the rich API of ActiveReports, developers can use it efficiently,  both at the designer level as well when working with code-behind.

Often, users working with Section Based Reports ask whether it is possible to generate a report with multiple columns. An instant answer would be, of course! However, what if the user may have a variable number of columns in the report? In this blog article, I will focus on an approach which allows users to decide the number of columns they want in the report and create a report based on it. Let us first see how our final result will look:

The viewer showing the report with columns split over multiple pages.


The idea here is to create a report dynamically and add a Textbox control to it, such that 15 columns can be adjusted on one page. If the number of columns exceeds this number, we create another report and add the remaining columns to it. Finally the reports are merged together to create a single report. Since the pages are added one by one, the pages always appear in the correct order. An important thing to note here is that the number of columns which a page may contain depends on the paper size defined by the user. So, it is required to consider this value as well as the width of the Textboxes in mind when adding the columns. Let us see what code is required when we click the "Generate Report" button.

private void Button1_Click(object sender, EventArgs e)
{
GrapeCity.ActiveReports.SectionReport ar = default(GrapeCity.ActiveReports.SectionReport);
GrapeCity.ActiveReports.SectionReport ar2 = default(GrapeCity.ActiveReports.SectionReport);

GrapeCity.ActiveReports.SectionReportModel.TextBox ctl = default(GrapeCity.ActiveReports.SectionReportModel.TextBox);
DataTable dt = new DataTable();
dt = GetData(TrackBar1.Value);

if (dt.Columns.Count > 0)
{
ar = GetClearReport();

for (int i = 2; i <= dt.Columns.Count - 1; i++)
{
if (i < 15)
{
ctl = new GrapeCity.ActiveReports.SectionReportModel.TextBox();
var _with1 = ctl;
_with1.Name = "ColN" + (i - 2).ToString();
_with1.Text = "ColN" + (i - 2).ToString();
_with1.DataField = "ColN" + (i - 2).ToString();
_with1.Location = new PointF(0.8f * i, 0.1f);
_with1.Size = new SizeF(0.5f, 0.3f);
_with1.Border.BottomStyle = GrapeCity.ActiveReports.BorderLineStyle.Solid;
ar.Sections[0].Controls.Add(ctl);
}
else
{
if (ar2 == null)
{
ar2 = GetClearReport();
}
ctl = new GrapeCity.ActiveReports.SectionReportModel.TextBox();
var _with2 = ctl;
_with2.Name = "ColN" + (i - 2).ToString();
_with2.Text = "ColN" + (i - 2).ToString();
_with2.DataField = "ColN" + (i - 2).ToString();
_with2.Location = new PointF(0.8f + 0.8f * (i - 14), 0.1f);
_with2.Size = new SizeF(0.5f, 0.3f);
_with2.Border.BottomStyle = GrapeCity.ActiveReports.BorderLineStyle.Solid;
ar2.Sections[0].Controls.Add(ctl);
}
}
}

if ((ar != null))
{
if (ar2 == null)
{
ar.DataSource = dt;
ar.Run();
Viewer1.Document = ar.Document;
}
else
{
ar.DataSource = dt;
ar.Run();
ar2.DataSource = dt;
ar2.Run();
GrapeCity.ActiveReports.Document.SectionDocument doc = new GrapeCity.ActiveReports.Document.SectionDocument();
for (int i = 0; i <= ar.Document.Pages.Count - 1; i++)
{
doc.Pages.Add(ar.Document.Pages[i]);
doc.Pages.Add(ar2.Document.Pages[i]);
}
Viewer1.Document = doc;
}
}
}


You will notice that in the code above I am calling GetClearReport() and GetData() methods. The former creates the report layout and defines the page settings while the latter gets the data from a Datatable. The code for the GetClearReport method is provided below:

private GrapeCity.ActiveReports.SectionReport GetClearReport()
{
GrapeCity.ActiveReports.SectionReport r = new GrapeCity.ActiveReports.SectionReport();

r.Document.Printer.PrinterName = "";
r.PageSettings.Orientation = GrapeCity.ActiveReports.Document.Section.PageOrientation.Landscape;
r.PageSettings.Margins.Left = 0.1f;
r.PageSettings.Margins.Right = 0.1f;
r.Document.Printer.Landscape = true;
r.PrintWidth = 11;

r.Sections.Add(GrapeCity.ActiveReports.Document.Section.SectionType.Detail, "Detail");
var _with1 = r.Sections[0];
_with1.Height = 2f;
_with1.CanGrow = true;
_with1.CanShrink = true;

GrapeCity.ActiveReports.SectionReportModel.TextBox ctl = default(GrapeCity.ActiveReports.SectionReportModel.TextBox);

ctl = new GrapeCity.ActiveReports.SectionReportModel.TextBox();
ctl.Name = "Years";
ctl.Text = "Years";
ctl.DataField = "Years";
ctl.Location = new PointF(0.1f, 0.1f);
ctl.Size = new SizeF(0.7f, 0.3f);
ctl.Border.BottomStyle = GrapeCity.ActiveReports.BorderLineStyle.Solid;
ctl.BackColor = Color.Aqua;
r.Sections[0].Controls.Add(ctl);

ctl = new GrapeCity.ActiveReports.SectionReportModel.TextBox();
ctl.Name = "Month";
ctl.Text = "Month";
ctl.DataField = "Month";
ctl.Location = new PointF(0.8f, 0.1f);
ctl.Size = new SizeF(0.7f, 0.3f);
ctl.Border.BottomStyle = GrapeCity.ActiveReports.BorderLineStyle.Solid;
ctl.BackColor = Color.AliceBlue;
r.Sections[0].Controls.Add(ctl);
return r;
}


So we are all set. I have not included the code for the GetData function here; however a running example in both C# and VB.NET can be downloaded from the links below.

Download C# Sample
Download VB.NET Sample