借鉴文章
http://blog.csdn.net/lsldd/article/details/4595386
http://blog.csdn.net/dyzhen/article/details/6185863
谢谢以上两位大神
(1) 使在输入了一个完整的匹配项,或者回车选中某项时,触发CBN_SELCHANGE消息.
(2) ShowDropDown(TRUE)采用消息迂回调用.原因如下,当输入中文词组时,OnEditUpdate会逐字依次调用,也就是说一次性输入几个汉字它就调用几次,而在此当中直接调用ShowDropDown(TRUE)会导致组合框Edit框的内容瞬间变成了匹配选项的内容,且为高亮选中状态,OnEditUpdate接下去处理接下来的汉字的时候,就仅把这一个汉字当成了输入内容,前面的内容就丢失了,所以导致匹配失效. (哎...说的自己都不明白...调试跟踪就知道是咋回事了).
本文首先派生了一个CComboBox类CComboCompletion,然后增加虚函数PreTranslateMessage,处理键盘输入,然后增加CBN_DROPDOWN和CBN_EDITUPDATE消息的处理.
头文件:ComboCompletion.h
#if !defined(AFX_COMBOCOMPLETION_H__9255E6D2_71F7_48CD_B6F5_5B249E0BE307__INCLUDED_) #define AFX_COMBOCOMPLETION_H__9255E6D2_71F7_48CD_B6F5_5B249E0BE307__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // ComboCompletion.h : header file // ///////////////////////////////////////////////////////////////////////////// // CComboCompletion window #define WM_SHOWDROP WM_USER + 101 class CComboCompletion : public CComboBox { // Construction public: CComboCompletion(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CComboCompletion) public: virtual BOOL PreTranslateMessage(MSG* pMsg); //}}AFX_VIRTUAL // Implementation public: virtual ~CComboCompletion(); // Generated message map functions protected: //{{AFX_MSG(CComboCompletion) afx_msg void OnDropdown(); afx_msg void OnEditupdate(); afx_msg HRESULT OnShowDropDown(WPARAM wParam, LPARAM lParam); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: BOOL m_bAutoComplete; }; ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_COMBOCOMPLETION_H__9255E6D2_71F7_48CD_B6F5_5B249E0BE307__INCLUDED_)
源文件:ComboCompletion.cpp
// ComboCompletion.cpp : implementation file // #include "stdafx.h" #include "hrinetnsm_con.h" #include "ComboCompletion.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CComboCompletion CComboCompletion::CComboCompletion() { } CComboCompletion::~CComboCompletion() { } BEGIN_MESSAGE_MAP(CComboCompletion, CComboBox) //{{AFX_MSG_MAP(CComboCompletion) ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropdown) ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate) //}}AFX_MSG_MAP ON_MESSAGE(WM_SHOWDROP, OnShowDropDown) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CComboCompletion message handlers 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; }
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
后来发现这个类还是不能满足条件,如果希望输入首字母,自动补全的同时还要删除无关信息,终于在pudn找到一个
头文件:AutoCombox1.h
#pragma once // CAutoCombox1 class CAutoCombox1 : public CComboBox { DECLARE_DYNAMIC(CAutoCombox1) public: CAutoCombox1(); virtual ~CAutoCombox1(); // manipulating listbox items int AddString(LPCTSTR lpszString); int DeleteString(UINT nIndex); int InsertString(int nIndex, LPCTSTR lpszString); void ResetContent(); //set state void SetFlag(UINT nFlag) {m_nFlag = nFlag;} private: int Dir(UINT attr, LPCTSTR lpszWildCard) {ASSERT(FALSE);}//forbidden protected: virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); void AutoSelect(); void AutoMatchAndSel(); DECLARE_MESSAGE_MAP() private: CEdit* m_pEdit; //edit control UINT m_nFlag; //some flag //bit 0: 0 is show all, 1 is remove not matching, if no maching, show all. CStringArray m_strArr; };
源文件:AutoCombox1.cpp
// AutoCombox1.cpp : implementation file // #include "stdafx.h" // #include "MyCombox.h" #include "AutoCombox1.h" // CAutoCombox1 IMPLEMENT_DYNAMIC(CAutoCombox1, CComboBox) CAutoCombox1::CAutoCombox1() { m_pEdit = NULL; m_nFlag = 0; } CAutoCombox1::~CAutoCombox1() { if (m_pEdit) { if (::IsWindow(m_hWnd)) { m_pEdit->UnsubclassWindow(); } delete m_pEdit; m_pEdit = NULL; } } BEGIN_MESSAGE_MAP(CAutoCombox1, CComboBox) END_MESSAGE_MAP() // CAutoCombox1 message handlers //自动选择最匹配的,如果没有,则不选择。//////////////////////////////// void CAutoCombox1::AutoSelect() { // Make sure we can 'talk' to the edit control if ( m_pEdit == NULL ) { m_pEdit = new CEdit(); m_pEdit->SubclassWindow(GetDlgItem(1001)->GetSafeHwnd()); } // Save the state of the edit control CString strText; //取得输入字符串 int nStart = 0, nEnd = 0; //取得光标位置 m_pEdit->GetWindowText(strText); m_pEdit->GetSel(nStart, nEnd); // Perform actual completion int nBestIndex = -1; //是否能找到匹配的字符 int nBestFrom = INT_MAX; //匹配开始的字符 if (!strText.IsEmpty()) { for ( int nIndex=0; nIndex<GetCount(); ++nIndex ) { CString str; GetLBText(nIndex,str); int nFrom = str.Find(strText); if ( nFrom != -1 && nFrom < nBestFrom )//能匹配,而且是更好的匹配,才记录 { nBestIndex = nIndex; nBestFrom = nFrom; } }//for } //Set select index if (!GetDroppedState()) { ShowDropDown(TRUE); m_pEdit->SetWindowText(strText); m_pEdit->SetSel(nStart, nEnd); } if ( GetCurSel() != nBestIndex ) { // Select the matching entry in the list SetCurSel(nBestIndex); // Restore the edit control m_pEdit->SetWindowText(strText); m_pEdit->SetSel(nStart, nEnd); } } //删除不匹配的,自动选择剩余中最匹配的,如果没有,则显示全部。////////////// void CAutoCombox1::AutoMatchAndSel() { // Make sure we can 'talk' to the edit control if ( m_pEdit == NULL ) { m_pEdit = new CEdit(); m_pEdit->SubclassWindow(GetDlgItem(1001)->GetSafeHwnd()); } // 保存edit控件的状态 CString strText; //取得输入字符串 int nStart = 0, nEnd = 0; //取得光标位置 m_pEdit->GetWindowText(strText); m_pEdit->GetSel(nStart, nEnd); //清空CComboBox里面的数据 CComboBox::ResetContent(); // 重新填充列表,并选择最合适的 int nBestIndex = -1; //是否能找到匹配的字符 int nBestFrom = INT_MAX; //匹配开始的字符 if (!strText.IsEmpty()) { for ( int nIndex=0; nIndex<m_strArr.GetSize(); ++nIndex ) { int nFrom = m_strArr[nIndex].Find(strText); char kk = m_strArr[nIndex].GetAt(0); char jj = strText.GetAt(0); BOOL flag = FALSE; if (kk==jj) { flag = TRUE; } if ( nFrom != -1&&flag==TRUE)//能匹配 { int n = CComboBox::AddString(m_strArr[nIndex]); if (nFrom < nBestFrom)//更好的匹配,则记录 { nBestIndex = n; nBestFrom = nFrom; } } }//for } if (GetCount() == 0) //没有的显示所有 { for (int nIndex=0; nIndex<m_strArr.GetSize(); ++nIndex) { CComboBox::AddString(m_strArr[nIndex]); } } //显示下拉列表 if (!GetDroppedState()) { ShowDropDown(TRUE); } //设置选择项 // Select and Restore the edit control SetCurSel(nBestIndex); m_pEdit->SetWindowText(strText); m_pEdit->SetSel(nStart, nEnd); } // manipulating listbox items int CAutoCombox1::AddString(LPCTSTR lpszString) { m_strArr.Add(lpszString); return CComboBox::AddString(lpszString); } int CAutoCombox1::DeleteString(UINT nIndex) { m_strArr.RemoveAt(nIndex); return CComboBox::DeleteString(nIndex); } int CAutoCombox1::InsertString(int nIndex, LPCTSTR lpszString) { m_strArr.InsertAt(nIndex, lpszString); return CComboBox::InsertString(nIndex, lpszString); } void CAutoCombox1::ResetContent() { m_strArr.RemoveAll(); CComboBox::ResetContent(); } //All Message Handle Dispatch BOOL CAutoCombox1::OnCommand(WPARAM wParam, LPARAM lParam) { if ( HIWORD(wParam) == EN_CHANGE ) { if (m_nFlag & 0x01) { AutoMatchAndSel(); } else { AutoSelect(); } return true; } else { return CComboBox::OnCommand(wParam, lParam); } }
本文的程序源码下载:AutoComboBox_模糊匹配&自动匹配 (提取码:8353)