Friday, November 9, 2012

Improved Custom ‘Export Button’ on Crystal Reports for Visual Studio


Crystal report works great when the export feature is used to export reports to PDF and word, but the one real drawback is the ‘Export to Excel’ feature. No matter how well you in designing the crystal report, the export to excel output is always skewed. It is the way Crystal Report runtime engine maps the objects and places them in excel cells based on the (x,y) position of the original Crystal Objects in the .RPT file.
I did not like how the default export feature was working on crystal reports and also wanted new features like:
  1. More uniform export to excel feature with less dummy rows and columns and more visually appealing output.   
  2. Restrict the number of export features to only Excel, PDF and Microsoft word and remove all the unnecessary list of export options that the system provides.
  3.  Control over Export file fonts, regardless of what font the Crystal report was developed on.
  4. A friendly option in the ‘Export Successful ‘message box for users to click on a link which opens the export file for them without going to the folder where the report was exported.
  5. Fixed width of rows for uniformity.

So let’s begin the process on steps:

Step 1: Assumptions
  • You are using Visual Studio 2005 or higher.
  • Crystal Report viewer is hosted in a windows form ‘ReportViewer.cs’
  • The example is for the report being exported to excel but it can be used for export to other formats

Step 2: Load data to the report via XML or Memory Stream
 private ReportDocument myReport = null;  
 private void ReportViewer_Load(object sender, EventArgs e)   
 {  
    myReport = new ReportDocument();  
    myData = new DataSet();  
    try   
    {  
      switch (_inputType)   
      {  
         case DataInputType.ByteArray:  
            myData.ReadXml(new System.IO.MemoryStream(_reportData));  
            break;  
         case DataInputType.MemoryStream:  
            stream.Seek(0, SeekOrigin.Begin);  
            myData.ReadXml(_stream);  
            break;  
      }  
      //Load RPT file  
      myReport.Load(reportPath + _RPTName);  
      myReport.SetDataSource(myData);  
      crystalReportViewer1.ShowFirstPage();  
      crystalReportViewer1.ShowGroupTreeButton = true;  
      crystalReportViewer1.ReportSource = myReport;  
    }  
   catch (Exception ex)   
    {  
        ErrorHandler.PublishError(false, "Error displaying report.", ex, true, true);  
    }  
 }  
Step 3: Hide the default export button and add our custom clone on the same place
 //Loop through all the controls in the crystal viewer, Strip the default export button and put a custom export button, Items [0] being the first button which happens to be the export button.  
       
 foreach (Control control in crystalReportViewer1.Controls)   
 {  
    if (control is System.Windows.Forms.ToolStrip)  
    {  
      //Remove Default Export Button, items[0]  
      ToolStripItem tsOriginalItem = ((ToolStrip) control).Items [0];  
      ((ToolStrip) control).Items.RemoveAt(0);  
   
       //Add a clone of Custom Button  
       ToolStripItem tsNewItem = ((ToolStrip) control).Items.Add("");  
        
      //Give this button the same tag,image and tooltip text so it looks and smells like the default export button  
      tsNewItem.ToolTipText = tsOriginalItem.ToolTipText;  
      tsNewItem.Tag = tsOriginalItem.Tag;  
      tsNewItem.Image = tsOriginalItem.Image;  
   
      ((ToolStrip) control).Items.Insert(0, tsNewItem);  
          
      //Write new click event for the new export button  
      tsNewItem.Click += new EventHandler(tsNewItem_Click);  
    }  
 }  
   
Step 4: Define your own custom save file dialogue window when the export button in step 3 is clicked.
 private void tsNewItem_Click(object sender, EventArgs e)   
 {  
       Form messageForm = new Form();  
       DiskFileDestinationOptions crDiskFileDestinationOptions = new DiskFileDestinationOptions();  
       saveFileDialog = new SaveFileDialog();  
       saveFileDialog.Title = "Export Report";  
       saveFileDialog.InitialDirectory = Environment.SpecialFolder.MyDocuments.ToString();  
       saveFileDialog.Filter = "Microsoft Excel (*.xls)|*.xls|Microsoft Excel (Data Only) (*.xls)|*.xls|PDF (*.pdf)|*.pdf|Microsoft Word (*.doc)|*.doc|xml (*.xml)|*.xml";  
       saveFileDialog.FilterIndex = 1;  
       if (saveFileDialog.ShowDialog() == DialogResult.OK) {  
           
       //show splash screen, for a nice look when the export is processing  
       //I have created a new class for this threaded process, which I will talk if possible in the future.  
       Thread splashthread = new Thread(SplashScreen.ShowSplashScreen);  
        splashthread.IsBackground = true;  
        splashthread.Start("Exporting Report...");  
   
        string fileName = saveFileDialog.FileName;  
        string fileFormat = fileName.Substring(fileName.Length - 4);  
   
        ExportOptions exportOpts = new ExportOptions();  
        exportOpts.ExportDestinationType = ExportDestinationType.DiskFile;  
        DiskFileDestinationOptions diskOpts = new DiskFileDestinationOptions();  
        diskOpts.DiskFileName = saveFileDialog.FileName;  
        exportOpts.ExportDestinationOptions = diskOpts;  
 }  
   
Step 5: Control formatting of the exported file based on what type of export it is
 switch (fileFormat)   
 {  
    case ".xls": //Export to Excel  
                   if(saveFileDialog.FilterIndex == 2) //Data only Export  
                    exportOpts.ExportFormatType = ExportFormatType.ExcelRecord;  
            else  
                   exportOpts.ExportFormatType = ExportFormatType.Excel;  
               
            ExcelFormatOptions exportFormatOptions = new ExcelFormatOptions();  
            exportFormatOptions.ShowGridLines = true;  
            exportFormatOptions.ConvertDateValuesToString = true;  
            exportFormatOptions.ExcelAreaType = AreaSectionKind.WholeReport;  
            exportFormatOptions.ExcelTabHasColumnHeadings = true;  
            exportFormatOptions.ExportPageBreaksForEachPage = true;  
            exportFormatOptions.ExportPageHeadersAndFooters = ExportPageAreaKind.OnEachPage;  
            exportFormatOptions.ExportPageBreaksForEachPage = false;  
            exportFormatOptions.ExportPageHeadersAndFooters = ExportPageAreaKind.OncePerReport;  
            exportOpts.ExportFormatOptions = exportFormatOptions;  
            break;  
   
    case ".pdf": //Export to PDF Document  
                   exportOpts.ExportFormatType = ExportFormatType.PortableDocFormat;  
            break;  
   
    case ".doc": //Export to Word Document  
            exportOpts.ExportFormatType = ExportFormatType.WordForWindows;  
            break;  
   
    case ".xml": //Export to XML  
           ExporttoXML(fileName);  
           exportOpts.ExportFormatType = ExportFormatType.Xml;  
           break;  
    default:  
           MessageBox.Show("Format not supported");  
           break;  
 }  
   
Step 6: Change Fonts and also make the cell on excel with fixed width 
 ReportDocument tmpReport = new ReportDocument();  
 tmpReport = myReport;  
 Font ft;  
 if (fileFormat == ".xls")   
 {  
   foreach (CrystalDecisions.CrystalReports.Engine.ReportObject myReportFieldObj in tmpReport.ReportDefinition.ReportObjects)   
   {  
     if (myReportFieldObj.Kind == ReportObjectKind.FieldObject || myReportFieldObj.Kind == ReportObjectKind.TextObject)   
     {  
      if (myReportFieldObj.Height < 200) //if the height of the control is less than 200 than fix it to 200  
         myReportFieldObj.Height = 200;  
   
         //Change the export document font to “Arial”  
      if (myReportFieldObj.Kind == ReportObjectKind.FieldObject)  
      {  
        FieldObject fobj = (FieldObject)myReportFieldObj;  
        ft = new Font("Arial", fobj.Font.Size, fobj.Font.Style, fobj.Font.Unit,  
        fobj.Font.GdiCharSet, fobj.Font.GdiVerticalFont);  
        fobj.ApplyFont(ft);  
      }  
      else if (myReportFieldObj.Kind == ReportObjectKind.TextObject)  
      {  
         TextObject fobj = (TextObject)myReportFieldObj;  
         ft = new Font("Arial", fobj.Font.Size, fobj.Font.Style, fobj.Font.Unit,  
         fobj.Font.GdiCharSet, fobj.Font.GdiVerticalFont);  
         fobj.ApplyFont(ft);  
       }  
     }  
  }  
  tmpReport.Export(exportOpts);  
   
  //load completed, close the splash screen  
  SplashScreen.CloseSplashScreen();  
  this.Activate();  
   
Step 7: Provide the hyper link to just created document using a custom form, if the system has Microsoft excel Installed
 Form messageForm = new Form(); //this needs to be declared at the top of the tsNewItem_Click method  
   
 messageForm.ClientSize = new Size(597, 92);  
 messageForm.Text = "Export to File";  
 messageForm.ShowInTaskbar = false;  
             
 Label lblMsg = new Label();  
 lblMsg.Text = "Export Complete. New file created at ";  
 lblMsg.Location = new Point(69, 29);  
 lblMsg.TabIndex = 0;  
 lblMsg.AutoSize = true;  
   
 LinkLabel lnkExcelFile = new LinkLabel();  
 lnkExcelFile.Text = fileName;  
 lnkExcelFile.Location = new Point(247, 29);  
 lnkExcelFile.Links.Add(0, fileName.Length, fileName);  
 lnkExcelFile.AutoSize = true;  
 lnkExcelFile.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(lnkExcel_LinkClicked);  
   
 Button btnOK = new Button();  
 btnOK.Location = new System.Drawing.Point(230, 57);  
 btnOK.Name = "btnOK";  
 btnOK.Size = new System.Drawing.Size(75, 23);  
 btnOK.TabIndex = 2;  
 btnOK.Text = "OK";  
 btnOK.UseVisualStyleBackColor = false;  
 btnOK.Click += new System.EventHandler(this.btnOK_Click);  
   
 messageForm.StartPosition = FormStartPosition.CenterScreen;  
 if (HasExcel())  
  {  
    messageForm.Controls.Add(lblMsg);  
    messageForm.Controls.Add(lnkExcelFile);  
  }  
 else  
  {  
    lblMsg.Text += " " + fileName;  
    messageForm.Controls.Add(lblMsg);  
  }  
  messageForm.Controls.Add(btnOK);  
  messageForm.TopMost = true;  
  messageForm.ShowDialog(this);  
   
Step 8: Method to check if excel is present in client system or clicked Ok and also Define the click event from the hyperlink 
  //Check to see if the system has Microsoft excel installed.  
 private bool HasExcel()  
 {  
    Microsoft.Win32.RegistryKey regClasses = Microsoft.Win32.Registry.ClassesRoot;  
    Microsoft.Win32.RegistryKey regExcel = regClasses.OpenSubKey("Excel.Application");  
   
    if (regExcel != null)  
    {  
       return true;  
    }  
    else return false;  
 }  
 // Export file linked clicked  
 private void lnkExcel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)  
 {  
    LinkLabel lnk = new LinkLabel();  
    lnk = (LinkLabel)sender;  
    lnk.Links[lnk.Links.IndexOf(e.Link)].Visited = true;  
    System.Diagnostics.Process.Start(e.Link.LinkData.ToString());  
    if (messageForm != null)  
       messageForm.Close();  
    }  
   
 // OK Button on the popup clicked  
 private void btnOK_Click(object sender, EventArgs e)  
 {  
    ActiveForm.Close();  
 }  
   

RootComponent types in solution.xml file in Dynamics CRM 365/2016

In Microsoft Dynamic CRM 2016/365 are you as confused as me when looking at the solution.xml from the solution export? looking at the xml a...