Printing
a
tree view
is not as
simple as
calling
WM_PAINT
message, the
default
printing
only
prints
visible
parts of the
tree view.
In the
article
contributed
by Mike Wild
entitled
A Print
Enabled Tree
Control
, a really
good job has
been done.
However, the
drawing of
the
tree
items has to
be done by
the program
itself,
which
requires a
long program
code. In
this
article,
I propose a
simpler way
to draw the
tree view
by calling
the default
WM_PAINT
message. The
program can
print the
whole region
of the
tree view
not
restricted
by the
current
window size
(horizontally
and
vertically)
by playing
some tricks.
Also, the
background
color of the
tree view
window is
removed
during
printing,
as
background
color is
normally
unwanted
during
printing.
The program
does
pagination
automatically
as the tree
view might
exceed one
page. Header
and footer
are inserted
in the
printing,
too.
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
determines
the maximum
rows per
page and
paginates
the tree
view into
several
pages
accordingly.
After
printing,
the window
is restored
to its
original
size and
position.
Besides
printing the
tree view,
the program
also copies
the prepared
tree view
bitmap to
the
clipboard
for the 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:
Collapse
Copy
Code
public:
CTreeCtrl* Tree;
protected:
CImageList m_treeicon;
private:
CRect rcBounds;
int m_nCharWidth;
int m_nRowHeight;
int m_nRowsPerPage;
HANDLE hDIB;
WINDOWPLACEMENT WndPlace;
public:
void PrintHeadFoot(CDC *pDC, CPrintInfo *pInfo);
HANDLE DDBToDIB( CBitmap& bitmap,
DWORD dwCompression, CPalette* pPal );
<!-- end the block of source code -->
Tree view
implementation
file
In the
tree view
constructor,
add the
following
line. This
is a pointer
for easier
access to
the
CTreeCtrl
class
associated
with the
tree view.
Collapse
Copy
Code
CPrtTViewView::CPrtTViewView()
{
Tree=&GetTreeCtrl();
}
<!-- end the block of source code -->
After
creating the
message
handlers,
replace them
with the
following
code:
Collapse
Copy
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();
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);
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;
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);
int nPageHeight = pDC->GetDeviceCaps(VERTRES);
m_nRowsPerPage = (int)((double)nPageHeight/d)/
m_nRowHeight-TOP_MARGIN-BOTTOM_MARGIN;
int pages=(ItemCount-1)/m_nRowsPerPage+1;
pInfo->SetMaxPage(pages);
CPaintDC dc(this);
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc,
rcBounds.Width(), rcBounds.Height() );
MemDC.SelectObject(&bitmap);
GetWindowPlacement(&WndPlace);
MoveWindow(0,0,
::GetSystemMetrics(SM_CXEDGE)*2+rcBounds.Width(),
::GetSystemMetrics(SM_CYEDGE)*2+rcBounds.Height(),
FALSE);
ShowScrollBar(SB_BOTH,FALSE);
Tree->EnsureVisible(Tree->GetRootItem());
CWnd::DefWindowProc( WM_PAINT,
(WPARAM)MemDC.m_hDC, 0);
CDC Mucancode.netDC;
Mucancode.netDC.CreateCompatibleDC(&dc);
CBitmap mucancode.netBitmap;
mucancode.netBitmap.CreateBitmap(rcBounds.Width(),
rcBounds.Height(), 1, 1, NULL);
Mucancode.netDC.SelectObject( &mucancode.netBitmap );
MemDC.SetBkColor(::GetSysColor(COLOR_WINDOW));
Mucancode.netDC.BitBlt( 0, 0, rcBounds.Width(),
rcBounds.Height(), &MemDC,
rcBounds.left, rcBounds.top,
SRCCOPY );
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();
MemDC.SetBkColor(RGB(0,0,0));
MemDC.SetTextColor(RGB(255,255,255));
MemDC.BitBlt(rcBounds.left, rcBounds.top,
rcBounds.Width(), rcBounds.Height(),
&Mucancode.netDC, rcBounds.left,
rcBounds.top, MERGEPAINT);
CPalette pal;
hDIB=DDBToDIB(bitmap, BI_RGB, &pal );
}
void CPrtTViewView::OnPrepareDC(CDC* pDC,
CPrintInfo* pInfo)
{
CTreeView::OnPrepareDC(pDC, pInfo);
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)
{
int nSavedDC = pDC->SaveDC();
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, 0, 0, rcBounds.Width(), height, rcBounds.left, rcBounds.Height()-top-height, rcBounds.Width(), height, lpDIBBits, &bmInfo, DIB_RGB_COLORS, SRCCOPY);
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;
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;
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() );
if ( dwCompression == BI_BITFIELDS )
return NULL;
hPal = (HPALETTE) pPal->GetSafeHandle();
if (hPal==NULL)
hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
bitmap.GetObject(sizeof(bm),(LPSTR)&bm);
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;
int nColors = (1 << bi.biBitCount);
if ( nColors > 256 )
nColors = 0;
dwLen = bi.biSize + nColors * sizeof(RGBQUAD);
hDC = ::GetDC(NULL);
hPal = SelectPalette(hDC,hPal,FALSE);
RealizePalette(hDC);
hDIB = GlobalAlloc(GMEM_FIXED,dwLen);
if (!hDIB)
{
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
lpbi = (LPBITMAPINFOHEADER)hDIB;
*lpbi = bi;
GetDIBits(hDC, (HBITMAP)bitmap.GetSafeHandle(),
0L, (DWORD)bi.biHeight,
(LPBYTE)NULL, (LPBITMAPINFO)lpbi,
(DWORD)DIB_RGB_COLORS);
bi = *lpbi;
if (bi.biSizeImage == 0)
{
bi.biSizeImage =
((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8)
* bi.biHeight;
if (dwCompression != BI_RGB)
bi.biSizeImage = (bi.biSizeImage * 3) / 2;
}
dwLen += bi.biSizeImage;
if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE))
hDIB = handle;
else
{
GlobalFree(hDIB);
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
lpbi = (LPBITMAPINFOHEADER)hDIB;
BOOL bGotBits = GetDIBits( hDC,
(HBITMAP)bitmap.GetSafeHandle(),
0L, (DWORD)bi.biHeight, (LPBYTE)lpbi + (bi.biSize + nColors * sizeof(RGBQUAD)),
(LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);
if( !bGotBits )
{
GlobalFree(hDIB);
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return NULL;
}
SelectPalette(hDC,hPal,FALSE);
::ReleaseDC(NULL,hDC);
return hDIB;
}
<!-- end the block of source code -->