📄 tabbedmdi.h
字号:
}
}
}
void HideTabControlIfChildNotMaximized(void)
{
if(m_bHideMDITabsWhenMDIChildNotMaximized)
{
T* pT = static_cast<T*>(this);
pT->HideTabControl();
}
}
// Message Handling
public:
DECLARE_WND_CLASS_EX(_T("MdiTabOwner"), 0, COLOR_APPWORKSPACE)
BEGIN_MSG_MAP(thisClass)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
NOTIFY_CODE_HANDLER(NM_CLICK, OnClick)
NOTIFY_CODE_HANDLER(CTCN_ACCEPTITEMDRAG, OnAcceptItemDrag)
NOTIFY_CODE_HANDLER(CTCN_CANCELITEMDRAG, OnCancelItemDrag)
NOTIFY_CODE_HANDLER(CTCN_DELETEITEM, OnDeleteItem)
NOTIFY_CODE_HANDLER(CTCN_SELCHANGING, OnSelChanging)
NOTIFY_CODE_HANDLER(CTCN_SELCHANGE, OnSelChange)
NOTIFY_CODE_HANDLER(CTCN_CLOSE, OnTabClose)
// NOTE: CCustomTabCtrl derived classes no longer
// need notifications reflected.
// REFLECT_NOTIFICATIONS()
END_MSG_MAP()
LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// "baseClass::OnCreate()"
LRESULT lRet = this->DefWindowProc(uMsg, wParam, lParam);
bHandled = TRUE;
if(lRet == -1)
{
return -1;
}
CreateTabWindow(m_hWnd, rcDefault, m_nTabStyles);
return 0;
}
LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
DestroyTabWindow();
// Say that we didn't handle it so that anyone else
// interested gets to handle the message
bHandled = FALSE;
return 0;
}
LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
if(m_TabCtrl)
{
// Let the tabs do all the drawing as flicker-free as possible
return 1;
}
bHandled = FALSE;
return 0;
}
LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
RECT rect = {0};
GetClientRect(&rect);
m_TabCtrl.SetWindowPos(NULL, &rect, SWP_NOZORDER | SWP_NOACTIVATE);
m_TabCtrl.UpdateLayout();
bHandled = TRUE;
return 0;
}
LRESULT OnContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
bHandled = TRUE;
int nIndex = -1;
POINT ptPopup = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
if(ptPopup.x == -1 && ptPopup.y == -1)
{
nIndex = m_TabCtrl.GetCurSel();
RECT rect = {0};
if(nIndex >= 0)
{
// If there is a selected item, popup the menu under the node,
// if not, pop it up in the top left of the tree view
m_TabCtrl.GetItemRect(nIndex, &rect);
}
::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rect, 2);
ptPopup.x = rect.left;
ptPopup.y = rect.bottom;
}
else
{
POINT ptClient = ptPopup;
::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
CTCHITTESTINFO tchti = { 0 };
tchti.pt = ptClient;
//If we become templated, pT->HitTest(&tchti);
nIndex = m_TabCtrl.HitTest(&tchti);
}
if( nIndex >= 0 )
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nIndex);
HWND hWndChild = pItem->GetTabView();
if(hWndChild != NULL)
{
::SendMessage(hWndChild, UWM_MDICHILDSHOWTABCONTEXTMENU, wParam, MAKELPARAM(ptPopup.x, ptPopup.y));
}
}
return 0;
}
LRESULT OnClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
// If they left click on an item, set focus on the tab view,
// but only if the view was already the active tab view
// (otherwise our code to reduce flicker when switching
// MDI children when maximized doesn't kick in).
NMCTCITEM* item = (NMCTCITEM*)pnmh;
if(item && (item->iItem >= 0) && (item->iItem == m_TabCtrl.GetCurSel()))
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(item->iItem);
if(pItem->UsingTabView())
{
::SetFocus(pItem->GetTabView());
}
}
}
bHandled = FALSE;
return 0;
}
LRESULT OnAcceptItemDrag(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
// If finished dragging, set focus on the tab view.
NMCTC2ITEMS* item = (NMCTC2ITEMS*)pnmh;
if(item && (item->iItem2 >= 0))
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(item->iItem2);
if(pItem->UsingTabView())
{
::SetFocus(pItem->GetTabView());
}
}
}
bHandled = FALSE;
return 0;
}
LRESULT OnCancelItemDrag(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
// If finished dragging, set focus on the tab view.
NMCTCITEM* item = (NMCTCITEM*)pnmh;
if(item && (item->iItem >= 0))
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(item->iItem);
if(pItem->UsingTabView())
{
::SetFocus(pItem->GetTabView());
}
}
}
bHandled = FALSE;
return 0;
}
LRESULT OnDeleteItem(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
}
bHandled = FALSE;
return 0;
}
LRESULT OnSelChanging(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
}
bHandled = FALSE;
return 0;
}
LRESULT OnSelChange(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
int nNewTab = m_TabCtrl.GetCurSel();
if(nNewTab >= 0)
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nNewTab);
if(pItem->UsingTabView())
{
HWND hWndNew = pItem->GetTabView();
HWND hWndOld = (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0, NULL);
if( hWndNew != hWndOld )
{
// We don't want any flickering when switching the active child
// when the child is maximized (when its not maximized, there's no flicker).
// There's probably more than one way to do this, but how we do
// it is to turn off redrawing for the MDI client window,
// activate the new child window, turn redrawing back on for
// the MDI client window, and force a redraw (not just a simple
// InvalidateRect, but an actual RedrawWindow to include
// all the child windows ).
// It might be nice to just turn off/on drawing for the window(s)
// that need it, but if you watch the messages in Spy++,
// the default implementation of the MDI client is forcing drawing
// to be on for the child windows involved. Turning drawing off
// for the MDI client window itself seems to solve this problem.
//
LRESULT nResult = 0;
WINDOWPLACEMENT wpOld = {0};
wpOld.length = sizeof(WINDOWPLACEMENT);
::GetWindowPlacement(hWndOld, &wpOld);
if(wpOld.showCmd == SW_SHOWMAXIMIZED)
{
nResult = ::SendMessage(m_hWndMDIClient, WM_SETREDRAW, FALSE, 0);
}
nResult = ::SendMessage(m_hWndMDIClient, WM_MDIACTIVATE, (LPARAM)hWndNew, 0);
WINDOWPLACEMENT wpNew = {0};
wpNew.length = sizeof(WINDOWPLACEMENT);
::GetWindowPlacement(hWndNew, &wpNew);
if(wpNew.showCmd == SW_SHOWMINIMIZED)
{
::ShowWindow(hWndNew, SW_RESTORE);
}
if(wpOld.showCmd == SW_SHOWMAXIMIZED)
{
nResult = ::SendMessage(m_hWndMDIClient, WM_SETREDRAW, TRUE, 0);
::RedrawWindow(m_hWndMDIClient, NULL, NULL,
//A little more forceful if necessary: RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
RDW_INVALIDATE | RDW_ALLCHILDREN);
}
}
}
}
}
bHandled = FALSE;
return 0;
}
LRESULT OnTabClose(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
{
// Be sure the notification is from the tab control
// (and not from a sibling like a list view control)
if(pnmh && (m_TabCtrl == pnmh->hwndFrom))
{
LPNMCTCITEM pnmCustomTab = (LPNMCTCITEM)pnmh;
if(pnmCustomTab)
{
if(pnmCustomTab->iItem >= 0)
{
TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(pnmCustomTab->iItem);
if(pItem)
{
::PostMessage(pItem->GetTabView(), WM_SYSCOMMAND, SC_CLOSE, 0L);
}
}
}
}
bHandled = FALSE;
return 0;
}
// Overrides from CCustomTabOwnerImpl
public:
void ShowTabControl()
{
T* pT = static_cast<T*>(this);
if(m_bHideMDITabsWhenMDIChildNotMaximized && m_hWndMDIClient)
{
HWND hWndActiveChild = (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0, 0);
if(hWndActiveChild && ::IsZoomed(hWndActiveChild))
{
pT->KeepTabsHidden(false);
}
}
else
{
pT->KeepTabsHidden(false);
}
}
void HideTabControl()
{
T* pT = static_cast<T*>(this);
pT->KeepTabsHidden(true);
}
void KeepTabsHidden(bool bKeepTabsHidden = true)
{
if(m_bKeepTabsHidden != bKeepTabsHidden)
{
m_bKeepTabsHidden = bKeepTabsHidden;
// CalcTabAreaHeight will end up doing UpdateLayout and Invalidate
T* pT = static_cast<T*>(this);
pT->CalcTabAreaHeight();
// For MDI tabs, the UpdateLayout done by CalcTabAreaHeight
// is not quite enough to force the tab control to show or hide.
// So we'll force the tab control to be shown or hidden.
if(m_bKeepTabsHidden)
{
pT->ForceHideMDITabControl();
}
else
{
pT->ForceShowMDITabControl();
}
}
}
void SetTabAreaHeight(int nNewTabAreaHeight)
{
if(m_bKeepTabsHidden)
{
m_nTabAreaHeight = 0;
//T* pT = static_cast<T*>(this);
//pT->UpdateLayout();
//Invalidate();
}
else if(m_nTabAreaHeight != nNewTabAreaHeight)
{
int nOldTabAreaHeight = m_nTabAreaHeight;
m_nTabAreaHeight = nNewTabAreaHeight;
//T* pT = static_cast<T*>(this);
//pT->UpdateLayout();
//Invalidate();
if(this->IsWindowVisible() == TRUE)
{
RECT rcMDIClient;
::GetWindowRect(m_hWndMDIClient, &rcMDIClient);
::MapWindowPoints(NULL, this->GetParent(), (LPPOINT)&rcMDIClient, 2);
// Don't ask me why these two lines are necessary.
// Take these lines out if you want to
// convince yourself that they are :-)
rcMDIClient.top -= nOldTabAreaHeight;
rcMDIClient.bottom += nOldTabAreaHeight;
// The tab resize/reposition logic happens when handling WM_WINDOWPOSCHANGING.
// If that ever changes, make the appropriate change here.
::SetWindowPos(
m_hWndMDIClient, NULL,
rcMDIClient.left, rcMDIClient.top,
(rcMDIClient.right - rcMDIClient.left),(rcMDIClient.bottom - rcMDIClient.top),
SWP_NOZORDER | SWP_NOACTIVATE);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -