WTL: CTabView 源码分析

来源:互联网 发布:java防止接口重复提交 编辑:程序博客网 时间:2024/05/18 00:05

        分析CTabView的原因,是我想改进现有的 CTabViewImpl, 最终目标是建立一套界面类似VS2010的控件集合。我已经做了一些工作,改进了菜单部分,对于分割窗口也进行了改写,剩下部分就是这个 CTabView了。我已经大致看了下整个 CTabViewImpl的代码,显然这个地方的改动的部分会比较多。现在我们就带着这个目的来分析目标源码。

      

template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>{public:DECLARE_WND_CLASS_EX(NULL, 0, COLOR_APPWORKSPACE)

这个分类的关键成员是: ATL::CContainedWindowT<CTabCtrl> m_tab;

           继续向下解释之前,要先弄清楚这个 ATL::CContainedWindowT容器类的作用。CContainedWindowT 是一个模板类,它起到对基类行为进行扩充的目的。

CContainedWindowT<CEdit>, CContainedWindowT<CTabCtrl>, 在主窗口里面这样实现的成员,它的消息会给映射到主窗口类的一个分段。我们看看 CTabViewImpl 的构造函数和消息映射。

CTabViewImpl() :m_nActivePage(-1), m_cyTabHeight(0), m_tab(this, 1),                         =============={m_ptStartDrag.x = 0;m_ptStartDrag.y = 0;}
// Message map and handlersBEGIN_MSG_MAP(CTabViewImpl)MESSAGE_HANDLER(WM_CREATE, OnCreate)MESSAGE_HANDLER(WM_DESTROY, OnDestroy)MESSAGE_HANDLER(WM_SIZE, OnSize)MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)MESSAGE_HANDLER(WM_GETFONT, OnGetFont)MESSAGE_HANDLER(WM_SETFONT, OnSetFont)NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)#ifndef _WIN32_WCENOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)#endif // !_WIN32_WCEFORWARD_NOTIFICATIONS()ALT_MSG_MAP(1)   // tab controlMESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)MESSAGE_HANDLER(WM_RBUTTONUP, OnTabRButtonUp)MESSAGE_HANDLER(WM_SYSKEYDOWN, OnTabSysKeyDown)END_MSG_MAP()

          CContainedWindowT 通过初始化的参数将映射区映射到ATL_MSG_MAP(1), 于是在主窗口里面就可以对子控件进行消息处理了。 CContainedWindowT  对于窗口消息函数的处理类似于CWindowImpl里面所做的。如果你想深究可以自己去研究,否则知道如何使用这个类就可以了。我不想管他是怎么做到的,但我知道它做到了。

          通过处理1区这些映射消息,TabView 实现了如下几个功能:

          1.  选项卡的拖动移动Tab页。

          2.  右键点击TAB时候,或者 按下Shift + F10 可以弹出一个指定的菜单,


关于拖动TAB选项卡,我们来研究一下,它是如何做到的,如何构建拖动图片,等等。

void GenerateDragImage(int nItem){ATLASSERT(IsValidPageIndex(nItem));#ifndef _WIN32_WCERECT rcItem = { 0 };m_tab.GetItemRect(nItem, &rcItem);::InflateRect(&rcItem, 2, 2);   // make bigger to cover selected item#else // CE specificnItem;   // avoid level 4 warningRECT rcItem = { 0, 0, 40, 20 };#endif // _WIN32_WCEATLASSERT(m_ilDrag.m_hImageList == NULL);m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);CClientDC dc(m_hWnd);CDC dcMem;dcMem.CreateCompatibleDC(dc);ATLASSERT(dcMem.m_hDC != NULL);dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);CBitmap bmp;bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);ATLASSERT(bmp.m_hBitmap != NULL);HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);#ifndef _WIN32_WCEm_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);#else // CE specificdcMem.Rectangle(&rcItem);#endif // _WIN32_WCEdcMem.SelectBitmap(hBmpOld);ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);}

     来看看产生拖动图像的这段代码,首先从m_tab获得选项卡区域的大小, CimageList 成员 m_ilDrag 建立。然后创建一个位图,配置好DC, 然后 向 m_tab发送WM_PRINTCLIENT 消息。于是,m_tab将它自身的图像绘制到我们刚才准备好的位图里面。最后,我们把这个位图加入到 CImageList 对象里面。


// Tab control message handlersLRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled){if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1)){m_bTabCapture = true;m_tab.SetCapture();m_ptStartDrag.x = GET_X_LPARAM(lParam);m_ptStartDrag.y = GET_Y_LPARAM(lParam);}bHandled = FALSE;return 0;}LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled){if(m_bTabCapture){if(m_bTabDrag){TCHITTESTINFO hti = { 0 };hti.pt.x = GET_X_LPARAM(lParam);hti.pt.y = GET_Y_LPARAM(lParam);int nItem = m_tab.HitTest(&hti);if(nItem != -1)MovePage(m_nActivePage, nItem);}::ReleaseCapture();}bHandled = FALSE;return 0;}LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled){if(m_bTabCapture){m_bTabCapture = false;if(m_bTabDrag){m_bTabDrag = false;T* pT = static_cast<T*>(this);pT->DrawMoveMark(-1);#ifndef _WIN32_WCEm_ilDrag.DragLeave(GetDesktopWindow());#endif // !_WIN32_WCEm_ilDrag.EndDrag();m_ilDrag.Destroy();m_ilDrag.m_hImageList = NULL;}}bHandled = FALSE;return 0;}LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled){bHandled = FALSE;if(m_bTabCapture){POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };if(!m_bTabDrag){#ifndef _WIN32_WCEif(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) ||   abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG))#else // CE specificif(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= 4 ||   abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= 4)#endif // _WIN32_WCE{T* pT = static_cast<T*>(this);pT->GenerateDragImage(m_nActivePage);int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));#ifndef _WIN32_WCEPOINT ptEnter = m_ptStartDrag;m_tab.ClientToScreen(&ptEnter);m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);#endif // !_WIN32_WCEm_bTabDrag = true;}}if(m_bTabDrag){TCHITTESTINFO hti = { 0 };hti.pt = pt;int nItem = m_tab.HitTest(&hti);T* pT = static_cast<T*>(this);pT->SetMoveCursor(nItem != -1);if(m_nInsertItem != nItem)pT->DrawMoveMark(nItem);m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);m_tab.ClientToScreen(&pt);m_ilDrag.DragMove(pt);bHandled = TRUE;}}return 0;}

在 OnTabLButtonDown 中,鼠标的位置成为拖动的起点。


还有一个成员:  ATL::CWindow m_wndTitleBar;

        因为没有找到提示,我捉摸了挺长时间才明天这个成员的意义。TabView 由于要不停地切换页面, 考虑到主窗口的标题栏的内容可能需要根据这个切换来变化,所以增加了这个成员。void SetTitleBarWindow(HWND hWnd), 和  UpdateTitleBar(), 分别用于指定和更新标题。


// TabView Notifications
#define TBVN_PAGEACTIVATED   (0U-741)
#define TBVN_CONTEXTMENU     (0U-742)

CTabView定义了两个通知代码, 用户切换了TAB页,或者启用了场景菜单, 会发送这两个通知到父窗口。你可以在哪里进行相应的处理。


0 0
原创粉丝点击