YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
XD++ Library
DocVizor
TFC Library
Free Products
Technical Support
UCanCode.net


Get Ready to Unleash the Power of UCanCode .NET


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


MFC CListCtrl: Print the contents of the list control,  VC++ Source Code

 
 

This code was contributed by Ravi Reddy.

I tried to use the Print code available in your site for List View Control. That was not good enough. The sample sends WM_PAINT message to itself after adjusting printer DC. The problem with this is this will only print currently visible items only and will not take care of Margins etc. It does not take care of number of pages etc also.

I wrote a slightly enhanced printing functionality here. This printing is divided into two main functions on the MFC line. OnBeginPrint and OnPrint as show in the following code. These will ideally be called form view OnFileBeginPrint and OnFilePrint functions.

I have a string list to take the header content. Each string in a string list will be painted in a separate row.

It has a DrawRow functions which will paint the row on screen (in case of Owner draw control fron OnDrawItem code) or on printer. As a matter of fact my CSapListControl has a lot of functionality like, full row selection, alternate color bands, TreeLike List controlwith expand and collpase features, virtaul function for Sorting, OnGetDispInfo etc. All these functions will be called depending on the flags set. For exaple it has OnDrawItem function, it will  not be called if the Owner draw flag is set. All my list controls in my project are derived from this. I will send you all the code for this class, when I get some time to write description for all the functions.

All the members starting with m_ are class variables.


#define        HEADER_HEIGHT       4
#define        FOOTER_HEIGHT       3
#define        LEFT_MARGIN         8
#define        RIGHT_MARGIN        4

void CSAPListCtrl::OnBeginPrint(CDC *pDC, CPrintInfo *pInfo)
{
     // OnBeginPrinting() is called after the user has committed to
     // printing by OK'ing the Print dialog, and after the framework
     // has created a CDC object for the printer or the preview view.

     // This is the right opportunity to set up the page range.
     // Given the CDC object, we can determine how many rows will
     // fit on a page, so we can in turn determine how many printed
     // pages represent the entire document.

     if(NULL == pDC || NULL == pInfo)
          return;

     int  nMaxRowCount = GetItemCount();
     if(!nMaxRowCount)
          return;

     //let us do all dimesions in ListControl units (Device) rather than
     //printer device units. Since we have more control on them
     CDC  *pCtlDC = GetDC();
     if(NULL == pCtlDC)
          return ;
     TEXTMETRIC tm;
     pCtlDC->GetTextMetrics(&tm);
     m_nCharWidth = tm.tmAveCharWidth;

     pDC->SetMapMode(MM_ANISOTROPIC);

     CRect     rc;
     GetItemRect(0, &rc, LVIR_BOUNDS);
     m_nRowHeight = rc.Height();

     int nMargins = (LEFT_MARGIN+RIGHT_MARGIN)*m_nCharWidth;
     //this will optimize the column widths. If we have more column than 
     //screen width and horizontal scroll on List control
     //this will reduce the column widths proportonately to fit all of them 
     //on the page. If we have fewer column all the column
     //widths will increased propertionately.
     pDC->SetWindowExt(rc.Width() + nMargins, pCtlDC->GetDeviceCaps(LOGPIXELSX));

     pDC->SetViewportExt(pDC->GetDeviceCaps(HORZRES),pDC->GetDeviceCaps(LOGPIXEL SX));
     double d = (double)pDC->GetDeviceCaps(LOGPIXELSY)/(double)pCtlDC->GetDeviceCaps(LOGPIX ELSY);
     ReleaseDC(pCtlDC);

     nMargins = (int)(m_nRowHeight*d);
     int nPageHeight     = pDC->GetDeviceCaps(VERTRES);
     m_nRowsPerPage = nPageHeight/nMargins;   //nMargins reused
     m_nRowsPerPage -= (HEADER_HEIGHT+FOOTER_HEIGHT);
     m_nRowsPerPage -= 1; //adjustment for list control header
     int nMaxPage = nMaxRowCount/m_nRowsPerPage + 1;
     pInfo->SetMaxPage(nMaxPage);
     pInfo->m_nCurPage = 1;  // start printing at page# 1
}

void CSAPListCtrl::OnPrint(CDC *pDC, CPrintInfo *pInfo)
{
     if(NULL == pDC || NULL == pInfo)
          return;

     // Draw as many rows as will fit on the printed page.
     // Clip the printed page so that there is no partially shown
     // row at the bottom of the page (the same row which will be fully
     // shown at the top of the next page).
     int       nStartRow = 0;
     int       nEndRow = 0;
     int       nMaxRowCount = GetItemCount();

     nStartRow = (pInfo->m_nCurPage - 1)*m_nRowsPerPage;
     nEndRow = nStartRow+m_nRowsPerPage;
     if(nEndRow > nMaxRowCount)
          nEndRow = nMaxRowCount;

     //create bold font for header and footer
     CFont     *pOldFont = NULL;
     CFont     BoldFont;
     LOGFONT  lf;
     pOldFont = GetFont();;
     pOldFont->GetLogFont(&lf);
     lf.lfWeight = FW_BOLD;
     BoldFont.CreateFontIndirect(&lf);

     pOldFont = pDC->SelectObject(&BoldFont);
     int nPrevBkMode = pDC->SetBkMode(TRANSPARENT);
     //print the header
     PrintHeader(pDC, pInfo);
     //Print the footer
     PrintFooter(pDC, pInfo);
     pDC->SetBkMode(nPrevBkMode);
     pDC->SelectObject(pOldFont);
     BoldFont.DeleteObject();

     //Set origin to print header and Header control. Keep Y position at 0 to
     //print header information
     //Change Y position of origin to print List control header
     //Adjust the List control origin to start printing after page margins
     pDC->SetWindowOrg(-1*(LEFT_MARGIN*m_nCharWidth), -1*HEADER_HEIGHT*m_nRowHeight);

     //send a message to Header control to print itsef. There is little scope to improve printing header.
     //I have m_HeaderCtrl in my class. If not take by HeaderCtrl = GetDlgItem(0);
     m_HeaderCtrl.SendMessage(WM_PAINT, (WPARAM)pDC->m_hDC);

     //Chage window position to take care of List Control Horizontal scrolling.
     //if List control is scrolled to left horizontally the above window origin
     //will not start painting from first column, instead it starts painting from
     //first visible column, because rcBounds etc are will have -ve left value
     //Same thing with vertical scrolling also
     CRect     rcBounds;
     GetItemRect(nStartRow, &rcBounds, LVIR_BOUNDS);

     //offset top margin of rcBounds by ListControl header
     CRect     rc;
     m_HeaderCtrl.GetClientRect(&rc);
     rcBounds.OffsetRect(0, -rc.Height());
     pDC->OffsetWindowOrg(rcBounds.left, rcBounds.top);
     //start printing rows
     for(int i = nStartRow; i < nEndRow; i++)
          DrawRow(pDC, i);

     //SetWindowOrg back for next page
     pDC->SetWindowOrg(0,0);
     return;
}

void CSAPListCtrl::PrintHeader(CDC *pDC, CPrintInfo *pInfo)
{
     CRect               rc(pInfo->m_rectDraw);
     CString             sTemp;

     //Print App title
     rc.left += LEFT_MARGIN*m_nCharWidth;
     rc.right -= RIGHT_MARGIN*m_nCharWidth;
     rc.bottom = rc.top+m_nRowHeight;

     //print App title on top right margin
     sTemp.LoadString(AFX_IDS_APP_TITLE);
     pDC->DrawText(sTemp, &rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);

     //print Header. One row for each string
     POSITION  pos = NULL;
     pos = m_HeaderList.GetHeadPosition();
     while(pos)
     {
          sTemp = m_HeaderList.GetNext(pos);
          pDC->DrawText(sTemp, &rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER);
          rc.OffsetRect(0, m_nRowHeight);
     }
}

//print footer with a line and date, and page number
void CSAPListCtrl::PrintFooter(CDC *pDC, CPrintInfo *pInfo)
{
     CRect     rc(pInfo->m_rectDraw);

     //draw line
     rc.left += LEFT_MARGIN*m_nCharWidth;
     rc.right -= RIGHT_MARGIN*m_nCharWidth;
     rc.top = rc.bottom - FOOTER_HEIGHT*m_nRowHeight;
     rc.bottom = rc.top + m_nRowHeight;
     pDC->MoveTo(rc.left, rc.top);
     pDC->LineTo(rc.right, rc.top);

     //draw page number
     CString   sTemp ;
     rc.OffsetRect(0, m_nRowHeight/2);
     sTemp.Format(IDS_PRINT_PAGE_TITLE, pInfo->m_nCurPage);
     pDC->DrawText(sTemp,-1,rc, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);

     CTime     t = CTime::GetCurrentTime();
     sTemp = t.Format("%c");
     pDC->DrawText(sTemp,-1,rc, DT_RIGHT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER);
}

//this is drawing code copied RowList sample and from www.codeguru.com site
int CSAPListCtrl::DrawRow(CDC *pDC, int nItem)
{
     CImageList          *pImageList = NULL;
     CFont               *pOldFont = NULL;
     CFont               BoldFont;
     CString             sLabel;
     UINT           dtFlags = DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER;
     int                 nSaveDC = pDC->SaveDC();

     if(!pDC->IsPrinting())
          dtFlags |= (DT_NOCLIP | DT_END_ELLIPSIS);     //no clip because we add ellipsis at the end
     // get item data
     LV_ITEM lvi;
     lvi.mucancode.net = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
     lvi.iItem=nItem;
     lvi.iSubItem=0;
     lvi.stateMucancode.net=0xFFFF;         // get all state flags
     GetItem(&lvi);

     BOOL bHighlight = ((lvi.state & LVIS_DROPHILITED) || ((lvi.state & LVIS_SELECTED)
                    && ((GetFocus() == this) || (GetStyle() & LVS_SHOWSELALWAYS))));

     //Get rectangles for painting
     CRect     rcBounds, rcLabel, rcIcon;
     GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
     GetItemRect(nItem, rcLabel, LVIR_LABEL);
     GetItemRect(nItem, rcIcon, LVIR_ICON);
     CRect     rcCol(rcBounds);

     CRect     rcWnd;
     sLabel = GetItemText(nItem, 0);
     //Label offset
     int offset = pDC->GetTextExtent(_T(" "), 1).cx;

     CRect     rcHighlight;
     int       nExt = 0;
     switch(m_nHighlight)
     {
     case HIGHLIGHT_NORMAL:
          nExt = pDC->GetOutputTextExtent(sLabel).cx + offset;
          rcHighlight = rcLabel;
          if(rcLabel.left + nExt < rcLabel.right)
               rcHighlight.right = rcLabel.left + nExt;
          break;

     case HIGHLIGHT_ALLCOLUMNS:
          rcHighlight = rcBounds;
          rcHighlight.left = rcLabel.left;
          break;

     case HIGHLIGHT_ROW:
          GetClientRect(&rcWnd);
          rcHighlight = rcBounds;
          rcHighlight.left = rcLabel.left;
          rcHighlight.right = rcWnd.right;
          break;

     default:
          rcHighlight.left = rcLabel.left;
          break;
     }

     //draw highlight. printing may not be required
     if(bHighlight)
     {
          pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
          pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
          pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
     }
     else {
          pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
          pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_WINDOW)));
     }

     //set clip region
     rcCol.right = rcCol.left + GetColumnWidth(0);

     //Nice to have regions but they are not working on printer DC we may need
     //to take get device caps to support regions. Does not seems to help much now
     //CRgn    rgn;
     //rgn.CreateRectRgnIndirect(&rcCol);
     //pDC->SelectClipRgn(&rgn);
     //rgn.DeleteObject();

     //Draw state icon
     if(lvi.state & LVIS_STATEIMAGEMucancode.net)
     {
          int nImage = ((lvi.state & LVIS_STATEIMAGEMucancode.net) >> 12) - 1;
          pImageList = GetImageList(LVSIL_STATE);
          //offset the state image icon indent levels.
          nExt = rcCol.left + lvi.iIndent*rcIcon.Width();    //nExt reused
          if(pImageList)
               pImageList->Draw(pDC, nImage, CPoint(nExt, rcCol.top), ILD_TRANSPARENT);
     }

     //Draw Normal and overlay icon
     pImageList = GetImageList(LVSIL_SMALL);  //assuming printing in report mode only
     if(pImageList)
     {
          UINT nOvlImageMucancode.net = lvi.state & LVIS_OVERLAYMucancode.net;
          pImageList->Draw(pDC, lvi.iImage, CPoint(rcIcon.left, rcIcon.top), 
			(bHighlight?ILD_BLEND50:0)|ILD_TRANSPARENT|nOvlImageMucancode.net);
     }

     //if state image mucancode.net is on and indent is 0 then consider it as Server row
     if((lvi.state & LVIS_STATEIMAGEMucancode.net) && !lvi.iIndent)
     {
          //create bold font
          LOGFONT  lf;
          pOldFont = GetFont();;
          pOldFont->GetLogFont(&lf);
          lf.lfWeight = FW_BOLD;
          BoldFont.CreateFontIndirect(&lf);
          pOldFont = pDC->SelectObject(&BoldFont);
          rcLabel.right = rcBounds.right;     //draw server name to full row width
     }

     //Draw item label
     rcLabel.left += offset/2;
     rcLabel.right -= offset;
     dtFlags |= DT_LEFT;
     pDC->DrawText(sLabel, rcLabel, dtFlags);

     if((lvi.state & LVIS_STATEIMAGEMucancode.net) && !lvi.iIndent)
     {
          pOldFont = pDC->SelectObject(pOldFont);
          BoldFont.DeleteObject();
          //focus rect if required
          if(lvi.state & LVIS_FOCUSED && (GetFocus() == this))
               pDC->DrawFocusRect(rcHighlight);
          pDC->RestoreDC(nSaveDC);
          return 0;
     }

     //dRAW LABELS FOR REMAINING COLUMNS
     LV_COLUMN lvc;
     lvc.mucancode.net = LVCF_FMT|LVCF_WIDTH;

     if(m_nHighlight == HIGHLIGHT_NORMAL)
     {
          pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
          pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
     }
     rcBounds.right = rcHighlight.right > rcBounds.right ? rcHighlight.right:rcBounds.right;

     //Nice to have regions but they are not working on printer DC we may need
     //to take get device caps to support regions. Does not seems to help much now
     //rgn.CreateRectRgnIndirect(&rcBounds);
     //pDC->SelectClipRgn(&rgn);
     //rgn.DeleteObject();

     for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
     {
          rcCol.left = rcCol.right;
          rcCol.right += lvc.cx;

          //draw background if needed
          if(m_nHighlight == HIGHLIGHT_NORMAL)
               pDC->FillRect(rcCol, &CBrush(::GetSysColor(COLOR_WINDOW)));

          sLabel = GetItemText(nItem, nColumn);
          if(sLabel.IsEmpty())
               continue;

          //Get the text justification
          UINT nJustify = DT_LEFT;
          switch(lvc.fmt & LVCFMT_JUSTIFYMucancode.net)
          {
          case LVCFMT_RIGHT:
               nJustify = DT_RIGHT;
               break;

          case LVCFMT_CENTER:
               nJustify = DT_CENTER;
               break;

          default:
               break;
          }
          rcLabel = rcCol;
          rcLabel.left += offset;
          rcLabel.right -= offset;

          dtFlags &= ~DT_RIGHT;
          dtFlags &= ~DT_CENTER;
          dtFlags |= nJustify;
          pDC->DrawText(sLabel, -1, rcLabel, dtFlags);
     }
     //focus rect if required
     if(lvi.state & LVIS_FOCUSED && (GetFocus() == this))
          pDC->DrawFocusRect(rcHighlight);

     pDC->RestoreDC(nSaveDC);
     return 0;
}

void CSAPListCtrl::SetHeaderString(CStringList *HeaderList)
{
     m_HeaderList.RemoveAll();
     m_HeaderList.AddTail(HeaderList);
}

 

 

Copyright ?1998-2007 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net