Plug-in class to support printing from a listview

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read