Plug-in class to support printing from a listview

Environment: Visual C++ 6.0

I read and used "Print the contents of the list control" by Ravi Reddy, as a basis for this code. His code is very good. However, I wanted a class that I could just plop into my view class as a contained member object to take over printing the list control. My class is LCPrinting.

Differences between LCPrinting and Ravi Reddy's code:

LCPrinting does not print in color (only black and white).

It does not print icons. It only prints a list control in report view.

It is a contained class. Just add it as a member to your view class.

It has support for column ordering (up to 30 columns.If U need more change it)

It guesses at the number of pages B4 the print dialog comes up.

It prints lines after each row.

It prints the header control manually instead of having the header print itself. This gives more control in how the header appears on a page.

Implementation:

Just add an LCPrinting member object to your view class like this:

LCPrinting lcp;

Override the following printing functions in your view class like so this:

//*****************************ONPREPAREPRINTING**********************
BOOL CStocky5View::OnPreparePrinting(CPrintInfo* pInfo) 
{	
 // The third parameter expects a pointer to your list control
 return lcp.OnPreparePrinting(pInfo, this, &lc); //Guess the # of pages
}

//**************************ONBEGINGPRINTING********************
void CStocky5View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{	
 // The title and date are CString references printed on each page
 CStocky5Doc* pDoc = GetDocument();
 CMainFrame * mf_ptr = (CMainFrame *)AfxGetMainWnd();
 CString doc_title = pDoc->GetTitle();
 lcp.OnBeginPrinting(pDC, pInfo, doc_title, mf_ptr->fdc.date);

 return;
}

//***********************ONPRINT**********************
void CStocky5View::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
 lcp.OnPrint(pDC, pInfo);
 return;
}
	
//******************ONENDPRINTING************************
void CStocky5View::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo) 
{
 lcp.OnEndPrinting(pDC, pInfo);//Clean up those printing fonts
 CListView::OnEndPrinting(pDC, pInfo);
 return;
}

Here is the class header file:


#ifndef G_LC_PRINTING
#define G_LC_PRINTING

class LCPrinting
{
public:
 LCPrinting();//Constructor
 BOOL OnPreparePrinting(CPrintInfo* pInfo, CView * cview, CListCtrl * t_lc);//Guess # of pages
 void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo, CString & t_title, CString & t_date);
 void OnPrint(CDC* pDC, CPrintInfo* pInfo);
 void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

protected:
 //vars
 CListCtrl * lc;//List Control Pointer
 CString TitleStr;
 CString DateStr;

 CFont * pOldFont;//Font stuff
 CFont BoldFont;
 CFont lcFont;

 CRect page_rc;//Scaling Vars
 int m_nRowHeight;
 int m_nRowsPerPage;
 int m_nMaxRowCount;
 int hc_items;
 unsigned int m_ratiox;//These two are for scaling output to the printer
 unsigned int m_ratioy;

 //Protected functions------
 void PrintHeaderControl(CDC *pDC, CPrintInfo *pInfo);
 void PrintHeader(CDC *pDC, CPrintInfo *pInfo);
 void PrintFooter(CDC *pDC, CPrintInfo *pInfo);
 void DrawRow(CDC *pDC, int nItem);
 void draw_line_at(CDC *pDC, unsigned int y);
 void compute_metrics(CDC *pDC);
};

#endif

Here is the .cpp file:


#include "stdafx.h"
#include "LCPrinting.h"

#define LEFT_MARGIN 2
#define RIGHT_MARGIN 4
#define HEADER_HEIGHT 4
#define FOOTER_HEIGHT 3

//Set it all to 0
//********************************CONSTRUCTOR************************************
LCPrinting::LCPrinting()
{
 lc = 0;
 pOldFont = 0;
 TitleStr = "";
 DateStr = "";

 page_rc.SetRect(0,0,0,0);	
 m_nRowHeight = 0;
 m_nRowsPerPage = 0;
 m_nMaxRowCount = 0;
 m_ratiox = 0;
 m_ratioy = 0;
 hc_items = 0;
 return;
}

//Using default for printer guess at # of pages.
//If no printer exists return FALSE;
//************************ONPREPAREPRINTING*******************************
BOOL LCPrinting::OnPreparePrinting(CPrintInfo* pInfo, CView * cview, CListCtrl * t_lc) 
{
 if(t_lc==NULL || cview==NULL || pInfo == NULL)          return FALSE;
 lc = t_lc;//Set Pointer to list Control

 //Lets make a guess as to how many pages there are based on the default printer.
 CPrintDialog pdlg(FALSE);
 if (!pdlg.GetDefaults()) return FALSE;//If no defaults then no printer!!
 CDC t_pDC;
 t_pDC.Attach(pdlg.GetPrinterDC());
 compute_metrics(&t_pDC);
 m_nMaxRowCount = lc->GetItemCount(); if(!m_nMaxRowCount) return FALSE;//Get the number of rows
 int nMaxPage = m_nMaxRowCount/m_nRowsPerPage + 1;
 pInfo->SetMaxPage(nMaxPage);
 pInfo->m_nCurPage = 1;  // start printing at page# 1

 //If you want to be able to do this remove it.
 pInfo->m_pPD->m_pd.Flags |=PD_HIDEPRINTTOFILE; 

 return cview->DoPreparePrinting(pInfo);
}

//Call this from your view class OnBeingPrinting function
//*************************ONBEGINGPRINTING*************************
void LCPrinting::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo, CString & t_title, CString & t_date)
{
 if(pDC == NULL || pInfo==NULL)          
  return;

 TitleStr = t_title;
 DateStr = t_date;

 //create lc font, and Bold lc Font
 LOGFONT  lf;
 CFont * lcfont_ptr = lc->GetFont();
 lcfont_ptr->GetLogFont(&lf);
 lcFont.CreateFontIndirect(&lf);
 lf.lfWeight = FW_BOLD;
 lf.lfHeight+=22;//Make it a little bigger
 lf.lfWidth = 0;
 BoldFont.CreateFontIndirect(&lf);
	
 compute_metrics(pDC);
 int nMaxPage = m_nMaxRowCount/m_nRowsPerPage + 1;//Compute this again in case user changed printer
 pInfo->SetMaxPage(nMaxPage);
 pInfo->m_nCurPage = 1;  // start printing at page# 1

 return;
}

//***********************ONPRINT*************************
void LCPrinting::OnPrint(CDC* pDC, CPrintInfo* pInfo) 
{
 if(NULL == pDC || NULL == pInfo)           
  return;

 //This has to be in OnPrint() or else PrintPreview goes screwy
 pOldFont = pDC->GetCurrentFont();

 //Fit all columns to 1 page, regardless of column number.
 pDC->SetMapMode(MM_ANISOTROPIC);

 //For every 1 List Control pixel
 pDC->SetWindowExt(1, 1);

 //The printer has ratio more dots
 pDC->SetViewportExt(m_ratiox, m_ratioy);

 int nStartRow = (pInfo->m_nCurPage - 1)*m_nRowsPerPage;
 int nEndRow = nStartRow+m_nRowsPerPage;
 if(nEndRow > m_nMaxRowCount)       
  nEndRow = m_nMaxRowCount;

 PrintHeader(pDC, pInfo);     //print the header

 pDC->SetWindowOrg(-1*page_rc.left, 0);
 PrintFooter(pDC, pInfo);	  //Print the footer
 pDC->SetWindowOrg(-1*page_rc.left, -1*HEADER_HEIGHT*m_nRowHeight);
 PrintHeaderControl(pDC, pInfo);//Print the header Control, Manually
 pDC->SelectObject(&lcFont);//Use the LC normal font
 pDC->SetTextColor(RGB(0,0,0));//Black text on
 pDC->SetBkColor(RGB(255,255,255));//White paper
	
 CRect     rcBounds;
 lc->GetItemRect(nStartRow, &rcBounds, LVIR_BOUNDS);

 //offset top margin of rcBounds by ListControl header
 CRect     rc;
 lc->GetHeaderCtrl()->GetClientRect(&rc);
 rcBounds.OffsetRect(0, -rc.Height());
 pDC->OffsetWindowOrg(rcBounds.left, rcBounds.top);

 //start printing rows
 for(int i = nStartRow; i < nEndRow; i++)
  DrawRow(pDC, i);

 //SetWindowOrg back for next page
 pDC->SetWindowOrg(0,0);
 pDC->SelectObject(pOldFont);//Put the old font back

 return;
}

//Set the extents after calling this function because it uses printer extents
//He is using a list in here have to figure out what to do.
//********************************PRINT_HEADER************************************
void LCPrinting::PrintHeader(CDC *pDC, CPrintInfo *pInfo)
{
 pDC->SelectObject(&BoldFont);
 pDC->SetTextColor(RGB(0,0,0));//Black text on
 pDC->SetBkColor(RGB(255,255,255));//White paper
	
 CRect rc = page_rc;
 rc.bottom = rc.top+m_nRowHeight;

 //print App title on top right margin
 pDC->DrawText(TitleStr, &rc,  DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_RIGHT  | DT_NOCLIP);

 return;
}

//print footer with a line and date, and page number
//****************************PRINT_FOOTER****************************************
void LCPrinting::PrintFooter(CDC *pDC, CPrintInfo *pInfo)
{
 CRect     rc = page_rc;
 rc.top = rc.bottom - FOOTER_HEIGHT*m_nRowHeight;
 rc.bottom = rc.top + m_nRowHeight;
 draw_line_at(pDC, rc.top);     //draw line

 //draw page number
 CString   sTemp ;
 rc.OffsetRect(0, m_nRowHeight/2);
 sTemp.Format("%d", pInfo->m_nCurPage);
 pDC->DrawText(sTemp,-1,rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);

 //Now draw the DateStr at bottom of page
 pDC->DrawText(DateStr,-1,rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);

 return;
}


//Do the cleanup
//********************ONEND_PRINTING*****************
void LCPrinting::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
lcFont.DeleteObject();
BoldFont.DeleteObject();
return;
}

//This function sets alls of the row and metric member vars
//************************COMPUTE_METRICS*********************
void LCPrinting::compute_metrics(CDC *pDC)
{
 //This includes width for all columns
 CRect row_rc;	lc->GetItemRect(0, &row_rc, LVIR_BOUNDS);

 //Get the list control window DC
 CDC  *pCtlDC = lc->GetDC(); if(NULL == pCtlDC) return;

 //so we can get the avg character width
 TEXTMETRIC tm;   pCtlDC->GetTextMetrics(&tm);

 //Lets get the ratios for scaling to printer DC
 //Fit all columns to 1 page, regardless of column number.
 m_ratiox = pDC->GetDeviceCaps(HORZRES)/(row_rc.Width() + (LEFT_MARGIN+RIGHT_MARGIN)*tm.tmAveCharWidth);

 //width of pDC/whats got to fit into it in lcDC units
 m_ratioy = pDC->GetDeviceCaps(LOGPIXELSY)/pCtlDC->GetDeviceCaps(LOGPIXELSY);

 lc->ReleaseDC(pCtlDC);

 //Set up a page rc in list control units that accounts for left and right margins
 page_rc.SetRect(0,0, pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
 page_rc.bottom = page_rc.bottom/m_ratioy;//Convert units to List Control
 page_rc.right = page_rc.right/m_ratiox;
 page_rc.left = LEFT_MARGIN*tm.tmAveCharWidth;//adjust sides for magins
 page_rc.right -= RIGHT_MARGIN*tm.tmAveCharWidth;

 m_nRowHeight = row_rc.Height();//Get the height of a row.
 int pRowHeight = (int)(m_nRowHeight*m_ratioy);//Get RowHeight in printer units.
 m_nRowsPerPage = pDC->GetDeviceCaps(VERTRES)/pRowHeight;//How many rows will fit on page?
 m_nRowsPerPage -= (HEADER_HEIGHT+FOOTER_HEIGHT);//After header and footer rows
 m_nRowsPerPage -= 1; //After header Control row

 return;
}

//You can't just have the header control print itself. 1st of all it looks crappy.
//2nd if part of header control is off screen does not print itself.
//So we will manually print it.
//************************PRINTHEADERCONTROL****************************
void LCPrinting::PrintHeaderControl(CDC *pDC, CPrintInfo *pInfo)
{
 UINT    dtFlags = DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER|DT_LEFT;//drawing flags
 CHeaderCtrl* hc = lc->GetHeaderCtrl();
 hc_items = hc->GetItemCount(); 
 if (hc_items < 1) return; //Remember that hc_items is also used to draw rows.
 int order_array[30];//Shouln't have more than 30 columns
 hc->GetOrderArray(order_array, hc_items);

 char temp_str[1024];
 HDITEM  phi;
 phi.mask = HDI_TEXT | HDI_WIDTH ;
 phi.cchTextMax = 1024;
 phi.pszText = temp_str;

 CRect rc(0,0,0,m_nRowHeight);

 for (int i = 0; i < hc_items; i++)
 {
  hc->GetItem(order_array[i], &phi);//Get in viewed order
  rc.right += phi.cxy;
  pDC->DrawText(temp_str, -1, rc, dtFlags);
  rc.left += phi.cxy;
 }

 //Now draw the line below header control
 draw_line_at(pDC, rc.bottom);

 return;
}

//*************************************DRAWROW********************************************
void LCPrinting::DrawRow(CDC *pDC, int nItem)
{
 if (hc_items < 1) //Then nothing to print
  return;  

 int order_array[30];//Shouln't have more than 30 columns
 lc->GetColumnOrderArray(order_array, hc_items);

 CString temp_str;
 LV_COLUMN lvc;
 lvc.mask = LVCF_WIDTH;

 CRect rc; lc->GetItemRect(nItem, rc, LVIR_LABEL);
 int offset = pDC->GetTextExtent(" ", 1).cx;//Returns CSIZE so get cx member of CSIZE object.
 rc.left += offset/2;//This makes it so that label will be over a little bit
 rc.right -= offset;//Just keep this stuff it DOES look better.

 for (int i = 0; i < hc_items; i++)
 {
  lc->GetColumn(order_array[i], &lvc);//Get in viewed order
  temp_str = lc->GetItemText(nItem, order_array[i]);
  rc.right += lvc.cx;
  pDC->DrawText(temp_str, -1, rc, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER|DT_LEFT);
  draw_line_at(pDC, rc.bottom);//draw a line below each row
  rc.left += lvc.cx;
 }

 return;
}

//Just pass this function a y position to draw the line at.
//*************************DRAW_LINE_AT************************************
void LCPrinting::draw_line_at(CDC *pDC, unsigned int y)
{
 pDC->MoveTo(0, y);
 pDC->LineTo(page_rc.right, y);//Use the page_rc to figure out the width of the line

 return;
}

Date Last Updated: February 3, 1999



Comments

  • tQFoE OuQ NMHK

    Posted by VMdMHiWtzQ on 11/14/2012 05:24pm

    soma cost order soma no prescription - carisoprodol 150

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

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds