InitialUpdateFrame()跟踪
在CSingleDocTemplate::OpenDocumentFile()或CMultiDocTemplate::OpenDocumentFile()的过程中,前者根据需要CreateNewFrame(),后者则一直CreateNewFrame()。之后需要调用CDocTemplate::InitialUpdateFrame()。
CDocument* CSingle/MultiDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,BOOL bMakeVisible)
{
...
CFrameWnd* pFrame = CreateNewFrame(pDocument, NULL);
...
InitialUpdateFrame(pFrame, pDocument, bMakeVisible);//因此对于单文档来说,同一个视图的OnInitialUpdate()将被调用多次!(CeateNewFrame里的LoadFrame和InitialUpdateFrame会发出了WM_INITIALUPDATE消息)而多文档每次都创建新的文档、子框架窗口和视图,因此OnInitialUpdate只会被调用两次。
...
}
void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,BOOL bMakeVisible)
{
// just delagate to implementation in CFrameWnd
pFrame->InitialUpdateFrame(pDoc, bMakeVisible);
}
void CFrameWnd::InitialUpdateFrame(CDocument* pDoc, BOOL bMakeVisible)
{
// if the frame does not have an active view, set to first pane
CView* pView = NULL;
if (GetActiveView() == NULL)//对于单文档,只有第一次为NULL;对于多文档,必然为NULL,即此时还没有活动视
{
CWnd* pWnd = GetDescendantWindow(AFX_IDW_PANE_FIRST, TRUE);
// CWnd* GetDescendantWindow( int nID, BOOL bOnlyPerm = FALSE ) const;
返回一个CWnd对象的指针,如果没有找到子窗口,则返回NULL。nID指定了要获取的控件或子窗口的标识符。bOnlyPerm指定了返回的窗口是否可以是临时的。如果为TRUE,则只能返回永久性的窗口;如果为FALSE,则该函数可以返回临时窗口。调用这个函数以找到给定的ID所指定的后代窗口。这个成员函数搜索整个子窗口树,并不仅是直接子窗口。
if (pWnd != NULL && pWnd->IsKindOf(RUNTIME_CLASS(CView)))
{
pView = (CView*)pWnd;
SetActiveView(pView, FALSE);//找到一个子窗口(视图)设置为活动视
}
}
if (bMakeVisible)
{
// send initial update to all views (and other controls) in the frame
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE); //发出WM_INITIALUPDATE消息,调用OnInitialUpdate()。
// give view a chance to save the focus (CFormView needs this)
if (pView != NULL)
pView->OnActivateFrame(WA_INACTIVE, this);//该函数为纯虚函数,可重载
// finally, activate the frame
// (send the default show command unless the main desktop window)
int nCmdShow = -1; // default
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->m_pMainWnd == this)//仅对单文档有效!因为只有单文档的m_pMainWnd才会调用InitialUpdateFrame,多文档是新创建的ChildFrm调用
{
nCmdShow = pApp->m_nCmdShow; // use the parameter from WinMain
pApp->m_nCmdShow = -1; // set to default after first time将m_nCmdShow重置为 -1
//int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)//入口,nCmdShow = SW_SHOWNORMAL = 1
{
...
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
...
}
BOOL AFXAPI AfxWinInit(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance,
_In_z_ LPTSTR lpCmdLine, _In_ int nCmdShow)//初始化
{
...
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
hPrevInstance; // Obsolete.
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;//所以程序初始化时,m_nCmdShow = 1
pApp->SetCurrentHandles();
}
...
}
}
ActivateFrame(nCmdShow);
//对于单文档,第一次运行到这里时(新建或者打开文档,单文档不能修改cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing,因为单文档的m_pMainWnd是在CDocTemplate::OpenDocumentFile()里动态创建的,而多文档的m_pMainWnd是之前就创建好的),nCmdShow = m_nCmdShow,而m_nCmdShow被修改为-1(由于该值不能被传入ShowWindow(),所以在之后的CXXXApp::InitialInstance()中为m_pMainWnd->ShowWindow(SW_SHOW)而不是m_pMainWnd->ShowWindow(m_nCmdShow)),而之后在程序运行过程中,若窗口状态等发生变化,m_nCmdShow会发生变化,具体原理存疑。而后第二次运行到这里nCmdShow = m_nCmdShow
//对于多文档,第一次运行到这里时,nCmdShow = -1
if (pView != NULL)
pView->OnActivateView(TRUE, pView, pView);//使该视图成为活动视,可重载
}
// update frame counts and frame title (may already have been visible)
if (pDoc != NULL)
pDoc->UpdateFrameCounts();
OnUpdateFrameTitle(TRUE);
}
void CFrameWnd::SetActiveView(CView* pViewNew, BOOL bNotify)
{
#ifdef _DEBUG
if (pViewNew != NULL)
{
ASSERT(IsChild(pViewNew));
ASSERT_KINDOF(CView, pViewNew);
}
#endif //_DEBUG
CView* pViewOld = m_pViewActive;
if (pViewNew == pViewOld)
return; // do not re-activate if SetActiveView called more than once
m_pViewActive = NULL; // no active for the following processing
// deactivate the old one
if (pViewOld != NULL)//对于多文档,则为NULL
pViewOld->OnActivateView(FALSE, pViewNew, pViewOld);
// if the OnActivateView moves the active window,
// that will veto this change
if (m_pViewActive != NULL)
return; // already set
m_pViewActive = pViewNew;
// activate
if (pViewNew != NULL && bNotify)//是否通知?通知则调用
pViewNew->OnActivateView(TRUE, pViewNew, pViewOld);
}
void CChildFrame::ActivateFrame(int nCmdShow)//nCmdShow默认为-1
{
// TODO: 在此添加专用代码和/或调用基类
//nCmdShow = SW_SHOWMAXIMIZED;//通过修改nCmdShow来修改子框架窗口风格
CMDIChildWnd::ActivateFrame(nCmdShow);
}
void CMDIChildWnd::ActivateFrame(int nCmdShow)
{
BOOL bVisibleThen = (GetStyle() & WS_VISIBLE) != 0;
CMDIFrameWnd* pFrameWnd = GetMDIFrame();
ASSERT_VALID(pFrameWnd);
// determine default show command
if (nCmdShow == -1)
{
// get maximized state of frame window (previously active child)
BOOL bMaximized;
pFrameWnd->MDIGetActive(&bMaximized);
// convert show command based on current style
DWORD dwStyle = GetStyle();
if (bMaximized || (dwStyle & WS_MAXIMIZE))
nCmdShow = SW_SHOWMAXIMIZED;
else if (dwStyle & WS_MINIMIZE)
nCmdShow = SW_SHOWMINIMIZED;
}
// finally, show the window
CFrameWnd::ActivateFrame(nCmdShow);
// update the Window menu to reflect new child window
CMDIFrameWnd* pFrame = GetMDIFrame();
::SendMessage(pFrame->m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0);
// Note: Update the m_bPseudoInactive flag. This is used to handle the
// last MDI child getting hidden. Windows provides no way to deactivate
// an MDI child window.
BOOL bVisibleNow = (GetStyle() & WS_VISIBLE) != 0;
if (bVisibleNow == bVisibleThen)
return;
if (!bVisibleNow)
{
// get current active window according to Windows MDI
HWND hWnd = (HWND)::SendMessage(pFrameWnd->m_hWndMDIClient,
WM_MDIGETACTIVE, 0, 0);
if (hWnd != m_hWnd)
{
// not active any more -- window must have been deactivated
ASSERT(!m_bPseudoInactive);
return;
}
// check next window
ASSERT(hWnd != NULL);
pFrameWnd->MDINext();
// see if it has been deactivated now...
hWnd = (HWND)::SendMessage(pFrameWnd->m_hWndMDIClient,
WM_MDIGETACTIVE, 0, 0);
if (hWnd == m_hWnd)
{
// still active -- fake deactivate it
ASSERT(hWnd != NULL);
OnMDIActivate(FALSE, NULL, this);
m_bPseudoInactive = TRUE; // so MDIGetActive returns NULL
}
}
else if (m_bPseudoInactive)
{
// if state transitioned from not visible to visible, but
// was pseudo deactivated -- send activate notify now
OnMDIActivate(TRUE, this, NULL);
ASSERT(!m_bPseudoInactive); // should get set in OnMDIActivate!
}
}
void CFrameWnd::ActivateFrame(int nCmdShow)
// nCmdShow is the normal show mode this frame should be in
{
// translate default nCmdShow (-1)
if (nCmdShow == -1)
{
if (!IsWindowVisible())
nCmdShow = SW_SHOWNORMAL;
else if (IsIconic())
nCmdShow = SW_RESTORE;
}
// bring to top before showing
BringToTop(nCmdShow);
if (nCmdShow != -1)
{
// show the window as specified
ShowWindow(nCmdShow);//所以对于单文档,在CXXXApp::InitialInstance里m_pMainWnd->ShowWindow(SW_SHOW)之前,已经调用过一次ShowWindow,只不过没有UpdateWindow(),所以还是以m_pMainWnd->ShowWindow为准。
//对于多文档,此处的ShowWindow乃是子窗口框架的ShowWindow,主框架窗口的ShowWindow在CXXXApp::InitialInstance里的pMainFrame->ShowWindow(m_nCmdShow)
// and finally, bring to top after showing
BringToTop(nCmdShow);
}
}
void CMDIChildWnd::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd*)
{
m_bPseudoInactive = FALSE; // must be happening for real
// make sure MDI client window has correct client edge
UpdateClientEdge();
// send deactivate notification to active view
CView* pActiveView = GetActiveView();
if (!bActivate && pActiveView != NULL)
pActiveView->OnActivateView(FALSE, pActiveView, pActiveView);
// allow hook to short circuit normal activation
BOOL bHooked = FALSE;
#ifndef _AFX_NO_OLE_SUPPORT
if (m_pNotifyHook != NULL && m_pNotifyHook->OnDocActivate(bActivate))
bHooked = TRUE;
#endif
// update titles (don't AddToTitle if deactivate last)
if (!bHooked)
OnUpdateFrameTitle(bActivate || (pActivateWnd != NULL));
// re-activate the appropriate view
if (bActivate)
{
if (pActiveView != NULL && GetMDIFrame() == GetActiveWindow())
pActiveView->OnActivateView(TRUE, pActiveView, pActiveView);
}
// update menus
if (!bHooked)
{
OnUpdateFrameMenu(bActivate, pActivateWnd, NULL);
GetMDIFrame()->DrawMenuBar();
}
}
void CView::OnActivateView(BOOL bActivate, CView* pActivateView, CView*)//当一个视图成为活动视时,调用该函数。
{
UNUSED(pActivateView); // unused in release builds
if (bActivate)
{
ASSERT(pActivateView == this);
// take the focus if this frame/view/pane is now active
if (IsTopParentActive())
SetFocus();
}
}

浙公网安备 33010602011771号