📄 tabbedmdi.h
字号:
}
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)
{
// 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)
{
// 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)
{
// 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)
{
bHandled = FALSE;
return 0;
}
LRESULT OnSelChanging(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
{
bHandled = FALSE;
return 0;
}
LRESULT OnSelChange(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
{
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)
{
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 OnAddFirstTab()
{
if(this->IsWindowVisible() == FALSE)
{
RECT rcMDIClient;
::GetWindowRect(m_hWndMDIClient, &rcMDIClient);
::MapWindowPoints(NULL, ::GetParent(m_hWndMDIClient), (LPPOINT)&rcMDIClient, 2);
this->ShowWindow(SW_SHOW);
// the MDI client resizes and shows our window when
// handling messages related to SetWindowPos
::SetWindowPos(
m_hWndMDIClient, NULL,
rcMDIClient.left, rcMDIClient.top,
(rcMDIClient.right - rcMDIClient.left),(rcMDIClient.bottom - rcMDIClient.top),
SWP_NOZORDER);
}
customTabOwnerClass::OnAddFirstTab();
}
void OnRemoveLastTab()
{
if(this->IsWindowVisible() == TRUE)
{
RECT rcTabs;
m_TabCtrl.GetWindowRect(&rcTabs);
::MapWindowPoints(NULL, m_TabCtrl.GetParent(), (LPPOINT)&rcTabs, 2);
this->ShowWindow(SW_HIDE);
RECT rcMDIClient;
::GetWindowRect(m_hWndMDIClient, &rcMDIClient);
::MapWindowPoints(NULL, ::GetParent(m_hWndMDIClient), (LPPOINT)&rcMDIClient, 2);
// the MDI client resizes and shows our window when
// handling messages related to SetWindowPos
// TODO: Is there a better way to do this?
// We're basically hiding the tabs and
// resizing the MDI client area to "cover up"
// where the tabs were
DWORD dwStyle = m_TabCtrl.GetStyle();
if(CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
{
::SetWindowPos(
m_hWndMDIClient, NULL,
rcMDIClient.left, rcMDIClient.top,
(rcMDIClient.right - rcMDIClient.left),
(rcMDIClient.bottom - rcMDIClient.top) + (rcTabs.bottom - rcTabs.top),
SWP_NOZORDER);
}
else
{
::SetWindowPos(
m_hWndMDIClient, NULL,
rcMDIClient.left, rcMDIClient.top - (rcTabs.bottom - rcTabs.top),
(rcMDIClient.right - rcMDIClient.left),
(rcMDIClient.bottom - rcMDIClient.top) + (rcTabs.bottom - rcTabs.top),
SWP_NOZORDER);
}
}
customTabOwnerClass::OnRemoveLastTab();
}
void SetTabAreaHeight(int nNewTabAreaHeight)
{
if(m_bKeepTabsHidden)
{
m_nTabAreaHeight = 0;
}
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);
}
}
}
};
template< class TTabCtrl >
class CMDITabOwner :
public CMDITabOwnerImpl<CMDITabOwner, TTabCtrl>
{
};
/////////////////////////////////////////////////////////////////////////////
//
// CTabbedMDIClient
//
/////////////////////////////////////////////////////////////////////////////
template< class TTabCtrl = CDotNetTabCtrl<CTabViewTabItem>, class TTabOwner = CMDITabOwner<TTabCtrl> >
class CTabbedMDIClient : public CWindowImpl<CTabbedMDIClient, CWindow>
{
public:
// Expose the type of tab control and tab owner
typedef typename TTabCtrl TTabCtrl;
typedef typename TTabOwner TTabOwner;
protected:
typedef CWindowImpl<CTabbedMDIClient, CWindow> baseClass;
typedef CTabbedMDIClient< TTabCtrl, TTabOwner > thisClass;
// Member variables
protected:
HWND m_hWndTabOwnerParent;
TTabOwner m_MdiTabOwner;
BOOL m_bUseMDIChildIcon;
BOOL m_bHideMDITabsWhenMDIChildNotMaximized;
bool m_bSubclassed;
bool m_bDrawFlat;
// Constructors
public:
CTabbedMDIClient() :
m_hWndTabOwnerParent(NULL),
m_bUseMDIChildIcon(FALSE),
m_bHideMDITabsWhenMDIChildNotMaximized(FALSE),
m_bSubclassed(false),
m_bDrawFlat(false)
{
ATLASSERT(UWM_MDICHILDACTIVATIONCHANGE != 0 && "The TabbedMDI Messages didn't get registered properly");
if(m_bDrawFlat)
{
m_MdiTabOwner.ModifyTabStyles(0,CTCS_FLATEDGE);
}
}
virtual ~CTabbedMDIClient()
{
if(this->IsWindow() && m_bSubclassed)
{
this->UnsubclassWindow(TRUE);
}
}
// Methods
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -