集成了自动完成、tooltip提示和自动加宽listbox的ComboBox
2011年05月06日
头文件CComboCompletion.h: #if !defined(AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5 _9D44_87410EF665A4__INCLUDED_) #define AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5_9D44_874 10EF665A4__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // ComboCompletion.h : header file // #define WM_SHOWDROP WM_USER + 101 // /// // CComboCompletion window class CComboCompletion : public CComboBox { // Construction public: CComboCompletion(BOOL bEnableTool = TRUE, BOOL bEnabelResize = FALSE); // Attributes public: BOOL m_bEnableTool; BOOL m_bEnableResize; private: static CWnd m_tipWnd; // used to draw tooltip text static CFont m_font; static CMap m_mapWndProc; COMBOBOXINFO m_cbi; // is mouse in area static BOOL m_bEnter; // last tooltip_show_item of list static int m_nOriSel; BOOL m_bAutoComplete; // Operations public: void CreateTooltipWnd(); void DestroyTooltipWnd(); private: // specify the windowproc for edit and list void InstallEditAndListWndProc(); static LRESULT CALLBACK HookTooltipWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK HookEditboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK HookListboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // handler for paint tooltip window static void OnHandleTooltipPaint(); // handler for mouse move on listbox static void OnHandleListboxMousemove(CListBox *pList, CPoint pt); // handler for mouse hover on editbox static void OnHandleEditboxMousehover(CWnd *pWnd); // track mouse event static BOOL OnTrackMouseEvent(HWND hWnd, DWORD dwFlags); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CComboCompletion) public: virtual BOOL PreTranslateMessage(MSG* pMsg); protected: virtual void PreSubclassWindow(); //}}AFX_VIRTUAL // Implementation public: virtual ~CComboCompletion(); // Generated message map functions protected: //{{AFX_MSG(CComboCompletion) afx_msg void OnDropdown(); afx_msg void OnEditupdate(); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); //}}AFX_MSG afx_msg HRESULT OnShowDropDown(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() }; // /// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5 _9D44_87410EF665A4__INCLUDED_) 源文件CComboCompletion.cpp // ComboCompletion.cpp : implementation file // #include "stdafx.h" #include "DlgBase.h" #include "ComboCompletion.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // /// // CComboCompletion CWnd CComboCompletion::m_tipWnd; CMap CComboCompletion::m_mapWndProc; CFont CComboCompletion::m_font; BOOL CComboCompletion::m_bEnter = FALSE; int CComboCompletion::m_nOriSel = LB_ERR; CComboCompletion::CComboCompletion(BOOL bEnableTool /*= TRUE*/, BOOL bEnabelResize /*= FALSE*/) { // use system default tool-tip font NONCLIENTMETRICS ncm; ZeroMemory(&ncm, sizeof(NONCLIENTMETRICS)); ncm.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); if (m_font.m_hObject == NULL) { m_font.CreateFontIndirect(&ncm.lfStatusFont); } m_bEnableTool = bEnableTool; m_bEnableResize = bEnabelResize; CreateTooltipWnd(); } CComboCompletion::~CComboCompletion() { if (m_cbi.hwndItem) { m_mapWndProc.RemoveKey(m_cbi.hwndItem); } else if (m_cbi.hwndList) { m_mapWndProc.RemoveKey(m_cbi.hwndList); } // destroy tooltip window DestroyTooltipWnd(); m_mapWndProc.RemoveAll(); } BEGIN_MESSAGE_MAP(CComboCompletion, CComboBox) //{{AFX_MSG_MAP(CComboCompletion) ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown) ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate) ON_WM_CTLCOLOR() //}}AFX_MSG_MAP END_MESSAGE_MAP() // /// // CComboCompletion message handlers void CComboCompletion::CreateTooltipWnd() { if (!::IsWindow(m_tipWnd.m_hWnd)) { m_tipWnd.CreateEx(WS_EX_TOOLWINDOW, AfxRegisterWndClass(0), NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL); WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_tipWnd.m_hWnd, GWL_WNDPROC, (LONG)HookTooltipWndProc); m_mapWndProc.SetAt(m_tipWnd.m_hWnd, oldWndProc); } } void CComboCompletion::DestroyTooltipWnd() { // must first destroy window m_tipWnd.DestroyWindow(); // second,remove wndproc WNDPROC oldWndProc; HWND hWnd = m_tipWnd.m_hWnd; BOOL bRet = m_mapWndProc.Lookup(hWnd, oldWndProc); if (bRet == TRUE) { m_mapWndProc.RemoveKey(hWnd); } } void CComboCompletion::InstallEditAndListWndProc() { ZeroMemory(&m_cbi, sizeof(COMBOBOXINFO)); m_cbi.cbSize = sizeof(COMBOBOXINFO); ::GetComboBoxInfo(m_hWnd, &m_cbi); if (m_cbi.hwndItem) {// specify wndproc for editbox WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_cbi.hwndItem, GWL_WNDPROC, (LONG)HookEditboxWndProc); m_mapWndProc.SetAt(m_cbi.hwndItem, oldWndProc); } if (m_cbi.hwndList) {// specify wndproc for listbox WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_cbi.hwndList, GWL_WNDPROC, (LONG)HookListboxWndProc); m_mapWndProc.SetAt(m_cbi.hwndList, oldWndProc); } } LRESULT CALLBACK CComboCompletion::HookTooltipWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_PAINT) { CComboCompletion::OnHandleTooltipPaint(); } else if (uMsg == WM_SHOWWINDOW && wParam == FALSE) { ReleaseCapture(); } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK CComboCompletion::HookEditboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CWnd *pWnd = CWnd::FromHandle(hWnd); if (uMsg == WM_MOUSEMOVE) { // note,mouse-leave msg must be send manual CPoint pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); CRect rect; pWnd->GetClientRect(&rect); if (!rect.PtInRect(pt)) { ::SendMessage(hWnd, WM_MOUSELEAVE, wParam, lParam); } // when mouse enter edit-box,start tracking mouse event if (!m_bEnter) { OnTrackMouseEvent(hWnd, TME_HOVER|TME_LEAVE); m_bEnter = TRUE; } } else if (uMsg == WM_MOUSEHOVER) { OnHandleEditboxMousehover(pWnd); } else if (uMsg == WM_MOUSELEAVE) { m_bEnter = FALSE; m_tipWnd.ShowWindow(SW_HIDE); } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK CComboCompletion::HookListboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CListBox *pList = (CListBox *)CWnd::FromHandle(hWnd); if (uMsg == WM_MOUSEMOVE) { CPoint pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); CRect rect; pList->GetClientRect(&rect); if (rect.PtInRect(pt)) { CComboCompletion::OnHandleListboxMousemove(pList, pt); } else { ::SendMessage(hWnd, WM_MOUSELEAVE, wParam, lParam); } // track mouse-leave msg if (m_bEnter == FALSE) { OnTrackMouseEvent(hWnd, TME_LEAVE); m_bEnter = TRUE; } } else if (uMsg == WM_MOUSELEAVE) { m_nOriSel = LB_ERR; m_bEnter = FALSE; m_tipWnd.ShowWindow(SW_HIDE); } else if (uMsg == WM_CAPTURECHANGED) {// note, because capture msg is handled by us,so it need not default handle return 1; } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } void CComboCompletion::OnHandleTooltipPaint() { // get dc CPaintDC dc(&m_tipWnd); // specify the rect CRect rect; m_tipWnd.GetClientRect(&rect); // draws a border around the specified rectangle CBrush border(RGB(0,0,0)); dc.FrameRect(&rect, &border); // fill the rectangle CBrush fill(GetSysColor(COLOR_INFOBK)); dc.FillRect(&rect, &fill); // specify the font CFont *pOldfont = dc.SelectObject(&m_font); // draw text CString strText; m_tipWnd.GetWindowText(strText); dc.SetBkMode(TRANSPARENT); dc.DrawText(strText, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX); // recover the default font dc.SelectObject(pOldfont); } BOOL CComboCompletion::OnTrackMouseEvent(HWND hWnd, DWORD dwFlags) { TRACKMOUSEEVENT track; ZeroMemory(&track, sizeof(TRACKMOUSEEVENT)); track.cbSize = sizeof(TRACKMOUSEEVENT); track.hwndTrack = hWnd; track.dwFlags = dwFlags; track.dwHoverTime = HOVER_DEFAULT; return _TrackMouseEvent(&track); } void CComboCompletion::OnHandleEditboxMousehover(CWnd *pWnd)// pWnd is the editbox pointer { // get rect of editbox CRect rcEdit; pWnd->GetClientRect(&rcEdit); CString strText; pWnd->GetWindowText(strText); // get tooltip dc CDC *pDc = m_tipWnd.GetDC(); CFont *pOldfont = pDc->SelectObject(&m_font); // use drawtext to calculate the actual rect of text CRect rcDraw = rcEdit; pDc->DrawText(strText, &rcDraw, DT_CALCRECT|DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_ NOPREFIX); // release tootip dc pDc->SelectObject(pOldfont); ::ReleaseDC(m_tipWnd.m_hWnd, pDc->m_hDC); if (rcDraw.Width() tooltip m_tipWnd.ShowWindow(SW_HIDE); } else {// if text is longer than edit, then show tooltip rcDraw.bottom = rcEdit.bottom; rcDraw.InflateRect(2, 2);// increase the width and height of draw rect pWnd->ClientToScreen(&rcDraw); m_tipWnd.SetWindowText(strText); ::SetCapture(pWnd->m_hWnd);// combobox has capture always m_tipWnd.SetWindowPos(&CWnd::wndTopMost, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), SWP_NOACTIVATE|SWP_SHOWWINDOW); } } void CComboCompletion::OnHandleListboxMousemove(CListBo x *pList, CPoint pt) { BOOL bOut = TRUE; int nSel = pList->ItemFromPoint(pt, bOut); if (nSel == m_nOriSel) return; if (nSel != LB_ERR && bOut == FALSE) { m_nOriSel = nSel; CString strText; pList->GetText(nSel, strText); CRect rcItem; pList->GetItemRect(nSel, &rcItem); CRect rcDraw = rcItem; CDC *pDc = m_tipWnd.GetDC(); CFont *pOldfont = pDc->SelectObject(&m_font); // re-calculate the acutal rect of text pDc->DrawText(strText, &rcDraw, DT_CALCRECT|DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_ NOPREFIX); CString strWidth; strWidth.Format("%d", rcDraw.Width()); OutputDebugString(strWidth); pDc->SelectObject(pOldfont); ::ReleaseDC(m_tipWnd.m_hWnd, pDc->m_hDC); if (rcDraw.Width() tooltip m_tipWnd.ShowWindow(SW_HIDE); } else { rcDraw.bottom = rcItem.bottom; rcDraw.InflateRect(2, 2);// increase the width and height of draw text pList->ClientToScreen(&rcDraw); m_tipWnd.ShowWindow(SW_HIDE); if (::GetCapture() != pList->m_hWnd) ::SetCapture(pList->m_hWnd); m_tipWnd.SetWindowText(strText); // note, in msdn, it say the SetWindowPos uses client coordinate,but in fact, it use screent coordinate m_tipWnd.SetWindowPos(&CWnd::wndTopMost, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), SWP_NOACTIVATE|SWP_SHOWWINDOW); } } } void CComboCompletion::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class if (m_bEnableTool == TRUE) { InstallEditAndListWndProc(); } CComboBox::PreSubclassWindow(); } // 以下为自动完成 BOOL CComboCompletion::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message == WM_CHAR) { m_bAutoComplete = TRUE; int nVirKey = pMsg->wParam; switch (nVirKey) { case VK_RETURN: { // 关闭下拉框 ShowDropDown(FALSE); CString strLine; GetWindowText(strLine); // 回车即选中高亮项 SelectString(-1, strLine); // 给父窗口发送选项改变的消息 WPARAM wParam = MAKELPARAM(GetDlgCtrlID(), CBN_SELCHANGE); GetParent()->PostMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd); break; } case VK_DELETE: case VK_BACK: m_bAutoComplete = FALSE; break; default: break; } } return CComboBox::PreTranslateMessage(pMsg); } void CComboCompletion::OnDropdown() { // TODO: Add your control notification handler code here SetCursor(LoadCursor(NULL, IDC_ARROW)); } void CComboCompletion::OnEditupdate() { // TODO: Add your control notification handler code here CString strLine; GetWindowText(strLine); int iHiLightStart = strLine.GetLength(); if(strLine.GetLength() == 0) { ShowDropDown(FALSE); SetWindowText(_T("")); m_bAutoComplete = TRUE; return; } // 处理删除操作 if(!m_bAutoComplete) { m_bAutoComplete = TRUE; return; } // 开始匹配用户输入 int iSelectedRow = FindString(-1, strLine); if(iSelectedRow >= 0) { // ShowDropDown(TRUE); PostMessage(WM_SHOWDROP, 0, 0); // 匹配的选项被选中 PostMessage(CB_SETCURSEL, iSelectedRow, 0); // 给父窗口发送选项改变的消息,这样可以保证当输入完整的匹配的部门时,不用回车也触发部门改变消息 WPARAM wParam = MAKELPARAM(GetDlgCtrlID(), CBN_SELCHANGE); GetParent()->PostMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd); } else { // ShowDropDown(FALSE); // SetWindowText(strLine); } // 高亮自动完成的部分 PostMessage(CB_SETEDITSEL, 0, MAKELPARAM(iHiLightStart, -1)); } HRESULT CComboCompletion::OnShowDropDown(WPARAM wParam, LPARAM lParam) { ShowDropDown(TRUE); return 0; } // 以下为自动加宽listbox HBRUSH CComboCompletion::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor); switch(nCtlColor) { case CTLCOLOR_EDIT: break; case CTLCOLOR_LISTBOX: if (m_bEnableResize == FALSE) { break; } int iItemNum=GetCount(); int iWidth=0; CString strItem; CClientDC dc(this); int iSaveDC=dc.SaveDC(); dc.SelectObject(GetFont()); int iVSWidth=::GetSystemMetrics(SM_CXVSCROLL); for(int i=0;i0) { CRect rc; pWnd->GetWindowRect(&rc); if(rc.Width()!=iWidth) { rc.right=rc.left+iWidth; pWnd->MoveWindow(&rc); } } break; } return hbr; } 以下为使用方法:
CDlgBaseDlg::CDlgBaseDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDlgBaseDlg::IDD, pParent), m_combo5(FALSE, TRUE)
2011年05月06日
头文件CComboCompletion.h: #if !defined(AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5 _9D44_87410EF665A4__INCLUDED_) #define AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5_9D44_874 10EF665A4__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // ComboCompletion.h : header file // #define WM_SHOWDROP WM_USER + 101 // /// // CComboCompletion window class CComboCompletion : public CComboBox { // Construction public: CComboCompletion(BOOL bEnableTool = TRUE, BOOL bEnabelResize = FALSE); // Attributes public: BOOL m_bEnableTool; BOOL m_bEnableResize; private: static CWnd m_tipWnd; // used to draw tooltip text static CFont m_font; static CMap m_mapWndProc; COMBOBOXINFO m_cbi; // is mouse in area static BOOL m_bEnter; // last tooltip_show_item of list static int m_nOriSel; BOOL m_bAutoComplete; // Operations public: void CreateTooltipWnd(); void DestroyTooltipWnd(); private: // specify the windowproc for edit and list void InstallEditAndListWndProc(); static LRESULT CALLBACK HookTooltipWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK HookEditboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK HookListboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // handler for paint tooltip window static void OnHandleTooltipPaint(); // handler for mouse move on listbox static void OnHandleListboxMousemove(CListBox *pList, CPoint pt); // handler for mouse hover on editbox static void OnHandleEditboxMousehover(CWnd *pWnd); // track mouse event static BOOL OnTrackMouseEvent(HWND hWnd, DWORD dwFlags); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CComboCompletion) public: virtual BOOL PreTranslateMessage(MSG* pMsg); protected: virtual void PreSubclassWindow(); //}}AFX_VIRTUAL // Implementation public: virtual ~CComboCompletion(); // Generated message map functions protected: //{{AFX_MSG(CComboCompletion) afx_msg void OnDropdown(); afx_msg void OnEditupdate(); afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); //}}AFX_MSG afx_msg HRESULT OnShowDropDown(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() }; // /// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_COMBOCOMPLETION_H__E1EBAD20_8F3B_48E5 _9D44_87410EF665A4__INCLUDED_) 源文件CComboCompletion.cpp // ComboCompletion.cpp : implementation file // #include "stdafx.h" #include "DlgBase.h" #include "ComboCompletion.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // /// // CComboCompletion CWnd CComboCompletion::m_tipWnd; CMap CComboCompletion::m_mapWndProc; CFont CComboCompletion::m_font; BOOL CComboCompletion::m_bEnter = FALSE; int CComboCompletion::m_nOriSel = LB_ERR; CComboCompletion::CComboCompletion(BOOL bEnableTool /*= TRUE*/, BOOL bEnabelResize /*= FALSE*/) { // use system default tool-tip font NONCLIENTMETRICS ncm; ZeroMemory(&ncm, sizeof(NONCLIENTMETRICS)); ncm.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); if (m_font.m_hObject == NULL) { m_font.CreateFontIndirect(&ncm.lfStatusFont); } m_bEnableTool = bEnableTool; m_bEnableResize = bEnabelResize; CreateTooltipWnd(); } CComboCompletion::~CComboCompletion() { if (m_cbi.hwndItem) { m_mapWndProc.RemoveKey(m_cbi.hwndItem); } else if (m_cbi.hwndList) { m_mapWndProc.RemoveKey(m_cbi.hwndList); } // destroy tooltip window DestroyTooltipWnd(); m_mapWndProc.RemoveAll(); } BEGIN_MESSAGE_MAP(CComboCompletion, CComboBox) //{{AFX_MSG_MAP(CComboCompletion) ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown) ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate) ON_WM_CTLCOLOR() //}}AFX_MSG_MAP END_MESSAGE_MAP() // /// // CComboCompletion message handlers void CComboCompletion::CreateTooltipWnd() { if (!::IsWindow(m_tipWnd.m_hWnd)) { m_tipWnd.CreateEx(WS_EX_TOOLWINDOW, AfxRegisterWndClass(0), NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL); WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_tipWnd.m_hWnd, GWL_WNDPROC, (LONG)HookTooltipWndProc); m_mapWndProc.SetAt(m_tipWnd.m_hWnd, oldWndProc); } } void CComboCompletion::DestroyTooltipWnd() { // must first destroy window m_tipWnd.DestroyWindow(); // second,remove wndproc WNDPROC oldWndProc; HWND hWnd = m_tipWnd.m_hWnd; BOOL bRet = m_mapWndProc.Lookup(hWnd, oldWndProc); if (bRet == TRUE) { m_mapWndProc.RemoveKey(hWnd); } } void CComboCompletion::InstallEditAndListWndProc() { ZeroMemory(&m_cbi, sizeof(COMBOBOXINFO)); m_cbi.cbSize = sizeof(COMBOBOXINFO); ::GetComboBoxInfo(m_hWnd, &m_cbi); if (m_cbi.hwndItem) {// specify wndproc for editbox WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_cbi.hwndItem, GWL_WNDPROC, (LONG)HookEditboxWndProc); m_mapWndProc.SetAt(m_cbi.hwndItem, oldWndProc); } if (m_cbi.hwndList) {// specify wndproc for listbox WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(m_cbi.hwndList, GWL_WNDPROC, (LONG)HookListboxWndProc); m_mapWndProc.SetAt(m_cbi.hwndList, oldWndProc); } } LRESULT CALLBACK CComboCompletion::HookTooltipWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_PAINT) { CComboCompletion::OnHandleTooltipPaint(); } else if (uMsg == WM_SHOWWINDOW && wParam == FALSE) { ReleaseCapture(); } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK CComboCompletion::HookEditboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CWnd *pWnd = CWnd::FromHandle(hWnd); if (uMsg == WM_MOUSEMOVE) { // note,mouse-leave msg must be send manual CPoint pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); CRect rect; pWnd->GetClientRect(&rect); if (!rect.PtInRect(pt)) { ::SendMessage(hWnd, WM_MOUSELEAVE, wParam, lParam); } // when mouse enter edit-box,start tracking mouse event if (!m_bEnter) { OnTrackMouseEvent(hWnd, TME_HOVER|TME_LEAVE); m_bEnter = TRUE; } } else if (uMsg == WM_MOUSEHOVER) { OnHandleEditboxMousehover(pWnd); } else if (uMsg == WM_MOUSELEAVE) { m_bEnter = FALSE; m_tipWnd.ShowWindow(SW_HIDE); } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } LRESULT CALLBACK CComboCompletion::HookListboxWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CListBox *pList = (CListBox *)CWnd::FromHandle(hWnd); if (uMsg == WM_MOUSEMOVE) { CPoint pt; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); CRect rect; pList->GetClientRect(&rect); if (rect.PtInRect(pt)) { CComboCompletion::OnHandleListboxMousemove(pList, pt); } else { ::SendMessage(hWnd, WM_MOUSELEAVE, wParam, lParam); } // track mouse-leave msg if (m_bEnter == FALSE) { OnTrackMouseEvent(hWnd, TME_LEAVE); m_bEnter = TRUE; } } else if (uMsg == WM_MOUSELEAVE) { m_nOriSel = LB_ERR; m_bEnter = FALSE; m_tipWnd.ShowWindow(SW_HIDE); } else if (uMsg == WM_CAPTURECHANGED) {// note, because capture msg is handled by us,so it need not default handle return 1; } // default handle other message WNDPROC oldWndProc; m_mapWndProc.Lookup(hWnd, oldWndProc); return ::CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam); } void CComboCompletion::OnHandleTooltipPaint() { // get dc CPaintDC dc(&m_tipWnd); // specify the rect CRect rect; m_tipWnd.GetClientRect(&rect); // draws a border around the specified rectangle CBrush border(RGB(0,0,0)); dc.FrameRect(&rect, &border); // fill the rectangle CBrush fill(GetSysColor(COLOR_INFOBK)); dc.FillRect(&rect, &fill); // specify the font CFont *pOldfont = dc.SelectObject(&m_font); // draw text CString strText; m_tipWnd.GetWindowText(strText); dc.SetBkMode(TRANSPARENT); dc.DrawText(strText, &rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX); // recover the default font dc.SelectObject(pOldfont); } BOOL CComboCompletion::OnTrackMouseEvent(HWND hWnd, DWORD dwFlags) { TRACKMOUSEEVENT track; ZeroMemory(&track, sizeof(TRACKMOUSEEVENT)); track.cbSize = sizeof(TRACKMOUSEEVENT); track.hwndTrack = hWnd; track.dwFlags = dwFlags; track.dwHoverTime = HOVER_DEFAULT; return _TrackMouseEvent(&track); } void CComboCompletion::OnHandleEditboxMousehover(CWnd *pWnd)// pWnd is the editbox pointer { // get rect of editbox CRect rcEdit; pWnd->GetClientRect(&rcEdit); CString strText; pWnd->GetWindowText(strText); // get tooltip dc CDC *pDc = m_tipWnd.GetDC(); CFont *pOldfont = pDc->SelectObject(&m_font); // use drawtext to calculate the actual rect of text CRect rcDraw = rcEdit; pDc->DrawText(strText, &rcDraw, DT_CALCRECT|DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_ NOPREFIX); // release tootip dc pDc->SelectObject(pOldfont); ::ReleaseDC(m_tipWnd.m_hWnd, pDc->m_hDC); if (rcDraw.Width() tooltip m_tipWnd.ShowWindow(SW_HIDE); } else {// if text is longer than edit, then show tooltip rcDraw.bottom = rcEdit.bottom; rcDraw.InflateRect(2, 2);// increase the width and height of draw rect pWnd->ClientToScreen(&rcDraw); m_tipWnd.SetWindowText(strText); ::SetCapture(pWnd->m_hWnd);// combobox has capture always m_tipWnd.SetWindowPos(&CWnd::wndTopMost, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), SWP_NOACTIVATE|SWP_SHOWWINDOW); } } void CComboCompletion::OnHandleListboxMousemove(CListBo x *pList, CPoint pt) { BOOL bOut = TRUE; int nSel = pList->ItemFromPoint(pt, bOut); if (nSel == m_nOriSel) return; if (nSel != LB_ERR && bOut == FALSE) { m_nOriSel = nSel; CString strText; pList->GetText(nSel, strText); CRect rcItem; pList->GetItemRect(nSel, &rcItem); CRect rcDraw = rcItem; CDC *pDc = m_tipWnd.GetDC(); CFont *pOldfont = pDc->SelectObject(&m_font); // re-calculate the acutal rect of text pDc->DrawText(strText, &rcDraw, DT_CALCRECT|DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_ NOPREFIX); CString strWidth; strWidth.Format("%d", rcDraw.Width()); OutputDebugString(strWidth); pDc->SelectObject(pOldfont); ::ReleaseDC(m_tipWnd.m_hWnd, pDc->m_hDC); if (rcDraw.Width() tooltip m_tipWnd.ShowWindow(SW_HIDE); } else { rcDraw.bottom = rcItem.bottom; rcDraw.InflateRect(2, 2);// increase the width and height of draw text pList->ClientToScreen(&rcDraw); m_tipWnd.ShowWindow(SW_HIDE); if (::GetCapture() != pList->m_hWnd) ::SetCapture(pList->m_hWnd); m_tipWnd.SetWindowText(strText); // note, in msdn, it say the SetWindowPos uses client coordinate,but in fact, it use screent coordinate m_tipWnd.SetWindowPos(&CWnd::wndTopMost, rcDraw.left, rcDraw.top, rcDraw.Width(), rcDraw.Height(), SWP_NOACTIVATE|SWP_SHOWWINDOW); } } } void CComboCompletion::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class if (m_bEnableTool == TRUE) { InstallEditAndListWndProc(); } CComboBox::PreSubclassWindow(); } // 以下为自动完成 BOOL CComboCompletion::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message == WM_CHAR) { m_bAutoComplete = TRUE; int nVirKey = pMsg->wParam; switch (nVirKey) { case VK_RETURN: { // 关闭下拉框 ShowDropDown(FALSE); CString strLine; GetWindowText(strLine); // 回车即选中高亮项 SelectString(-1, strLine); // 给父窗口发送选项改变的消息 WPARAM wParam = MAKELPARAM(GetDlgCtrlID(), CBN_SELCHANGE); GetParent()->PostMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd); break; } case VK_DELETE: case VK_BACK: m_bAutoComplete = FALSE; break; default: break; } } return CComboBox::PreTranslateMessage(pMsg); } void CComboCompletion::OnDropdown() { // TODO: Add your control notification handler code here SetCursor(LoadCursor(NULL, IDC_ARROW)); } void CComboCompletion::OnEditupdate() { // TODO: Add your control notification handler code here CString strLine; GetWindowText(strLine); int iHiLightStart = strLine.GetLength(); if(strLine.GetLength() == 0) { ShowDropDown(FALSE); SetWindowText(_T("")); m_bAutoComplete = TRUE; return; } // 处理删除操作 if(!m_bAutoComplete) { m_bAutoComplete = TRUE; return; } // 开始匹配用户输入 int iSelectedRow = FindString(-1, strLine); if(iSelectedRow >= 0) { // ShowDropDown(TRUE); PostMessage(WM_SHOWDROP, 0, 0); // 匹配的选项被选中 PostMessage(CB_SETCURSEL, iSelectedRow, 0); // 给父窗口发送选项改变的消息,这样可以保证当输入完整的匹配的部门时,不用回车也触发部门改变消息 WPARAM wParam = MAKELPARAM(GetDlgCtrlID(), CBN_SELCHANGE); GetParent()->PostMessage(WM_COMMAND, wParam, (LPARAM)m_hWnd); } else { // ShowDropDown(FALSE); // SetWindowText(strLine); } // 高亮自动完成的部分 PostMessage(CB_SETEDITSEL, 0, MAKELPARAM(iHiLightStart, -1)); } HRESULT CComboCompletion::OnShowDropDown(WPARAM wParam, LPARAM lParam) { ShowDropDown(TRUE); return 0; } // 以下为自动加宽listbox HBRUSH CComboCompletion::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor); switch(nCtlColor) { case CTLCOLOR_EDIT: break; case CTLCOLOR_LISTBOX: if (m_bEnableResize == FALSE) { break; } int iItemNum=GetCount(); int iWidth=0; CString strItem; CClientDC dc(this); int iSaveDC=dc.SaveDC(); dc.SelectObject(GetFont()); int iVSWidth=::GetSystemMetrics(SM_CXVSCROLL); for(int i=0;i0) { CRect rc; pWnd->GetWindowRect(&rc); if(rc.Width()!=iWidth) { rc.right=rc.left+iWidth; pWnd->MoveWindow(&rc); } } break; } return hbr; } 以下为使用方法:
CDlgBaseDlg::CDlgBaseDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDlgBaseDlg::IDD, pParent), m_combo5(FALSE, TRUE)