分类:
2008-10-13 16:43:03
CPopupText 类定义和实现 //////////////////////////////////////////////////////////////// // PupText.h // #pragma once // Get NONCLIENTMETRICS info: ctor calls SystemParametersInfo. // class CNonClientMetrics : public NONCLIENTMETRICS { public: CNonClientMetrics() { cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS,0,this,0); } }; // Popup text window, like tooltip. // Can be right or left justified relative to creation point. // class CPopupText : public CWnd { public: CSize m_szMargins; // extra space around text: change if you like enum {JUSTIFYLEFT=0, JUSTIFYRIGHT}; CPopupText(); virtual ~CPopupText(); BOOL Create(CPoint pt, CWnd* pParentWnd, UINT nStyle=0, UINT nID=0); void ShowDelayed(UINT msec); void Cancel(); protected: CFont m_font; // font to use (same as tooltips) UINT m_nStyle; // style (see enum below) virtual void PostNcDestroy(); virtual BOOL PreCreateWindow(CREATESTRUCT& cs); afx_msg void OnPaint(); afx_msg void OnTimer(UINT nIDEvent); afx_msg LRESULT OnSetText(WPARAM wp, LPARAM lp); DECLARE_DYNAMIC(CPopupText); DECLARE_MESSAGE_MAP(); }; PupText.cpp //////////////////////////////////////////////////////////////// // VCKBASE -- September 2000 // Visual C++ 6.0 环境编译, Windows 98 和 NT 环境运行. // #include "stdafx.h" #include "puptext.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNAMIC(CPopupText,CWnd) BEGIN_MESSAGE_MAP(CPopupText,CWnd) ON_WM_PAINT() ON_MESSAGE(WM_SETTEXT, OnSetText) ON_WM_TIMER() END_MESSAGE_MAP() CPopupText::CPopupText() { m_szMargins = CSize(4,4); // create font ?use system tooltip font CNonClientMetrics ncm; m_font.CreateFontIndirect(&ncm.lfStatusFont); } CPopupText::~CPopupText() { } // Create window. pt is upper-left or upper-right corner depending on // nStyle. // CPopupText::Create(CPoint pt, CWnd* pParentWnd, UINT nStyle, UINT nID) { m_nStyle = nStyle; return CreateEx(0, NULL, NULL, WS_POPUP|WS_VISIBLE, CRect(pt,CSize(0,0)), pParentWnd, nID); } // Someone changed the text: resize to fit new text // LRESULT CPopupText::OnSetText(WPARAM wp, LPARAM lp) { CClientDC dc = this; CFont* pOldFont = dc.SelectObject(&m_font); CRect rc; GetWindowRect(&rc); int x = (m_nStyle & JUSTIFYRIGHT) ? rc.right : rc.left; int y = rc.top; dc.DrawText(CString((LPCTSTR)lp), &rc, DT_CALCRECT); rc.InflateRect(m_szMargins); if (m_nStyle & JUSTIFYRIGHT) x -= rc.Width(); SetWindowPos(NULL,x,y,rc.Width(),rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE); return Default(); } // Paint the text. Use system colors // void CPopupText::OnPaint() { CPaintDC dc(this); CRect rc; GetClientRect(&rc); CString s; GetWindowText(s); CBrush b(GetSysColor(COLOR_INFOBK)); // use tooltip bg color dc.FillRect(&rc, &b); // draw text dc.SetBkMode(TRANSPARENT); CFont* pOldFont = dc.SelectObject(&m_font); dc.SetTextColor(GetSysColor(COLOR_INFOTEXT)); // tooltip text color dc.DrawText(s, &rc, DT_SINGLELINE|DT_CENTER|DT_VCENTER); dc.SelectObject(pOldFont); } // Register class if needed // BOOL CPopupText::PreCreateWindow(CREATESTRUCT& cs) { static CString sClassName; if (sClassName.IsEmpty()) sClassName = AfxRegisterWndClass(0); cs.lpszClass = sClassName; cs.style = WS_POPUP|WS_BORDER; cs.dwExStyle |= WS_EX_TOOLWINDOW; return CWnd::PreCreateWindow(cs); } // CPopupText is intended to be used on the stack, // not heap, so don't auto-delete. // void CPopupText::PostNcDestroy() { // don't delete this } // Show window with delay. No delay means show now. // void CPopupText::ShowDelayed(UINT msec) { if (msec==0) // no delay: show it now OnTimer(1); else // delay: set time SetTimer(1, msec, NULL); } // Cancel text梜ill timer and hide window // void CPopupText::Cancel() { KillTimer(1); ShowWindow(SW_HIDE); } // Timer popped: display myself and kill timer // void CPopupText::OnTimer(UINT nIDEvent) { ShowWindow(SW_SHOWNA); Invalidate(); UpdateWindow(); KillTimer(1); }CPopupText的基类是CWnd。用这个派生类不仅可以在列表框中实现类似Toolbar的提示窗口,还可以在其它的通用控件(如组合框、列表视图等)中实现类似Toolbar的提示窗口。
CListBoxTipHandler //////////////////////////////////////////////////////////////// // ListBoxTip.h // #pragma once #include "subclass.h" #include "puptext.h" // Generic tip-handler to display tip for wide text in a listbox. // To use: // - instantiate one of these for each listbox // - call Init // class CListBoxTipHandler : public CSubclassWnd { protected: UINT m_idMyControl; // id of listbox control UINT m_nCurItem; // index of current item BOOL m_bCapture; // whether mouse is captured static CPopupText g_wndTip; // THE tip window // subclass window proc virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp); // virtual fns you can override virtual void OnMouseMove(CPoint p); virtual BOOL IsRectCompletelyVisible(const CRect& rc); virtual UINT OnGetItemInfo(CPoint p, CRect& rc, CString& s); public: CListBoxTipHandler(); ~CListBoxTipHandler(); static UINT g_nTipTimeMsec; // global: msec wait before showing // tip void Init(CWnd* pListBox); // initialize }; //////////////////////////////////////////////////////////////// // ListBoxTip.cpp // #include "stdafx.h" #include "PupText.h" #include "ListBoxTip.h" // THE popup tip window CPopupText CListBoxTipHandler::g_wndTip; // Timeout before showing long listbox tip ?you can change UINT CListBoxTipHandler::g_nTipTimeMsec = 100; // .1 sec CListBoxTipHandler::CListBoxTipHandler() { m_nCurItem=-1; m_bCapture = FALSE; } CListBoxTipHandler::~CListBoxTipHandler() { } // Install hook. Initialize control ID from listbox and create // (invisible) tip window. // void CListBoxTipHandler::Init(CWnd* pListBox) { CSubclassWnd::HookWindow(pListBox); m_idMyControl = pListBox->GetDlgCtrlID(); if (!g_wndTip) { // create scroll tip window g_wndTip.Create(CPoint(0,0), NULL, 0); } } // "Hook" function traps messages sent to listbox/control. // LRESULT CListBoxTipHandler::WindowProc(UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_MOUSEMOVE: OnMouseMove(CPoint(GET_X_LPARAM(lp),GET_Y_LPARAM(lp))); break; } return CSubclassWnd::WindowProc(msg, wp, lp); } // User moved the mouse. // Get info for listbox item under mouse: // If text is too wide to fit in listbox, prepare tip window and start // timer to display it. // void CListBoxTipHandler::OnMouseMove(CPoint pt) { CListBox* pListBox = (CListBox*)CWnd::FromHandle(m_hWnd); if (!m_bCapture) { ::SetCapture(m_hWnd); m_bCapture = TRUE; } // Get text and text rectangle for item under mouse CString sText; // item text CRect rcText; // item text rect UINT nItem = OnGetItemInfo(pt, rcText, sText); if (nItem==-1 || nItem!=m_nCurItem) { g_wndTip.Cancel(); // new item, or no item: cancel popup text if (nItem>=0 && !IsRectCompletelyVisible(rcText)) { // new item, and not wholly visible: prepare popup tip CRect rc = rcText; pListBox->ClientToScreen(&rc); // text rect in screen coords g_wndTip.SetWindowText(sText); // set tip text to that of item // move tip window over list text g_wndTip.SetWindowPos(NULL, rc.left-3, rc.top, rc.Width()+6, rc.Height(), SWP_NOZORDER|SWP_NOACTIVATE); g_wndTip.ShowDelayed(g_nTipTimeMsec); // show popup text delayed } } m_nCurItem = nItem; if (nItem==-1) { ::ReleaseCapture(); m_bCapture=FALSE; } } // Determine if given rectangle is completely visible within listbox // BOOL CListBoxTipHandler::IsRectCompletelyVisible(const CRect& rc) { CListBox* pListBox = (CListBox*)CWnd::FromHandle(m_hWnd); CRect rcClient; pListBox->GetClientRect(&rcClient); return rcClient.Width() > rc.Width(); } // Get info (rectangle and text) for item under point // UINT CListBoxTipHandler::OnGetItemInfo(CPoint p, CRect& rc, CString& s) { CListBox* pListBox = (CListBox*)CWnd::FromHandle(m_hWnd); ASSERT_VALID(pListBox); BOOL bOutside; UINT nItem = pListBox->ItemFromPoint(p,bOutside); s.Empty(); if (!bOutside) { pListBox->GetText(nItem, s); pListBox->GetItemRect(nItem, &rc); CFont *pFont = pListBox->GetFont(); CClientDC dc(pListBox); CFont* pOldFont = dc.SelectObject(pFont); dc.DrawText(s,&rc,DT_CALCRECT); dc.SelectObject(pOldFont); return nItem; } return -1; }第一个技巧:如果用户把鼠标从文本提示窗口上移走,CListBoxTipHandler会调用CPopupText::Cancel 隐藏提示窗。当用户的鼠标在列表框项目间移动时不会有什么问题,但如果将鼠标完全移到列表框之外会发生什么呢?显然,你无法知道哪一个是最后一个WM_MOUSEMOVE消息。为了避免这种情况,CListBoxTipHandler代表列表框来捕获鼠标,所以全部的鼠标消息都到了CListBoxTipHandler,当鼠标移到列表框之外的情况发生时,CListBoxTipHandler释放鼠标。