VC++ combobox模糊匹配,自動匹配

借鑑文章

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);
	}
}

=================================================================================================================

程序猿和程序媛必備的咖啡-OneDay咖啡生活-https://shop110384469.taobao.com/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章