WTL: CTabView 源码分析
2014-04-13 21:21
211 查看
分析CTabView的原因,是我想改进现有的 CTabViewImpl, 最终目标是建立一套界面类似VS2010的控件集合。我已经做了一些工作,改进了菜单部分,对于分割窗口也进行了改写,剩下部分就是这个 CTabView了。我已经大致看了下整个 CTabViewImpl的代码,显然这个地方的改动的部分会比较多。现在我们就带着这个目的来分析目标源码。
这个分类的关键成员是: ATL::CContainedWindowT<CTabCtrl> m_tab;
继续向下解释之前,要先弄清楚这个 ATL::CContainedWindowT容器类的作用。CContainedWindowT 是一个模板类,它起到对基类行为进行扩充的目的。
CContainedWindowT<CEdit>, CContainedWindowT<CTabCtrl>, 在主窗口里面这样实现的成员,它的消息会给映射到主窗口类的一个分段。我们看看 CTabViewImpl 的构造函数和消息映射。
CContainedWindowT 通过初始化的参数将映射区映射到ATL_MSG_MAP(1), 于是在主窗口里面就可以对子控件进行消息处理了。 CContainedWindowT 对于窗口消息函数的处理类似于CWindowImpl里面所做的。如果你想深究可以自己去研究,否则知道如何使用这个类就可以了。我不想管他是怎么做到的,但我知道它做到了。
通过处理1区这些映射消息,TabView 实现了如下几个功能:
1. 选项卡的拖动移动Tab页。
2. 右键点击TAB时候,或者 按下Shift + F10 可以弹出一个指定的菜单,
关于拖动TAB选项卡,我们来研究一下,它是如何做到的,如何构建拖动图片,等等。
来看看产生拖动图像的这段代码,首先从m_tab获得选项卡区域的大小, CimageList 成员 m_ilDrag 建立。然后创建一个位图,配置好DC, 然后 向 m_tab发送WM_PRINTCLIENT 消息。于是,m_tab将它自身的图像绘制到我们刚才准备好的位图里面。最后,我们把这个位图加入到 CImageList 对象里面。
还有一个成员: ATL::CWindow m_wndTitleBar;
因为没有找到提示,我捉摸了挺长时间才明天这个成员的意义。TabView 由于要不停地切换页面, 考虑到主窗口的标题栏的内容可能需要根据这个切换来变化,所以增加了这个成员。void SetTitleBarWindow(HWND hWnd), 和 UpdateTitleBar(), 分别用于指定和更新标题。
// TabView Notifications
#define TBVN_PAGEACTIVATED (0U-741)
#define TBVN_CONTEXTMENU (0U-742)
CTabView定义了两个通知代码, 用户切换了TAB页,或者启用了场景菜单, 会发送这两个通知到父窗口。你可以在哪里进行相应的处理。
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 handlers BEGIN_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_WCE NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo) #endif // !_WIN32_WCE FORWARD_NOTIFICATIONS() ALT_MSG_MAP(1) // tab control MESSAGE_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_WCE RECT rcItem = { 0 }; m_tab.GetItemRect(nItem, &rcItem); ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item #else // CE specific nItem; // avoid level 4 warning RECT rcItem = { 0, 0, 40, 20 }; #endif // _WIN32_WCE ATLASSERT(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_WCE m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC); #else // CE specific dcMem.Rectangle(&rcItem); #endif // _WIN32_WCE dcMem.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 handlers LRESULT 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_WCE m_ilDrag.DragLeave(GetDesktopWindow()); #endif // !_WIN32_WCE m_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_WCE if(abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG) || abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG)) #else // CE specific if(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_WCE POINT ptEnter = m_ptStartDrag; m_tab.ClientToScreen(&ptEnter); m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter); #endif // !_WIN32_WCE m_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页,或者启用了场景菜单, 会发送这两个通知到父窗口。你可以在哪里进行相应的处理。
相关文章推荐
- 每天来点算法_1
- BIOS密码清除方法--unlock6的使用.
- 信监:变更控制
- 二分查找
- WSAAsyncSelect 模型
- hanoi
- 常用SQL语句(增删改查、合并统计、模糊搜索)
- HDU 1085
- 【LeetCode练习题】Combination Sum
- HDU 2108 字符串递归
- wget使用方法(some)
- 杭电acm 2952
- bind "xxx.js" to object using script
- 使用c++编写和使用.so动态链接库
- hdu3786 Floyd或搜索 水题
- 在Linux中用source,dot(.)和直接用脚本文件名执行shell脚本的区别
- 机器学习——深度学习(Deep Learning)
- Android支付接入(一):支付宝
- kvm简介(二)
- 分析位深度与颜色深度的关系和发展策略