For example, we need to create Access database (mdb) named "reports" and a table for storing our report templates. I named this table "reports_table". Three tables are enough in our data table:
Expand|Select|Wrap|Line Numbers
- № Field name Type Description
- 1 Id counter Auto-increment, index
- 2 ReportName text Name of report
- 3 ReportFile BLOB Binary data with template
To do this:
- open the designer;
- show custom form with a list of templates from our database instead of standard dialog when users open a file in the designer;
- load template in the designer after selecting it in the custom form;
- Save the template in a database where the user saves a file in the designer.
Create a new project in Visual Studio first. Then you need to drag-n-drop "Report" object from toolbox to the empty form. Also drag the " EnvironmentSettings" object (properties of reporting engine).
You can setup many settings of the report and the designer in "environmentSettings1" object. You have to override the event handlers CustomOpenDialog, CustomOpenReport, CustomSaveDialog and CustomSaveReport.
Event handler CustomOpenDialog allows releasing any algorithm of the load report template in the designer - load template from the database field and load it in the designer or set e.Cancel = true; when loading fails.
The problem reduces to showing a dialogue with a list of files from which you want to select the report template from the database and load the template in the designer. Of course, if the user presses the Cancel button in this dialog, we set e.Cancel = true. The loading process is clear but saving code need some details.
The user can choose two ways to save a file - simply by clicking "Save", in this case report should be rewritten to the old place, or the second option - "Save as...”, the user is expected to select path of file. Storage location of report templates cannot be changed in our case. You need to save template in the old place (database field) in both ways - capture the CustomSaveDialog event by empty function and save template in database in the CustomSaveReport event.
Now you need to add a new data source to the project - reports.mdb.Drop the button on form for launching of the reports designer.
Write in the Click event handler:
Expand|Select|Wrap|Line Numbers
- private void button1_Click(object sender, EventArgs e)
- {
- report1.Design();
- }
Assign the buttons OK and Cancel corresponding to DialogResult.
Do not forget to bind your data source to DataGrid - select the field name of the record and make the index field invisible.
You need to save the selected index when you click on the ‘OK’ button:
Expand|Select|Wrap|Line Numbers
- public int reportID;
- //...
- private void OKBtn_Click(object sender, EventArgs e)
- {
- // save id of report for use in future
- reportID = (int)dataGridView1.CurrentRow.Cells[0].Value;
- }
Expand|Select|Wrap|Line Numbers
- // define variable for store of report ID
- private int reportID;
- // define array byte[] for store of template
- private byte[] blob;
- //....
- private void environmentSettings1_CustomOpenDialog(object sender,
- FastReport.Design.OpenSaveDialogEventArgs e)
- {
- using (ReportListForm reportListForm = new ReportListForm())
- {
- // show dialog for report selection
- if (reportListForm.ShowDialog() == DialogResult.OK)
- {
- // get report ID
- reportID = reportListForm.reportID;
- // load report in array from BLOB
- blob =
- (byte[])this.reports_tableTableAdapter.GetDataByID(reportID).Rows[0]["ReportFile"];
- // read file name of report for designers title
- e.FileName =
- (string)this.reports_tableTableAdapter.GetDataByID(reportID).Rows[0]["ReportName"];
- }
- else
- // cancel loading
- e.Cancel = true;
- }
- }
Expand|Select|Wrap|Line Numbers
- private void environmentSettings1_CustomOpenReport(object sender,
- FastReport.Design.OpenSaveReportEventArgs e)
- {
- using (MemoryStream stream = new MemoryStream())
- {
- // skip all garbage created by MS Access in begin of blob field - we seek the tag of XML
- int start = 0;
- for (int i = 0; i < blob.Length - 1; i++)
- {
- if (blob[i] == (byte)'<' && blob[i + 1] == (byte)'?')
- {
- start = i;
- break;
- }
- }
- // copy of blob content in stream
- stream.Write(blob, start, blob.Length - start);
- stream.Position = 0;
- // load template in designer
- e.Report.Load(stream);
- }
- }
We turn to the organization and save the template using the designer. As written above, we need to override the event handler CustomSaveDialog by an empty function to prevent a user from unnecessary actions:
Expand|Select|Wrap|Line Numbers
- private void environmentSettings1_CustomSaveDialog(object sender,
- FastReport.Design.OpenSaveDialogEventArgs e)
- {
- // empty
- }
Expand|Select|Wrap|Line Numbers
- private void environmentSettings1_CustomSaveReport(object sender,
- FastReport.Design.OpenSaveReportEventArgs e)
- {
- // create stream for store of template
- using (MemoryStream stream = new MemoryStream())
- {
- // save template in stream
- e.Report.Save(stream);
- // copy stream in array
- byte[] blob = stream.ToArray();
- // save array in BLOB
- this.reports_tableTableAdapter.UpdateReport(blob, reportID);
- }
- }
Compile and run application, then press a button to open the designer:
- Click menu item "Open":
- Select any report template and press OK.
You can change the report and save the template after that- new template will be stored in database.
Full source code of this example is available in an attached zip archive (databases with reports are included).