首页 > 代码库 > 在CFormView或对话框中动态添加CScrollView、CFormView
在CFormView或对话框中动态添加CScrollView、CFormView
在CFormView或对话框中动态添加CScrollView、CFormView
本代码可以在CFormView中,根据事先画好的控件位置创建CScrollView
也可以在CDialog中创建CScrollView、CFormView等
注:
若以下代码放在CMainRightView::OnCreate(LPCREATESTRUCT lpCreateStruct)内,则GetDlgItem()函数将调用失败,因为此时控件都还未被创建!
void CMainRightView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
//获得目标位置控件
UINT TargetCtrlID = IDC_STATIC_SCROLLVIEW;
CWnd *pWnd = this->GetDlgItem(TargetCtrlID);
CRect RectTargetCtrl;
pWnd->GetWindowRect(RectTargetCtrl);
pWnd->DestroyWindow();
this->ScreenToClient(RectTargetCtrl);
//在目标位置动态创建CScrollView
CEMapView *pEMapView = (CEMapView*)RUNTIME_CLASS(CEMapView)->CreateObject();
pEMapView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, RectTargetCtrl, this, TargetCtrlID);
//使用CreateView创建的视图 不能自动调用OnInitialUpdate函数,需要人工调用OnInitialUpdate函数或者发送 WM_INITIALUPDATE消息
pEMapView->OnInitialUpdate();
//SetScrollSizes()必须被调用,否则运行时会出ASSERT错误,当然,也可以在目标View内的OnInitialUpdate()中调用
pEMapView->SetScrollSizes(MM_TEXT, CSize(RectTargetCtrl.Width()-10, RectTargetCtrl.Height()-10));
// 使用CreateView创建的视图不会自动显示并且激活,需要人工操作
pEMapView->ShowWindow(SW_SHOW);
}
注:如果需要在CDialog中创建CScrollView、CFormView,则需要在View中overload、override这些View中以下的4个方法,否则会出ASSERT错误
afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message);
afx_msg void OnDestroy();
virtual void PostNcDestroy();
virtual void OnActivateFrame(UINT nState, CFrameWnd* pDeactivateFrame);
int CFormViewPrint::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
}
void CFormViewPrint::OnDestroy()
{
CWnd::OnDestroy();
// TODO: Add your message handler code here
}
void CFormViewPrint::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
CWnd::PostNcDestroy();
}
void CFormViewPrint::OnActivateFrame(UINT nState, CFrameWnd* pDeactivateFrame)
{
// TODO: Add your specialized code here and/or call the base class
CWnd::OnActivateFrame(nState, pDeactivateFrame);
}
原因可参考
View和Control的区别(如何在对话框上使用CView类)
CView继承类,和其他窗口类的区别,很重要的就是对CDocument类和CFrameWnd类的操作,而其中,涉及CDocument类的操作,都进行了有效性判断(m_pDocument != NULL),CView类初始化的时候,m_pDocument = NULL,因此并不影响CView类作为控件的使用。涉及CFrame类的操作,有这么几个地方:
第一个地方:CView::OnDestroy()。
void CView::OnDestroy()
{
CFrameWnd* pFrame = GetParentFrame();
if (pFrame != NULL && pFrame->GetActiveView() == this)
pFrame->SetActiveView(NULL); //deactivate during death
CWnd::OnDestroy();
}
第二个地方:CView::OnActivateFrame()。
void CView::OnActivateFrame(UINT , CFrameWnd* )
{
}
这里,其实是空的,在CView继承类中,只有CFormView类继承了这个虚函数
void CFormView::OnActivateFrame(UINT nState, CFrameWnd* )
{
if (nState == WA_INACTIVE)
SaveFocusControl(); // save focus whenframe loses activation
}
实际上都不需要真的CFrame指针,对CView类作为控件使用没有障碍。
第三个地方:CView::OnMouseActivate()。
int CView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
int nResult = CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
if (nResult == MA_NOACTIVATE || nResult == MA_NOACTIVATEANDEAT)
return nResult; // frame does not want to activate
CFrameWnd* pParentFrame = GetParentFrame();
if (pParentFrame != NULL)
{
// eat it if this will cause activation
ASSERT(pParentFrame == pDesktopWnd ||pDesktopWnd->IsChild(pParentFrame));
// either re-activate the current view, or set this view to beactive
CView* pView = pParentFrame->GetActiveView();
HWND hWndFocus = ::GetFocus();
if (pView == this &&
m_hWnd != hWndFocus && !::IsChild(m_hWnd, hWndFocus))
{
// re-activate this view
OnActivateView(TRUE, this, this);
}
else
{
// activate this view
pParentFrame->SetActiveView(this);
}
}
return nResult;
}
另外,在CView::PostNcDestroy(),实现了CView类的自我销毁,这是因为CView类是可以动态生成的(DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE)。
// self destruction
void CView::PostNcDestroy()
{
// default for views is to allocate them on the heap
// the default post-cleanup is to ‘delete this‘.
// never explicitly call ‘delete‘ on a view
delete this;
基本上,修改了CView继承类的这几个地方,直接返回不要调用基类相应的成员函数,就可以在对话框上使用。
下面举例,用向导生成对话框应用程序,对话框类为CMyDigalog。
从CHTMLView类继承一个CHTMLCtrl类,建立相应的消息处理函数并修改以下几个地方:
// CHTMLCtrl 消息处理程序
int CHTMLCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINTmessage)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
//return CHtmlView::OnMouseActivate(pDesktopWnd,nHitTest, message);
}
void CHTMLCtrl::OnDestroy()
{
//CHtmlView::OnDestroy();
// TODO: 在此处添加消息处理程序代码
CWnd::OnDestroy();
}
void CHTMLCtrl::PostNcDestroy()
{
// TODO: 在此添加专用代码和/或调用基类
//CHtmlView::PostNcDestroy();
}
void CHTMLCtrl::OnActivateFrame(UINT nState, CFrameWnd*pDeactivateFrame)
{
// TODO: 在此添加专用代码和/或调用基类
//CHtmlView::OnActivateFrame(nState,pDeactivateFrame);
}
增加一个成员函数CreateFromCtrl:
public:
BOOL CreateFromCtrl(UINT nID, CWnd* pParent);
这个函数通过对话框上的控件创建一个CHTMLCtrl控件,目的是在对话框设计的时候便于布局:
BOOL CHTMLCtrl::CreateFromCtrl(UINT nID,CWnd* pParent)
{
if (!pParent || !pParent->GetSafeHwnd())
return FALSE;
CWnd *pCtrl = pParent->GetDlgItem(nID);
if (!pCtrl)
return FALSE;
CRect rcCtrl;
pCtrl->GetWindowRect(rcCtrl);
pParent->ScreenToClient(rcCtrl);
UINT style = ::GetWindowLong(pCtrl->GetSafeHwnd(), GWL_STYLE);
pCtrl->DestroyWindow();
return Create(NULL, NULL, style |WS_CHILD | WS_VISIBLE, rcCtrl, pParent, nID, NULL);
}
还应注意,默认的从CView继承的类,其构造函数和析构函数是protected的,需要修改成public。
还应注意,默认的从CView继承的类,其构造函数和析构函数是protected的,需要修改成public。
public:
CHTMLCtrl(); //动态创建所使用的受保护的构造函数
virtual ~CHTMLCtrl();
然后,在对话框模板中(使用资源编辑器),插入一个Static Text控件,ID为IDC_HTML。
在对话框头文件中,插入包含文件:
#include "htmlctrl.h"
增加CHTMLCtrl类型的成员变量:
CHTMLCtrl m_ctlHTML;
在对话框初始化的时候,创建这个CHTMLCtrl控件:
BOOL CMyDialog::OnInitDialog()
{
CDialog::OnInitDialog();
...
m_ctlHTML.CreateFromCtrl(IDC_HTML, this);
return TRUE;
}
下面,再添加一个按钮,在按钮的消息响应函数中打开HTML文件:
void CMyDialog::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}