在对话框上画图可以通过添加控件来进行,但这种画图有一个严重弊端就是画图范围受控件范围控制。最近做一个程序,需要一个数据报告窗口,因为输出的内容比较多,格式不一致(涉及多个表,但每个表的数据要严格对齐),所以如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 = (CDrawView*)RUNTIME_CLASS(CDrawView)->CreateObject();
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++) { …… }
}
效果图如下: 本文来自CSDN博客,转载请标明出处: http://blog.csdn.net/clever101/archive/2009/01/14/3779089.aspx
An alternative to the often asked view-in-dialog problem using a dialog bar
http://www.codeproject.com/KB/dialog/view_in_dialog.aspx IntroductionI tried to create a View on a Dialog, and thus looked up all the articles on the web. There are several attempts to do this, but more or less all of them produce errors and aren't too useful in my opinion. So I decided to go a different way. I created a new Window type (called But unfortunately I have to deal with an unexpected disadvantage using this simple technique. As the UsageAs you can see in the demo project, I have added my
ConclusionPleas write me an email if you could improve my classes, especially, if you achieve to use all sort of controls in the dialog bar. I'm sorry, but I generated the demo project using the German version of the SDK. Thus the auto-generated comments are in German. This should not prevent you from understanding the code, as all pieces of my own code are commented in English.
Creating a View in Dialog (An easy way).
http://www.codeguru.com/Cpp/W-D/dislog/article.php/c5009/
Environment developed: VC6 Win 2K] To create a view you normally follow the Microsoft's Document template model. (i.e) Single document template or multi doc template. You can pass three runtime classes to the constructors: CSingleDocTemplate( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass ); OR CMultiDocTemplate( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass ); When the document template is added, frame (child for MDI , main frame for SDI), document, and a view is created dynamically and added to the application's document template list. But some times you may need to create a view without using the default document template classes. For instance, if you are to create a view in dialog, you can not use the regular way of document template. In those cases you can use the following method: BOOL CVwindlgDlg::OnInitDialog() { ... CCreateContext pContext; /** * Note:CDialig derived pointer is converted to * CWnd pointer (a common base class for CDialog and CFrameWnd). * Thus casting it back to CFrameWnd is also easy. */ CWnd* pFrameWnd = this; pContext.m_pCurrentDoc = new CMyDocument; pContext.m_pNewViewClass = RUNTIME_CLASS(CMyVw); CMyVw *pView = (CMyVw *) ((CFrameWnd*)pFrameWnd)->CreateView(&pContext); ASSERT(pView); pView->ShowWindow(SW_NORMAL); /** * After a view is created, resize that to * have the same size as the dialog. */ CRect rectWindow; GetWindowRect(rectWindow); /** * Leave a little space for border and title... */ rectWindow.right += 15; rectWindow.top -= 10; pView->MoveWindow(rectWindow); CString str(AfxGetApp()->m_lpCmdLine); /** * Note: "CMyVw" is a CHTMLView derived class to add some * spice to the view, I have provided a HTML page * to which it navigates when the dialog is up. */ char strPath[255]; ::GetCurrentDirectory(255,(LPSTR)(LPCSTR)strPath); strcat(strPath,"//defaultpage.html"); pView->Navigate(strPath); .... return TRUE; // return TRUE unless you set the // focus to a control } DownloadsDownload demo project - 6 KBDownload source - 38 KB
【错误】
ASSERT( pParentFrame == pDesktopWnd || pDesktopWnd->IsChild(pParentFrame));
解决方法:重载OnMouseActive(),屏蔽掉找主框架的代码,就不会出错了。但是在debug模式下可以,但是在release版本下却还是会出错!!!据说代码还是在mfc42.dll里出错的。难道是MFC4.2的问题?
另一种解决办法见下篇文章:
如何在没有文档的情况下使用CView及其派生类
在MFC中,
CView及其派生类将显示及其相关的操作做的很好,比如在程序中
如果我们的视图类继承 CScrollView了那么我们不用写任何代码该视图就能够 具有滚动的响应机制,能够自动根据设置的滚动范围和窗口实际大小确定是 否显示滚动条(水平或者竖直的), CEditView, CRichEditView等也提供了 相应的在不同实用范围内的一些基本操作处理。 通常在程序中,我们需要处理并且显示一些数据,将显示部分如果放到视图中 本文是在处理如下的情况中提出的:在数据采集和分析的时候,常常需要观看很
CRuntimeClass *pViewRuntimeClass=RUNTIME_CLASS(CMyScrollView); CMyScrollView *pView=(CMyScrollView*)pViewRuntimeClass->CreateObject();
// 注意我们在上面用new的方法【具体是在pViewRuntimeClass->CreateObject()的时候】产生了一个 答案是否定的:窗口接受最后一个消息是WM_NCDESTROY,在OnNcDestroy()中CView调用了PostNcDestory这个虚函数
2、在SDI(单文档)程序中的对话框中使用CView 在SDI中的弹出对话框中创建视图,如果你用,上面的方法1做的话,只要你的鼠标不点击视图,就不会有问题(这样的要求 int CView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) CFrameWnd* pParentFrame = GetParentFrame(); // 这里 **********(A) // 省略
单看上面的代码,及其原因我们还是不能决定如何使用CScrollView。 但是观察A处GetParentFrame的实现,却为我们打开了希望之门: GetParentFrame是从CWnd继承而来
ASSERT_VALID(this); CWnd* pParentWnd = GetParent(); // start with one parent up 分析这段代码,发现他的目的就是在他的祖先窗口中上溯,直到找到一个是FrameWnd类型的窗口之后, 返回这个窗口对象的指针(如果没有的话,返回NULL),在SDI中这样的窗口是一定存在的(主框架窗口便是!)。 CRect rectWndClient; CFrameWnd *pFrame= new CFrameWnd(); CRuntimeClass *pViewClass=RUNTIME_CLASS(CMyScrollView); CMyScrollView *pView=(CMyScrollView*)pViewClass->CreateObject();
// 在对话框头文件中,声明一个指向你的视图的指针
m_pFrame= new CFrameWnd(); m_pView=(CMyScrollView*)pViewClass->CreateObject();
//注意在第一次调用OnSize之前视图還沒有创建,这也是要在构造函数中将
if (m_pFrame && m_pView)
{ m_pFrame-> SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER); m_pView-> SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER); }
《浅谈:切换视时基于FormView的对话框属性设置与ASSERT报错的问题
最近做的项目中用到了
FormView切换视图,其主要原理是:先新建一些Dialog对话框,然后给这些对话框绑定对应的View,注意:这些View要基于FormView。
一开始还好好的,利用 切换视的代码进行的很顺利(网上的相关代码很多,我就不赘述了),但是,后来新加了两个Dialog,不知我怎么弄的,奇怪的事发生了:先前添加了Dialog都能正常的切换,但是,一切换新添加的Dialog,每次运行到:
BOOL CFormView::Create(LPCTSTR /*lpszClassName*/, LPCTSTR /*lpszWindowName*/,
DWORD dwRequestedStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) { ASSERT(pParentWnd != NULL); ASSERT(m_lpszTemplateName != NULL);
m_pCreateContext = pContext; // save state for later OnCreate
#ifdef _DEBUG
// dialog template must exist and be invisible with WS_CHILD set <——请注意此处 if (!_AfxCheckDialogTemplate(m_lpszTemplateName, TRUE)) { ASSERT(FALSE); // invalid dialog template name PostNcDestroy(); // cleanup if Create fails too soon
return FALSE;
} #endif //_DEBUG
中的ASSERT时就报错,而在Release版本下却不会报错。我检查了一下这两个新加的Dialog与先前的Dialog属性有哪些不同,原来是自己把后来加的Dialog的Visible属性设置成了TRUE了, 根据代码的要求是:对话框模板必须存在,属性要设置成不可见和子窗口风格。SystemMenu和TitleBar属性最好都设为False,改完以后,一切正常~~
注:还有一种方法就是在添加Dialog资源时,在Dialog列表上点右键->添加资源,选择Dialog->IDD_FORMVIEW,再点“新建”按钮,这样新建出来的Dialog属性就会自动配好了 |
MC view 在对话框的使用方法
最新推荐文章于 2021-01-13 20:55:09 发布