This parameter screen was generated DYNAMICALLY by just reading the *.rpt file!
Introduction
I have made several significant changes to the code in this updated version of the article. One of the changes involves replacing the use of an XML file to collect parameter information with a simpler approach. I have also changed the order of the columns placing the Prompt
data as the first column which I realize makes more sense.
The company I own provides custom software solutions to Fortune 500 companies in Los Angeles, CA and Miami, FL for C# and VB .NET WinForm and WebForm applications. We can provide a Web or WinForm app that would take 6 programmers a year to complete in just DAYS thereby saving our clients, in some cases, millions of dollars.
To be able to do this, I have written what I call "Model Applications" of MDI & SDI WinForms, and Web Apps, etc. that simply require changing artwork and business logic.
I use Crystal Reports in my model apps for creating reports. This is the first part of a two part article, that will demonstrate how to implement a generic solution to using Crystal Reports in WinForms and Web Apps.
Part II of this article deals with Web Apps. You can read Part II here.
This article, Part I, deals with using Crystal Reports in WinForms.
Background
Crystal Reports is not easy to work with, and it has plenty of bugs and quirks in my experience. But the big companies all seem to like it, so I had to develop a generic approach to using it.
One of the worst features of Crystal Reports is the way in which it handles report parameters by presenting each parameter in a separate screen which clients really hate! To solve this, this demo will show you how to present ALL of the parameters contained in a *.rpt file in a SINGLE screen to the user.
This demo displays reports using two screens, a Reports Viewer, and a Parameters Viewer. You could combine these screens which is easy to do. These screens contain the following:
- Reports Viewer - This screen has a
ComboBox
that displays a list of reports that the user can select from. When the user presses the "Get Report" button, a modal window called the Parameters Viewer will be launched.
- Parameters Viewer - This screen displays the parameters contained in the report selected in a
datagrid
that we dynamically add controls to in order to make selecting parameter values easier for the user. After setting the parameter values for the selected report, the user will close the Parameters Viewer and the Reports Viewer will display the selected report with the selected parameters.
Crystal Reports creates a binary file with a RPT extension that contains ALL of the information we need to display any report, and so that is what we will use in this generic solution.
- Select the *.rpt file.
- Read the parameter information directly from the *.rpt file.
- Build a table dynamically displaying all of the parameters in the selected *.rpt report file
- The parameter table that we create dynamically must also contain controls that are also created dynamically to facilitate the users selection of parameters like a date calendar,
checkbox
es, etc.
- We must create validation controls dynamically from the parameter data that we read from the *.rpt file.
- Finally, we pass the parameter data into the Crystal Viewer and display our selected Crystal Report.
- Database connection information is read dynamically from the selected *.rpt file.
In the demo reports included, I didn't bother to connect to any data source since doing that is trivial and the main point of this article is to show how to display the parameter data in a datagrid
with selection controls dynamically. But all of the code that does this is in place in the demo and will read the database information dynamically if there is any database information in the *.rpt file.
Reading Parameter Data From *.rpt Report Files
We started by pre-defining the columns of our Datagrid
of Parameters as follows: Parameter, Kind, Value, Min, Max, Prompt.
We will add each parameter kind as a new row to our datagrid
.
BooleanParameter
CurrencyParameter
DateParameter
DateTimeParameter
NumberParameter
StringParameter
TimeParameter
To dynamically build a datagrid
of parameters, we read parameters from the *.rpt file as follows:
// Get parameter field definitions in collection of ParameterFieldDefinition objects
CrystalDecisions.CrystalReports.Engine.ParameterFieldDefinitions crParamFieldDefinitions
= crDoc.DataDefinition.ParameterFields;
// Iterate over collection, processing each ParameterFieldDefinition
foreach (CrystalDecisions.CrystalReports.Engine.ParameterFieldDefinition _
def in crParamFieldDefinitions)
For each parameter in the foreach
loop, we add a row to our dynamic datagrid
which has pre-defined columns.
Dynamically Adding Controls to Our Datagrid of Parameters
What is unusual about our datagrid
of parameters is that we are adding each parameter as a new row instead of as a new column which is what is more commonly done in datagrid
s. This means that I needed to implement a different approach to adding user controls. To accomplish this, I used the GotFocus
event of the datagrid
to selectively add/remove controls based upon the Parameter Kind listed in column 2 of the datagrid
.
For the purposes of this article, I only implemented three controls types:
DateTime
TimePicker
CheckBox
For example, when a user clicks on the datagrid
, if Column 2 has a value of DateTime
, then a DateTime
Control will appear in the data cell for the user to select a DateTime
. I should point out that some programmers prefer using a "Radio Button" for Boolean values but I prefer using a simple CheckBox
for Boolean values which I implemented in the demo here. It should be obvious that you can implement ANY controls that turn you on or that a client requests.
The DateTime Picker Control
The DateTime
Picker is a control that allows the user to select either a date, a datetime, or a time value and provides two distinct GUI interfaces for a Calendar and a Time Spinner.
DateTime Control
Format |
Description |
h |
12-hour - Hour with one digit if value is less than 10 |
hh |
12-hour - Hour with a leading 0 if the value is less than 10 |
H |
24-hour - Hour with one digit if the value is less than 10 |
HH |
24-hour - Hour with a leading 0 if the value is less than 10 |
m |
Minute - Minute with one digit if the value is less than 10 |
mm |
Minute - Minute with a leading 0 if the value is less than 10 |
t |
AM/PM - Letter A or P for the AM or PM section |
tt |
AM/PM - Letters AM or PM for the last section |
To set the DateTime
control as a Time Spinner , set the ShowUpDown
Boolean property to true
, and change the Format
property to a Time
value. The Time Picker control is a spin button made of different sections: the hours value, the minutes value, the optional seconds value, and the optional AM/PM string
. To change the time, the user clicks a section and uses either the mouse or the keyboard to increase or decrease that particular value. To change another value, the user must first click it and then use the spin button.
By default, the time displays using the H:M:SS AM/PM
format. This means that the time uses 1 digit for the hours from 0 to 9, 1 digit for the minutes from 0 to 9, 1 digit for the seconds from 0 to 9 and the AM or PM for morning or afternoon. To customize the way the time displays, first set the Format
property to Custom
. Then, in the CustomFormat
property, use a combination of the following characters to create a custom format: hh:mm:tt
I set the format at run time by assigning the desired format to the DateTimePicker::CustomFormat
property. By default, the control assumes the time of the computer when the control was added. If you want to set a different time, apply a Format
combination to the Value
property. In the same way, at any time, you can retrieve the time value on the control by accessing the Value
property.
Points of Interest
File Exports
I included the ability to export the loaded report in various file types including:
- Rich Text Document (RTF)
- Portable Document (PDF)
- Microsoft Word (DOC)
- Microsoft Excel (XLS)
- Crystal Report (RPT)
- HTML 3.2 (HTML)
- HTML 4.0 (HTML)
Optional Uranus File
While "uranus" is typically a dark, gaseous and unpleasant planet, in this demo I included "uranus" as the name of a temporary XML file that you can optionally use to store parameter data.
Project Pathways
Load ALL of the Crystal Reports DLLs which can be found at:
C:\Program Files\Common Files\Crystal Decisions\1.1\Managed
CrystalHelper Class
This class is a simple class that makes it easier to pass discrete parameter values to Crystal Reports. It allows you to further customize parameter options without having to re-write all of the code to pass parameters to Crystal Reports.
Calendar Auto Drop Down
If you want the calendar to drop down automatically when the calendar field in the datagrid
is clicked, you can send a Windows message WM_KEYDOWN = 0x100
to update the button using:
Win32.SendMessage(dtp.Handle, 0x100, 0x73, 0x3E0001)
Adjust Width of ComboBox DropDown
We automatically adjust the width of our dynamically generated combobox
in the datagrid
cell to the width of the longest element using the combobox
's DropDown
event as follows:
private void AdjustWidthComboBox_DropDown(object sender, System.EventArgs e) {
ComboBox senderComboBox = (ComboBox)sender;
int width = senderComboBox.DropDownWidth;
Graphics g = senderComboBox.CreateGraphics();
Font font = senderComboBox.Font;
int vertScrollBarWidth =
(senderComboBox.Items.Count>senderComboBox.MaxDropDownItems)?
SystemInformation.VerticalScrollBarWidth:0;
int newWidth;
foreach (Object s in senderComboBox.Items) {
string ss = senderComboBox.GetItemText(s);
newWidth = (int)g.MeasureString(ss, font).Width + vertScrollBarWidth;
if (width < newWidth) { width = newWidth; }
}
senderComboBox.DropDownWidth = width;
}
Override CrystalViewer's Refresh Event
If we don't override the CrystalViewer
's refresh event, the Parameters Screen will pop up on refresh, so we just do the following:
private void CrystalReportViewer1_ReportRefresh(object source,
CrystalDecisions.Windows.Forms.ViewerEventArgs e) { e.Handled = true; }
That's all folks!
If you have any questions about this article or source code, please feel free to contact me.
Contact Details