略谈如何在对话框创建视图类画图

作者:朱金灿

来源:blog.csdn.net/clever101

 

     在对话框上画图可以通过添加控件来进行,但这种画图有一个严重弊端就是画图范围受控件范围控制。最近做一个程序,需要一个数据报告窗口,因为输出的内容比较多,格式不一致(涉及多个表,但每个表的数据要严格对齐),所以如ListControl等控件并不适合。所以我想到在对话框上创建视图类上进行数据显示。

 

一是视图窗口如何动态创建。首先是视图窗口的定位。一般的动态创建窗口定位窗口的位置不太容易。我从网上找到的一个好办法在对话框上加一个静态文本控件,然后把视图创建在该控件之上。这个问题想好,创建就基本完成了。

 

创建的具体步骤如下:

1.     定义一个派生自CScrollView类的视图类CDrawView(至于为什么不是CView而是CScrollView,原因我会在下面谈)。

 

2. 在对话框类上定义一个CDrawView类指针*m_pDrawView

具体代码如何:

BOOL CStaticDataReport::OnInitDialog() { CDialog::OnInitDialog(); UINT TargetCtrlID = IDC_STATIC; CWnd *pWnd = this->GetDlgItem(TargetCtrlID); CRect RectTargetCtrl; pWnd->GetWindowRect(RectTargetCtrl); pWnd->DestroyWindow(); this->ScreenToClient(RectTargetCtrl); //在目标位置动态创建CScrollView if (NULL==m_pDrawView) { return FALSE; } m_pDrawView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW|WS_VSCROLL|WS_HSCROLL, RectTargetCtrl, this, TargetCtrlID); m_pDrawView->ShowWindow(SW_SHOW); return TRUE; }

二.前面我已经谈到了在对话框上绘图的一个弊端是绘图范围受控件范围所谓,一不小心就会出界,如下图所示:

 

  

 使用视图类的好处是你可以使用滚动条扩大绘图范围,这也是我为何将自定义视图类继承CScrollView类的原因。

 

   实际上滚动条的处理也是不太容易的,主要是滚动条添加后如何重绘新的显示范围比较麻烦。为此我重翻petzod的名著《Windows程序设计》的里面一节:建立更好的滚动。将里面的Win API代码改为MFC实现。因为我的程序是输出文字的,我就以如何在文字输出视图窗口创建滚动条。

 

   首先要创建窗口的滚动条,你就必须在动态创建是指定两种窗口风格:WS_VSCROLL|WS_HSCROLL

 

   其次你需要指定窗口的滚动范围,具体就是滚动的最大高度和宽度。我的做法是在视图类定义两个变量:

    int m_iMaxWidth;    // 滚动的的最大宽度

    int m_MaxNumLines;  // 滚动的最大高度

 

这个你可以自定义滚动范围。

 

其次你还要定义一些文字大小的相关变量:

    int m_cxChar;

    int m_cxCaps;

    int m_cyChar;

 

OnCreate函数()(WM_CREATE消息映射函数)获取字体大小,代码如下:

 

 

int CDrawView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CScrollView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here CDC *pDC = GetDC(); TEXTMETRIC tm ; pDC->GetTextMetrics(&tm) ; m_cxChar = tm.tmAveCharWidth ; m_cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * m_cxChar / 2 ; m_cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC(pDC); return 0; }

 

OnSize函数(WM_SIZE消息映射函数)设置滚动范围:

void CDrawView::OnSize(UINT nType, int cx, int cy) { SCROLLINFO si ; si.cbSize = sizeof (si) ; si.fMask = SIF_RANGE | SIF_PAGE ; si.nMin = 0 ; si.nMax = m_MaxNumLines - 1 ; si.nPage = cy/m_cyChar ; SetScrollInfo (SB_VERT, &si, TRUE) ; // Set horizontal scroll bar range and page size si.cbSize = sizeof (si) ; si.fMask = SIF_RANGE | SIF_PAGE ; si.nMin = 0 ; si.nMax = m_iMaxWidth; si.nPage = cx/m_cxChar ; SetScrollInfo (SB_HORZ, &si, TRUE) ; }

分别响应WM_VSCROLL消息和WM_HSCROLL,主要目的是设置滚定信息和决定是否要更新窗口:

void CDrawView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default SCROLLINFO si ; si.cbSize = sizeof (si) ; si.fMask = SIF_ALL ; GetScrollInfo (SB_VERT, &si) ; // Save the position for comparison later on int iVertPos = si.nPos ; switch (nSBCode) { case SB_TOP: si.nPos = si.nMin ; break ; case SB_BOTTOM: si.nPos = si.nMax ; break ; case SB_LINEUP: si.nPos -= 1 ; break ; case SB_LINEDOWN: si.nPos += 1 ; break ; case SB_PAGEUP: si.nPos -= si.nPage ; break ; case SB_PAGEDOWN: si.nPos += si.nPage ; break ; case SB_THUMBTRACK: si.nPos = si.nTrackPos ; break ; default: break ; } // Set the position and then retrieve it. Due to adjustments // by Windows it may not be the same as the value set. si.fMask = SIF_POS ; SetScrollInfo(SB_VERT, &si, TRUE) ; GetScrollInfo(SB_VERT, &si) ; // If the position has changed, scroll the window and update it if (si.nPos != iVertPos) { ScrollWindow (0, m_cyChar * (iVertPos - si.nPos),NULL, NULL) ; UpdateWindow() ; } CScrollView::OnVScroll(nSBCode, nPos, pScrollBar); } void CDrawView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { // TODO: Add your message handler code here and/or call default // Get the minimum and maximum scroll-bar positions. // Get all the vertical scroll bar information int minpos=0; int maxpos = 2 + m_iMaxWidth/m_cxChar ; SCROLLINFO si ; si.cbSize = sizeof (si) ; si.fMask = SIF_ALL ; // Save the position for comparison later on GetScrollInfo (SB_HORZ, &si) ; int iHorzPos = si.nPos ; switch (nSBCode) { case SB_LEFT: // Scroll to far left. si.nPos = minpos; break; case SB_RIGHT: // Scroll to far right. si.nPos = maxpos; break; case SB_ENDSCROLL: // End scroll. break; case SB_LINELEFT: // Scroll left. if (si.nPos > minpos) si.nPos--; break; case SB_LINERIGHT: // Scroll right. if (si.nPos < maxpos) si.nPos++; break; case SB_PAGELEFT: // Scroll one page left. { // Get the page size. SCROLLINFO info; GetScrollInfo (SIF_ALL, &info) ; // pScrollBar->GetScrollInfo(&info, SIF_ALL); if (si.nPos > minpos) si.nPos = max(minpos, si.nPos - (int) info.nPage); } break; case SB_PAGERIGHT: // Scroll one page right. { // Get the page size. SCROLLINFO info; GetScrollInfo (SIF_ALL, &info) ; // pScrollBar->GetScrollInfo(&info, SIF_ALL); if (si.nPos < maxpos) si.nPos = min(maxpos, si.nPos + (int) info.nPage); } break; case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position si.nPos = nPos; // of the scroll box at the end of the drag operation. break; case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the si.nPos = nPos; // position that the scroll box has been dragged to. break; } si.fMask = SIF_POS ; SetScrollInfo (SB_HORZ, &si, TRUE) ; GetScrollInfo (SB_HORZ, &si) ; // If the position has changed, scroll the window if (si.nPos != iHorzPos) { ScrollWindow (m_cxChar * (iHorzPos - si.nPos), 0,NULL, NULL) ; } CScrollView::OnHScroll(nSBCode, nPos, pScrollBar); }

然后在OnPaint函数(WM_PAINT消息响应函数)你就可以获取当前绘图范围了:

void CDrawView::OnPaint() { CPaintDC dc(this); // device context for painting SCROLLINFO si ; si.cbSize = sizeof (si) ; si.fMask = SIF_POS ; GetScrollInfo(SB_VERT, &si) ; int iVertPos = si.nPos ; // Get horizontal scroll bar position GetScrollInfo (SB_HORZ, &si) ; int iHorzPos = si.nPos ; // Find painting limits int iPaintBeg = max(0,iVertPos + dc.m_ps.rcPaint.top /m_cyChar) ; int iPaintEnd = min(m_MaxNumLines - 1,iVertPos + dc.m_ps.rcPaint.bottom/m_cyChar); int x = 0; int y = 0; int i = 0; int j =0; // iPaintBeg为绘图起始行,iPaintEnd为绘图结束行 for (i = iPaintBeg ; i <= iPaintEnd;i++) { …… } }

效果图如下:

你可能感兴趣的:(windows,api,null,mfc,scroll)