A Print Enabled Tree View
The trick behind the program is, the program enlarges the window size to cover the entire boundary of the tree view, then the program calls the WM_PAINT message to perform default printing to a DC. The background color of the DC is then removed. The code is modified from the article Setting a background color. The prepared tree bitmap in its device-dependent form cannot be sent directly to the printer DC, unexpected result might be obtained. The device-dependent bitmap is converted to DIB using DDBToDIB() function. This function is copied from Converting DDB to DIB. After that, the DIB is sent to the printer DC using StretchDIBits() function.
If the tree view is longer than the paper size, the program determine the maximum rows per page and paginates the tree view into several pages accordingly. After printing, the window is restored to the original size and position. Besides printing the tree view, the program also copies the prepared tree view bitmap to the clipboard for user to save the bitmap elsewhere.
Tree view header file:
Several message handling functions have to be declared using the class wizard, i.e. OnPreparePrinting(), OnBeginPrinting(), OnPrepareDC(), OnPrint(), and OnEndPrinting().
In the tree view header file, include the following variable declarations and function declaration:
// Attributes public: CTreeCtrl* Tree; protected: CImageList m_treeicon; private: CRect rcBounds; int m_nCharWidth; int m_nRowHeight; int m_nRowsPerPage; HANDLE hDIB; WINDOWPLACEMENT WndPlace; // Operations public: void PrintHeadFoot(CDC *pDC, CPrintInfo *pInfo); HANDLE DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal );
Tree view implementation file:
At the tree view constructor, add the following line. This is a pointer for easier access to the CTreeCtrl class associated with the tree view.
CPrtTViewView::CPrtTViewView()
{
Tree=&GetTreeCtrl();
}
After creating the message handlers, replace them with the following code:
#define LEFT_MARGIN 4
#define RIGHT_MARGIN 4
#define TOP_MARGIN 4
#define BOTTOM_MARGIN 4
BOOL CPrtTViewView::OnPreparePrinting(CPrintInfo* pInfo)
{
return DoPreparePrinting(pInfo);
}
void CPrtTViewView::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
HTREEITEM hItem=Tree->GetRootItem();
Tree->GetItemRect(hItem,rcBounds,TRUE);
m_nRowHeight = rcBounds.Height();
// Find the total number of visible items & the right most coordinate
int ItemCount=0;
do
{
ItemCount++;
CRect rc;
Tree->GetItemRect(hItem,rc,TRUE);
if (rc.right>rcBounds.right)
rcBounds.right=rc.right;
hItem=Tree->GetNextItem(hItem,TVGN_NEXTVISIBLE);
}
while (hItem);
// Find the entire print boundary
int ScrollMin,ScrollMax;
GetScrollRange(SB_HORZ,&ScrollMin,&ScrollMax);
rcBounds.left=0;
if (ScrollMax>rcBounds.right)
rcBounds.right=ScrollMax+1;
rcBounds.top=0;
rcBounds.bottom=m_nRowHeight*ItemCount;
// Get text width
CDC *pCtlDC = Tree->GetDC();
if (NULL == pCtlDC) return;
TEXTMETRIC tm;
pCtlDC->GetTextMetrics(&tm);
m_nCharWidth = tm.tmAveCharWidth;
double d = (double)pDC->GetDeviceCaps(LOGPIXELSY)/(double)pCtlDC->GetDeviceCaps(LOGPIXELSY);
ReleaseDC(pCtlDC);
// Find rows per page
int nPageHeight = pDC->GetDeviceCaps(VERTRES);
m_nRowsPerPage = (int)((double)nPageHeight/d)/m_nRowHeight-TOP_MARGIN-BOTTOM_MARGIN;
// Set maximum pages
int pages=(ItemCount-1)/m_nRowsPerPage+1;
pInfo->SetMaxPage(pages);
// Create a memory DC compatible with the paint DC
CPaintDC dc(this);
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
// Select a compatible bitmap into the memory DC
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rcBounds.Width(), rcBounds.Height() );
MemDC.SelectObject(&bitmap);
// Enlarge window size to include the whole print area boundary
GetWindowPlacement(&WndPlace);
MoveWindow(0,0,::GetSystemMetrics(SM_CXEDGE)*2+rcBounds.Width(),
::GetSystemMetrics(SM_CYEDGE)*2+rcBounds.Height(),FALSE);
ShowScrollBar(SB_BOTH,FALSE);
// Call the default printing
Tree->EnsureVisible(Tree->GetRootItem());
CWnd::DefWindowProc( WM_PAINT, (WPARAM)MemDC.m_hDC, 0 );
// Now create a mask
CDC MaskDC;
MaskDC.CreateCompatibleDC(&dc);
CBitmap maskBitmap;
// Create monochrome bitmap for the mask
maskBitmap.CreateBitmap( rcBounds.Width(), rcBounds.Height(), 1, 1, NULL );
MaskDC.SelectObject( &maskBitmap );
MemDC.SetBkColor( ::GetSysColor( COLOR_WINDOW ) );
// Create the mask from the memory DC
MaskDC.BitBlt( 0, 0, rcBounds.Width(), rcBounds.Height(), &MemDC,
rcBounds.left, rcBounds.top, SRCCOPY );
// Copy image to clipboard
CBitmap clipbitmap;
clipbitmap.CreateCompatibleBitmap(&dc, rcBounds.Width(), rcBounds.Height() );
CDC clipDC;
clipDC.CreateCompatibleDC(&dc);
CBitmap* pOldBitmap = clipDC.SelectObject(&clipbitmap);
clipDC.BitBlt( 0, 0, rcBounds.Width(), rcBounds.Height(), &MemDC,
rcBounds.left, rcBounds.top, SRCCOPY);
OpenClipboard();
EmptyClipboard();
SetClipboardData(CF_BITMAP, clipbitmap.GetSafeHandle());
CloseClipboard();
clipDC.SelectObject(pOldBitmap);
clipbitmap.Detach();
// Copy the image in MemDC transparently
MemDC.SetBkColor(RGB(0,0,0));
MemDC.SetTextColor(RGB(255,255,255));
MemDC.BitBlt(rcBounds.left, rcBounds.top, rcBounds.Width(), rcBounds.Height(),
&MaskDC, rcBounds.left, rcBounds.top, MERGEPAINT);
CPalette pal;
hDIB=DDBToDIB(bitmap, BI_RGB, &pal );
}
void CPrtTViewView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
CTreeView::OnPrepareDC(pDC, pInfo);
// Map logical unit of screen to printer unit
pDC->SetMapMode(MM_ANISOTROPIC);
CClientDC dcScreen(NULL);
pDC->SetWindowExt(dcScreen.GetDeviceCaps(LOGPIXELSX),dcScreen.GetDeviceCaps(LOGPIXELSX));
pDC->SetViewportExt(pDC->GetDeviceCaps(LOGPIXELSX),pDC->GetDeviceCaps(LOGPIXELSX));
}
void CPrtTViewView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
// Save dc state
int nSavedDC = pDC->SaveDC();
// Set font
CFont Font;
LOGFONT lf;
CFont *pOldFont = GetFont();
pOldFont->GetLogFont(&lf);
lf.lfHeight=m_nRowHeight-1;
lf.lfWidth=0;
Font.CreateFontIndirect(&lf);
pDC->SelectObject(&Font);
PrintHeadFoot(pDC,pInfo);
pDC->SetWindowOrg(-1*(LEFT_MARGIN*m_nCharWidth),-m_nRowHeight*TOP_MARGIN);
int height;
if (pInfo->m_nCurPage==pInfo->GetMaxPage())
height=rcBounds.Height()-((pInfo->m_nCurPage-1)*m_nRowsPerPage*m_nRowHeight);
else
height=m_nRowsPerPage*m_nRowHeight;
int top=(pInfo->m_nCurPage-1)*m_nRowsPerPage*m_nRowHeight;
pDC->SetBkColor(RGB(255,255,255));
pDC->SetTextColor(RGB(0,0,0));
LPBITMAPINFOHEADER lpbi;
lpbi = (LPBITMAPINFOHEADER)hDIB;
int nColors = lpbi->biClrUsed ? lpbi->biClrUsed : 1 << lpbi->biBitCount;
BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB;
LPVOID lpDIBBits;
if( bmInfo.bmiHeader.biBitCount > 8 )
lpDIBBits = (LPVOID)((LPDWORD)(bmInfo.bmiColors +
bmInfo.bmiHeader.biClrUsed) +
((bmInfo.bmiHeader.biCompression == BI_BITFIELDS) ? 3 : 0));
else
lpDIBBits = (LPVOID)(bmInfo.bmiColors + nColors);
HDC hDC=pDC->GetSafeHdc();
StretchDIBits(hDC, // hDC
0, // DestX
0, // DestY
rcBounds.Width(), // nDestWidth
height, // nDestHeight
rcBounds.left, // SrcX
rcBounds.Height()-top-height, // SrcY
rcBounds.Width(), // wSrcWidth
height, // wSrcHeight
lpDIBBits, // lpBits
&bmInfo, // lpBitsInfo
DIB_RGB_COLORS, // wUsage
SRCCOPY); // dwROP
pDC->SelectObject(pOldFont);
pDC->RestoreDC( nSavedDC );
}
void CPrtTViewView::OnEndPrinting(CDC* pDC, CPrintInfo* pInfo)
{
GlobalFree(hDIB);
SetWindowPlacement(&WndPlace);
RedrawWindow();
}
void CPrtTViewView::PrintHeadFoot(CDC *pDC, CPrintInfo *pInfo)
{
CClientDC dcScreen(NULL);
CRect rc;
rc.top=m_nRowHeight*(TOP_MARGIN-2);
rc.bottom = (int)((double)(pDC->GetDeviceCaps(VERTRES)*dcScreen.GetDeviceCaps(LOGPIXELSY))
/(double)pDC->GetDeviceCaps(LOGPIXELSY));
rc.left = LEFT_MARGIN*m_nCharWidth;
rc.right = (int)((double)(pDC->GetDeviceCaps(HORZRES)*dcScreen.GetDeviceCaps(LOGPIXELSX))
/(double)pDC->GetDeviceCaps(LOGPIXELSX))-RIGHT_MARGIN*m_nCharWidth;
// Print App title on top left corner
CString sTemp;
sTemp=GetDocument()->GetTitle();
sTemp+=" object hierarchy";
CRect header(rc);
header.bottom=header.top+m_nRowHeight;
pDC->DrawText(sTemp, header, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
rc.top = rc.bottom - m_nRowHeight*(BOTTOM_MARGIN-1);
rc.bottom = rc.top + m_nRowHeight;
// Print draw page number at bottom center
sTemp.Format("Page %d/%d",pInfo->m_nCurPage,pInfo->GetMaxPage());
pDC->DrawText(sTemp,rc, DT_CENTER | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
}
HANDLE CPrtTViewView::DDBToDIB( CBitmap& bitmap, DWORD dwCompression, CPalette* pPal )
{
BITMAP bm;
BITMAPINFOHEADER bi;
LPBITMAPINFOHEADER lpbi;
DWORD dwLen;
HANDLE hDIB;
HANDLE handle;
HDC hDC;
HPALETTE hPal;
ASSERT( bitmap.GetSafeHandle() );
// The function has no arg for bitfields
if ( dwCompression == BI_BITFIELDS )
return NULL;
// If a palette has not been supplied use defaul palette
hPal = (HPALETTE) pPal->GetSafeHandle();
if (hPal==NULL)
hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
// Get bitmap information
bitmap.GetObject(sizeof(bm),(LPSTR)&bm);
// Initialize the bitmapinfoheader
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bm.bmWidth;
bi.biHeight = bm.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = bm.bmPlanes * bm.bmBitsPixel;
bi.biCompression = dwCompression;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// Compute the size of the infoheader and the color table
int nColors = (1 << bi.biBitCount);
if ( nColors > 256 )
nColors = 0;
dwLen = bi.biSize + nColors * sizeof(RGBQUAD);
// We need a device context to get the DIB from
hDC = ::GetDC(NULL);
hPal = SelectPalette(hDC,hPal,FALSE);
RealizePalette(hDC);
// Allocate enough memory to hold bitmapinfoheader and color table
hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
if (!hDIB)
{
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
lpbi = (LPBITMAPINFOHEADER)hDIB;
*lpbi = bi;
// Call GetDIBits with a NULL lpBits param, so the device driver
// will calculate the biSizeImage field
GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(), 0L, (DWORD)bi.biHeight,
(LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);
bi = *lpbi;
// If the driver did not fill in the biSizeImage field, then compute it
// Each scan line of the image is aligned on a DWORD (32bit) boundary
if (bi.biSizeImage == 0)
{
bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8)
* bi.biHeight;
// If a compression scheme is used the result may infact be larger
// Increase the size to account for this.
if (dwCompression != BI_RGB)
bi.biSizeImage = (bi.biSizeImage * 3) / 2;
}
// Realloc the buffer so that it can hold all the bits
dwLen += bi.biSizeImage;
if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
hDIB = handle;
else
{
GlobalFree(hDIB);
// Reselect the original palette
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
// Get the bitmap bits
lpbi = (LPBITMAPINFOHEADER)hDIB;
// FINALLY get the DIB
BOOL bGotBits = GetDIBits( hDC, (HBITMAP)bitmap.GetSafeHandle(),
0L, // Start scan line
(DWORD)bi.biHeight, // # of scan lines
(LPBYTE)lpbi // address for bitmap bits
+ (bi.biSize + nColors * sizeof(RGBQUAD)),
(LPBITMAPINFO)lpbi, // address of bitmapinfo
(DWORD)DIB_RGB_COLORS); // Use RGB for color table
if( !bGotBits )
{
GlobalFree(hDIB);
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return hDIB;
}

Comments
I need help
Posted by Legacy on 02/25/2003 12:00amOriginally posted by: Sascha
Hello to everybody.
I have a problem. I set up a new MFC - Project with a dialog based application.
I add a form to my project with a CTreeCtrl on it.
Now I have 2 froms. The first form is my "login-form" and the second is the on with the tree.
Now I want to Insert Items out of an oracle database, in the three when I have a successful login. I want to insert items in the tree while the login form is open.(The other form is closed)
The compiler give me a untreated exception in mfc42.dll.
Must I open the form with the tree for that operation?
Did I need a pointer?
What errors did I made?
Could anybody help me?
My code for the Tree is : (inster)
TV_INSERTSTRUCT TreeCtrlItem;
ReplyTreeCtrlItem.hParent = TVI_ROOT;
TreeCtrlItem.hInsertAfter = TVI_LAST;
TreeCtrlItem.item.mask = TVIF_TEXT | TVIF_PARAM;
TreeCtrlItem.item.pszText = "SAMPLES";
TreeCtrlItem.item.lParam = 0;
HTREEITEM hTreeItem1 =
m_structure_tree.InsertItem(&TreeCtrlItem);
Very Nice Work
Posted by Legacy on 11/07/2002 12:00amOriginally posted by: William Campbell
Anyone needing to do any printing in an MDI /SDI app need not look further than this code, excellent.
Reply
Vertical Scroll Bar Fix
Posted by Legacy on 05/06/2002 12:00amOriginally posted by: Paul Belikian
http://www.codeproject.com/treectrl/prttview.asp#xx2329xx
Reply
If no default printer
Posted by Legacy on 08/28/2001 12:00amOriginally posted by: Mary
if there is no default printer, on closing the print preview the tree view is lost....
can you help me on how to display the print preview contents on a screen even if there is no printer attached.
ReplyPrinting large trees/lists
Posted by Legacy on 06/27/2001 12:00amOriginally posted by: Dave Barratt
I converted Koay Kah Hoe excellent code to print a virtual list view in report mode with good results. But I was wondering if anyone has a technique for overcoming the size limitation of approx 25 pages caused by stretching the bitmap beyond memory limitations?
If anyone wants the list view print code I can let them have it.
ReplyIs it possible to convert to an OCX?
Posted by Legacy on 12/14/2000 12:00amOriginally posted by: Ram
Hi
Is it possible to convert this to an OCX file, so that it can be used in a VB application. Can anyone give any ideas regarding this.
Thanks for ur help
ReplyGlobalReAlloc
Posted by Legacy on 05/11/2000 12:00amOriginally posted by: Janet Aiken
I'm running with BoundsChecker on in VCPP 6.0. When it hits the GlobalReAlloc function I get a memory leak error. Anyone have any ideas why or how I can fix this. Thanks
ReplyExcellent
Posted by Legacy on 02/10/2000 12:00amOriginally posted by: Dave Barratt
This has really help me, thank you! I did a quick conversion to get this to print a list view and it worked first time. One problem is that the list header (report mode) doesn't print properly - any idea's on a fix?
ReplyIF YOU FIND THE LINK DON'T WORK, LOOK AT THIS
Posted by Legacy on 10/30/1999 12:00amOriginally posted by: Koay Kah Hoe
The link should work, but it cannot be accessed directly from Codeguru page. Sorry for that.
You can use the following link:
http://www.codeguru.com/treeview/PrtTView.zip
or keying in the following URL at your browser's address bar:
http://www.geocities.com/SiliconValley/Lakes/6165/prttview.zip
Sorry for the inconvenience.
Reply"download projet demo" link don't work !
Posted by Legacy on 10/29/1999 12:00amOriginally posted by: gerti
"download projet demo" link don't work !
ReplyLoading, Please Wait ...