Introduction
Once again, I present a small printing class I recently developed to allow me to print the content of a CTreeCtrl
. The code in itself is contained within a small class from which you can inherit your view/dialog that needs to be able to print the content of a CTreeCtrl
.
So How Do We Use It?
To gain all the functionality you will require to print the content of a CTreeCtrl
, you need to inherit the class that will be handling the actual printing from the supplied class TreeCtrlPrint
.
#include "TreeCtrlPrint.h"
class CExampleView : public CFormView, TreeCtrlPrint
{
Doing this gives you access to the following functions in the class:
HTREEITEM GetNextTreeItem(const CTreeCtrl& treeCtrl, HTREEITEM hItem)
You use this function to iterate through the tree control in the order of all items as they would be drawn if all nodes in the tree control have been expanded. This lets you plot all the items in the correct order.
void DrawTreeItem(const CTreeCtrl& treeCtrl, HTREEITEM hPrintItem, CDC *pDC, CRect rect, CWnd *pWnd)
This function actually does the drawing of the specific tree item, you need to pass in the handle of the tree item to print, the rectangle on the target DC which you want to print the output into. Some assumptions are made about the DC in that it will have the correct font selected into it to render with and the height of the rectangle supplied will be used as the size of an icon/image when plotted.
ExpandAll(CTreeCtrl& treeCtrl)
This function will automatically expand all tree items with children.
Typical Printing Code
You have to do some work when printing the content of the tree control:
Select the Correct Font
If you look in the example application, you will see that I make use of the font being used by the tree control for printing, but just scaled correctly for the DPI of the printer:
CFont font ;
LOGFONT lf ;
pTreeCtrl->GetFont()->GetLogFont(&lf);
lf.lfHeight = -MulDiv(abs(lf.lfHeight), pDC->GetDeviceCaps(LOGPIXELSY), 72);
VERIFY(font.CreateFontIndirect(&lf));
pDC->SelectObject(&font) ;
Get the Number of Items that Can Fit on a Page
Once you have the correct font selected, you can calculate how many items can fit on a page, and from there, calculate the number of pages required to print the tree control content.
CString text("A") ;
CSize cs = dc.GetTextExtent(text) ;
linesPerPage = abs(pInfo->m_rectDraw.Height() / cs.cy) -1;
if ((numTreeItems % linesPerPage) != 0)
{
numberOfPages++;
}
numberOfPages += (numTreeItems / linesPerPage);
The Actual Print Loop
When printing, your OnPrint()
function gets called once per page of output, so we may need to navigate to the correct start position in the tree control before we start to print the tree items.
HTREEITEM hPrintItem = pTreeCtrl->GetRootItem();
for (int i = 0 ; i < linesPerPage * currentPage && i < numTreeItems ; i++)
{
hPrintItem = GetNextTreeItem(*pTreeCtrl, hPrintItem);
}
Once we're at the correct start position in the tree, we just keep printing items until either we run out of items or page space:
for (i = linesPerPage * currentPage ;
(i < numTreeItems) && (i < (linesPerPage * (currentPage + 1))) ; i++)
{
CRect rect(pInfo->m_rectDraw.left + (cs.cx * 2),
pInfo->m_rectDraw.top + (cs.cy * line_number),
pInfo->m_rectDraw.left + pInfo->m_rectDraw.Width(),
pInfo->m_rectDraw.top + (cs.cy * (line_number + 1)));
DrawTreeItem(*pTreeCtrl, hPrintItem, pDC, rect, this);
line_number++;
hPrintItem = GetNextTreeItem(*pTreeCtrl, hPrintItem);
}
This code uses one extra variable currentPage
which is taken from pInfo->m_nCurPage - 1
, as we need a 0 based page numbering scheme to correctly work out the starting print item for following pages.
Well, that's all there is to it really.
Problems Encountered
As I said earlier, this is recently developed code for one of my applications. I ported it across for the CodeProject article and setup the example application. It was then that I found out that my code did not work properly for cases where you had more than 1 root item (my app only has 1 root item). My code was indenting the following root items and generally making a mess of things, so in a way, this article improved my application...
History
- 17th March, 2003
A bug fix for the GetNextTreeItem()
function as reported by bhtang - should now work correctly with any type of populated tree. The example application was updated to call UpdatePrinterSelection(TRUE);
in InitInstance()
so that a default printer will be automatically selected at start-up, as suggested by Michael A. Rusakov
A new function ExpandAll(CTreeCtrl& treeCtrl)
was added to the class to expand all tree items which have children.
- 5th March, 2003 - Initial version
Enjoy!