Introduction & How to.
So, a couple of months ago i was in need to take a printout of a TreeView Control. I'm lazy, so i just searched Google for some examples. But the fact is that TreeView is NOT print friendly. That means the Control does not directly support printing. Anyway, i found some people were in need of it, just like me and i saw a couple of examples in MFC. But i didnt find a better solution in them. First thing is that those poeple are trying to take the snap of control using PrintWindow() and passing the bitmap into printers DC. But my TreeView doesn't have a fixed size, as it varies at runtime. It have scrollbars, so if i use that logic, then i'll get only the Visible Client area only(Current Visible only). This wont work with a tree control having items more than its visible client area. I need to print the Whole tree area including non-visible client area. So, after having some calculations, keeping the above method too in mind, i came into an Idea. the Logic is.. I just made a Memory DC (empty one) and calculated the TreeView Control's Maximum Horizontal value and Maximum Vertical Scroll value. From that i can calculate how long and wide is the tree expanded. Now i took the Snap of the TreeControl using PrintWindow() API using the new maximum boundaries (rcBounds in this code). and i just copied it into the empty DC, which i already created in Memory. from there, i calculated how many pages are required and how many elements(items in a treeview) need to be or can be printed in a single page. Then i calculated the resolution of printer and Display DC by using GetDeviceCaps() API. And the StretchBlt() API is used for bit blitting the copied Bitmap into the Printer DC. I hope this will be a little help to all those who are looking for printing a TreeView Control in Win32 VC++.
Ahem.. Sorry, i commented less.. but i believe that i've covered the logic above, so there'll be no more difficulty for you to understand the code. MSDN is there, so you can refer for API's there...
I hope this helps you. Post your comments and let me know your feedback :)
Code.
BOOL PrintTreeWindow( TREE wHand )
{
BOOL bSucess;
HDC hPrinterDC;
DOCINFO di;
//GetPrinterDC() is userdefined, check below.
hPrinterDC = GetPrinterDC();
if( !hPrinterDC )
{
MessageBox( appWindow, TEXT("Failed Creating Printer DC, GetPrinterDC()
Failed!"), TEXT("Error!"), MB_ICONERRORMB_OK );
bSucess = false;
return bSucess;
}
RECT rc;
//get rectangle co-ordinates of our Treewindow control.
GetClientRect( treeHandle, &rc );
RECT rcBounds;
//Get the handle to root item(First one, on top).
HTREEITEM hItem = TreeView_GetRoot( treeHandle );
//Calculate the rectangle bounds of Root item - To calculate how many items can
be displayed in a page while printing.
TreeView_GetItemRect( treeHandle, hItem, &rcBounds, TRUE );
int nRowHeight = rcBounds.bottom - rcBounds.top;
// Find the total number of visible items & the right most coordinate
int ItemCount=0;
do
{
ItemCount++;
RECT rc;
TreeView_GetItemRect( treeHandle, hItem, & rc, TRUE );
if ( rc.right > rcBounds.right )
rcBounds.right = rc.right;
hItem = TreeView_GetNextItem( treeHandle, hItem, TVGN_NEXTVISIBLE );
}
while ( hItem );
//find the entire printing boundary
int ScrollMin, ScrollMax;
GetScrollRange( treeHandle, SB_HORZ, &ScrollMin, &ScrollMax );
rcBounds.left = 0;
if ( ScrollMax > &rcBounds.right )
rcBounds.right = ScrollMax + 1;
rcBounds.top = 0;
rcBounds.bottom = nRowHeight * ItemCount;
// Get the text width
HDC hDC = GetDC( treeHandle );
if ( NULL == hDC ) return false;
TEXTMETRIC tm;
GetTextMetrics( hDC, &tm );
int nCharWidth = tm.tmAveCharWidth;
double d = (double)GetDeviceCaps( hDC, LOGPIXELSX )/(double)GetDeviceCaps( hDC,
LOGPIXELSY );
// Find rows per page
int nPageHeight = GetDeviceCaps(hPrinterDC, VERTRES);
int nRowsPerPage = (int)( (double)nPageHeight / d ) / nRowHeight - TOP_MARGIN -
BOTTOM_MARGIN;
int nHeight = nRowsPerPage * ( rc.bottom - rc.top );
// Set maximum pages...
int pages = ( ItemCount - 1 ) / nRowsPerPage + 1;
int cx = rc.right - rc.left;
int cy = rc.bottom - rc.top;
int cxStretch, cyStretch;
cxStretch = GetDeviceCaps( hPrinterDC, HORZRES );
cyStretch = GetDeviceCaps( hPrinterDC, VERTRES );
WINDOWPLACEMENT WndPlace;
//Getting current placement of Tree, including scroll positions.
GetWindowPlacement( treeHandle, &WndPlace );
MoveWindow( treeHandle, 0, 0, ::GetSystemMetrics( SM_CXEDGE ) * 2 + (
rcBounds.right - rcBounds.left ), ::GetSystemMetrics( SM_CYEDGE ) * 2 + (
rcBounds.bottom - rcBounds.top ), FALSE );
// Avoided the scroll bars from printing, so disabling temporarily.
ShowScrollBar( treeHandle, SB_BOTH, FALSE );
cx = rcBounds.right - rcBounds.left;
cy = rcBounds.bottom - rcBounds.top;
HDC hMemDC = CreateCompatibleDC( hDC );
HBITMAP hbitmap = CreateCompatibleBitmap( hDC, cx, cy);
ReleaseDC( treeHandle, hDC );
if ( hbitmap )
{
SelectObject( hMemDC, hbitmap);
ShowScrollBar( treeHandle, SB_BOTH, FALSE );
SendMessage( treeHandle, WM_PAINT, (WPARAM)hMemDC, 0 );
// Init the DOCINFO and start the document.
InitDocStruct( &di, _T("TreePrinting"));
StartDoc( hPrinterDC, &di );
int y = 0;
for ( int i = 0; i < 3; i++ ) // for printing 3 pages.
{
//here, i used to divide into 3 pages.Use a better calculation
for the second last parameter of StretchBlt().
StartPage( hPrinterDC );
StretchBlt( hPrinterDC, 0, 0, cxStretch, cyStretch,
hMemDC, 0, y, cx, nRowHeight * ( 126 / 3), SRCCOPY );
EndPage( hPrinterDC );
y += ( nRowHeight * 42 );
}
EndDoc( hPrinterDC ); //Indicating the print job is finished.
DeleteObject( hbitmap ); //No need to hold bitmap in memory anymore.
}
//Memory DC is no more needed.
DeleteDC( hMemDC );
//Delete Printer DC when done.
DeleteDC(hPrinterDC);
//Revert the window to its previous placement including scroll position.
SetWindowPlacement( treeHandle, &WndPlace );
//Show the scrollbars into Treewindow back.
ShowScrollBar( treeHandle, SB_BOTH, TRUE );
bSucess = true;
return bSucess;
}
GetPrinterDC()
HDC GetPrinterDC( void )
{
PRINTDLG pdlg;
// Initialize the PRINTDLG structure.
memset( &pdlg, 0, sizeof( PRINTDLG ) );
pdlg.lStructSize = sizeof( PRINTDLG );
// Set the flag to return printer DC.
pdlg.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC;
// Invoke the printer dialog box.
if( PrintDlg( &pdlg )== TRUE)
{
// hDC member of the PRINTDLG structure contains
// the printer DC.
return pdlg.hDC;
}
else
{
MessageBox(NULL,L"GetPrinterDC Failed!!!",L"Error",MB_ICONERROR | MB_OK);
return false;
}
}
Search This Blog
Saturday, November 22, 2008
Subscribe to:
Post Comments (Atom)
This is really nice. thanks for sharing and it really helped me. Thanks
ReplyDeleteThanks a lot for sharing your work!
ReplyDeleteBut i don't understand why it is necessary to use a bitmap. Why can't we just use PrintWindow(treeHandle,hPrinterDC,NULL) ??