65.9K
CodeProject 正在变化。 阅读更多。
Home

支持打印的树视图

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8投票s)

2000年1月18日

CPOL
viewsIcon

137834

downloadIcon

1944

用于向树形视图添加打印功能的代码。

打印树形视图不像调用 WM_PAINT 消息那么简单,默认打印只会打印树形视图的可见部分。在 Mike Wild 撰写的文章 支持打印的树形控件 中,已经完成了一项非常好的工作。但是,树形项目本身的绘制需要由程序完成,这需要大量的程序代码。 在本文中,我提出了一种更简单的方法,通过调用默认的 WM_PAINT 消息来绘制树形视图。 通过一些技巧,程序可以打印树形视图的整个区域,而不受当前窗口大小的限制(水平和垂直方向)。 此外,在打印期间会删除树形视图窗口的背景颜色,因为通常在打印期间不需要背景颜色。 程序还会自动分页,因为树形视图可能超过一页。 标题和页脚也会插入到打印中。

该程序的技巧在于,程序会扩大窗口大小以覆盖树形视图的整个边界,然后程序调用 WM_PAINT 消息以对 DC 执行默认打印。 然后删除 DC 的背景颜色。 代码是从文章 设置背景颜色 修改而来。 准备好的设备相关形式的树形位图不能直接发送到打印机 DC,可能会获得意外的结果。 使用 DDBToDIB() 函数将设备相关位图转换为 DIB。 此函数是从 将 DDB 转换为 DIB 复制而来。 之后,使用 StretchDIBits() 函数将 DIB 发送到打印机 DC。

如果树形视图比纸张大小长,程序将确定每页的最大行数,并相应地将树形视图分页为几页。 打印后,窗口将恢复到其原始大小和位置。 除了打印树形视图外,程序还会将准备好的树形视图位图复制到剪贴板,供用户将位图保存到其他位置。

树形视图头文件

必须使用类向导声明几个消息处理函数,即 OnPreparePrinting()OnBeginPrinting()OnPrepareDC()OnPrint()OnEndPrinting()

在树形视图头文件中,包含以下变量声明和函数声明

// 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 );
<!-- end the block of source code -->

树形视图实现文件

在树形视图构造函数中,添加以下行。 这是指向更容易访问与树形视图关联的 CTreeCtrl 类的指针。

CPrtTViewView::CPrtTViewView()
{
    Tree=&GetTreeCtrl();
}
<!-- end the block of source 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;
}
<!-- end the block of source code -->
© . All rights reserved.