Easy! Reports

Environment: VC6

One of the problems while writing programs in C++ is that the tools and API that you can use to generate reports are very limited. For instance, there is a plethora of classes for viewing (CView, CHtmlView etc.) but no reporting API.

The tool presented here is a simple API that can be used to print fairly complex business style reports.

Almost all reports have the following standard elements:

  1. A Report header. Typically, this is the establishment name, business address etc. The report header is printed only on the first page of the report.
  2. The page header. This generally consists of the report title, e.g. "Payroll Summary" and a date. This page header is printed on every page of the report.
  3. The page header is followed by one or more tabular data sections. For instance, a payroll summary may include employee names and their salaries grouped by their department. There may be several sections, one for each department. Each group may be followed by a summary section, summarizing the data for that group.
  4. The data section is followed by a page footer. This page footer generally consists of a page number. The page footer is generally printed on every page.
  5. The page footer is optionally followed by the Report footer. This report footer is printed only on the last page.

Given the above, we present a simple yet powerful layout class. This class is called "CEasyReport". There are basically three functions that are really the key.

  1. "SetDataCols", which starts a new tabular section. The arguments are a array of CColInfo items describing each column in the section. The description includes the column name, the column width and alignment. The column name can contain '\n', which sets up a multiple row column heading.
  2. "AtTab(int, CString)". This is the workhorse function which can be used to print text in columns. The first parameter is the column number and the second character is the text to be printed. For instance, if your tabular section consists of a "Name" column and a "Date of Birth" column, you would print the name using:
      AtTab(0,"Lal, Vipul");  // write the name in col 0
      AtTab(1,"12/04/1980");  // write DOB in col 1
    
    We could have overloaded this function to print a long, double, a CTime etc, but we left that to to you to suit your own requirements.
  3. Call "NextRow" to advance the printing to the next row. Typically, you would do this when you have printed all the columns in one row. This function checks to see if there is enough space on the current page to print another row, and ejects the page if not.

Whenever you need to start a new section, simply call SetDataCols. Thus, if your report consists of a main section followed by a summary section for each group, your typical code loop would be:

m_Recordset.Open(...);
while(! m_Recordset.IsEof())
{
  SetDataCols(ColsInMainSection...)
  m_CurGroup = m_Recordset.GroupColumnData;
  do
  {
    AtTab(0,DataForGroupCol);
    ... other columns...
    NextRow(); // advance the printer row
    m_Recordset.next;
    // compute summary items
  }
  while( !m_Recordset.IsEof() && 
    m_Recordset.GroupColumnData == m_CurGroup);
  // Start a summary section...
  SetDataCols(ColsInSummarySection);
  AtTab(...);  // print summary data
  NextRow();
}

While generating a report content, no actual image is generated. Rather, the generation process simply generates "report objects". For instance, a CTextObject contains the text to be written and the rectangular co-ordinates for the text. Thus, every page consists of a set of "report objects". Later, when we are called to print or preview the report, we simply call upon the report object to draw itself. Since every such object has the co-ordinates with respect to the top of the page, we can draw any page. Thus, the user can select to print or preview page 3,5 and 7 of the report and we simply draw all objects for those pages. The current version supports a Text object, a Line object and a Date and PageNumber object.

Note: The current version simply generates these report objects in memory. You might want to serialize these to disk if the report is really long.

The API has a "RepeatHdr" flag, which forces the column headings to be printed on every page. When this flag is off, the column header is printed every time the tabular section is set up.

The report also has "SuppressBlankHdr" flag. When this flag is set, the column headings are not printed unless you print something in one of the columns.

The report also has a "word-wrap" mode in which one can print a "paragraph" of text. The text is aligned within the page margins.

The demo report project generates a report of all employees, grouped by department. The demo project also has a small Access database which has two tables - an Employee table and a Departments table. The SQL Query used for the report is:

SELECT * FROM employees INNER JOIN departments 
    ON employees.DeptID = departments.DeptID 
       GROUP BY employees.DeptID

However, the current API is not limited to SQL databases in any way and the source of the data could be anything, even an in-memory array. However, the algorithm presented above does rely on the fact that the data is already grouped and sorted. This would generally be the case, since the database can use indexes etc to sort and group the data.

Please do feel free to email me your suggestions, comments and bug fixes.

Downloads

Download demo project - 45 Kb
Download source - 13 Kb


Comments

  • How to Close DataEnviromment

    Posted by thiruonthenet on 02/07/2006 07:31am

    I am using vb data reports with DataEnvironment code i used DataEnvironment1.connection1(1) '[passing a parameter to data report DataReport1.show but after close of shown DataReport1 if i try to asses it again it is giving an error.how can overcome it.

    Reply
  • How to create a setup file

    Posted by thiruonthenet on 02/07/2006 07:03am

    Hi,
       In vb how can we create setup.exe files

    Reply
  • Fix for Font Problem

    Posted by mracky on 01/26/2005 07:50pm

    With a moderate amount of tweaking the CEasyReport is a great start for a report class.  The creation of fonts has a bug (SelectObject() fails and returns NULL for all the fonts.  Correction is below.
    ---------------------------------------------------
    void	CEasyReport::SetupTextStyles(HDC  inDC)
    {
    	// create default fonts...
    	LOGFONT			aFont;
    	CPoint			aPoint;
             .
             .
             .
    	// determine the cell sizes for the fonts...
    	TEXTMETRIC		aTM;
    
    	SelectObject(inDC,(HFONT)*m_Fonts[eCaptionFont]);
    	GetTextMetrics( inDC, &aTM );
    	m_TextSizes[ eCaptionFont].cx = aTM.tmAveCharWidth;
    	m_TextSizes[ eCaptionFont].cy = aTM.tmHeight;
    
    	SelectObject(inDC,(HFONT)*m_Fonts[eColHeadingFont]);
    	GetTextMetrics( inDC, &aTM );
    	m_TextSizes[ eColHeadingFont].cx = aTM.tmAveCharWidth;
    	m_TextSizes[ eColHeadingFont].cy = aTM.tmHeight;
    
    	SelectObject(inDC,(HFONT)*m_Fonts[eDataFont]);
    	GetTextMetrics(inDC,&aTM );
    	m_TextSizes[ eDataFont].cx = aTM.tmAveCharWidth;
    	m_TextSizes[ eDataFont].cy = aTM.tmHeight;
    
    	SelectObject(inDC,(HFONT)*m_Fonts[eTextFont]);
    	GetTextMetrics(inDC,&aTM );
    	m_TextSizes[ eTextFont].cx = aTM.tmAveCharWidth;
    	m_TextSizes[ eTextFont].cy = aTM.tmHeight+ aTM.tmExternalLeading;
    	m_BreakChar = aTM.tmBreakChar;
    }
    ------------------------------------
    notice the * to dereference the font pointer 
    
    Like I said, its a good starting place.  Thanks for the effort Vapul!

    • Fix for Font Problem2

      Posted by BokiC on 03/21/2006 10:29am

      void	CEasyReport::WriteParagraph(const char *inText)
      {
      
      ...
      
      	::SelectObject(m_PrinterDC,(HFONT)*m_Fonts[eTextFont]);

      Reply
    Reply
  • How to print the dialog boxes in MDI applications?

    Posted by Legacy on 10/21/2003 12:00am

    Originally posted by: Prajakta

    I have tried plenty of codes to print my dialog box through a command button which is on the same dialog box to be print. I am not getting the way how the CDC pointer can get the path of my document. IF anyone knows the solution plz send it to me, I am calling my dialog box in MODLESS state, and I need the code to print this dialog box. My application is a MDI application.
    
    prajakta

    Reply
  • how to use this as text file reader?

    Posted by Legacy on 09/04/2003 12:00am

    Originally posted by: Manish Singhal

    Hi,
    The tool is wonderfule. Congrats, but as you mentioned that the API's wouls be it any source, but i was not able to make it work using simple text file. Can you provide me some lead on that?

    Reply
  • Changing page bug

    Posted by Legacy on 08/29/2003 12:00am

    Originally posted by: Giova

    If the report has more than 1 page and you try to chage the current page unsing GotoPage() it doesnot updates the view until you scroll down the page.

    how can I solve this?

    Reply
  • advancedui

    Posted by Legacy on 07/04/2003 12:00am

    Originally posted by: Gerson N Silva

    EasyReports,
    As to print directly of a field memo with a lot of lines of text of a table access???

    Reply
  • EasyReport

    Posted by Legacy on 04/01/2003 12:00am

    Originally posted by: Bert Farrell

    This is a very nice Class. Is it possible to output to a Dialog.

    Reply
  • http://www.ucancode.net

    Posted by Legacy on 02/21/2003 12:00am

    Originally posted by: hap

    It's great!

    Reply
  • help:change the font

    Posted by Legacy on 11/27/2002 12:00am

    Originally posted by: Jack

    how can I change the font that printed such as ReportHeader ,PageHeader,etc.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 13, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT If you are developing applications, you'll want to join us to learn how applications are changing as a result of gesture recognition. This technology will change how you and your users interact - not simply with your devices, but with the world around you. Your devices will be able to see and hear what your users are doing. Are your applications ready for this? Join us to learn about Intel® RealSense™ Technology, including never been …

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds