读书人

分享一个智能提醒搜索控件的实现

发布时间: 2012-09-25 09:55:58 作者: rapoo

分享一个智能提示搜索控件的实现

功能:当你在编辑框中键入一个字符(或者一个字符串),该编辑框下方将自动弹出一个list列表,显示所有包含该字符的项。

使用方法:在你的应用程序中,包含以下两个文件;在你的UI中,假如用于搜索的那个编辑框是成员变量:CEdit m_Edit; 将CEdit替换为CTipEditBox。只需在初始化时调用

m_Edit.setString(str_vector);其中str_vector是你需要搜索的字符串的范围。在选中某一项或者回车时,你的主窗口将收到MyMsg_SEARCH_COMPLETE消息。可以响应该消息来处理搜索到得结果命令。

demo:

分享一个智能提醒搜索控件的实现

以下请看CTipEditBox.h:

#pragma once// CTipEditBox#include <vector>using std::vector;class CMyTipListCtrl : public CListCtrl{ DECLARE_DYNAMIC(CMyTipListCtrl)public: CMyTipListCtrl(CWnd* p_editBox); virtual ~CMyTipListCtrl(); bool FromNoSel; bool ToNoSel; bool isEnter;protected: DECLARE_MESSAGE_MAP() CWnd* m_pEditBox; int pre_selItem; int m_selItem; static int m_counter; public: afx_msg void OnNMClick(NMHDR *pNMHDR, LRESULT *pResult);public: afx_msg void OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult);public: afx_msg void OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult);public: afx_msg void OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult);};class CTipEditBox : public CEdit{ DECLARE_DYNAMIC(CTipEditBox)public: CTipEditBox(); virtual ~CTipEditBox();protected: DECLARE_MESSAGE_MAP() void release_listCtrl();  //release the list control if user is created at the outside of class void change_current_select(bool isDown); //select the next or previews item int get_strings_max_width(CDC* pDC, vector<CString>& strings); //get the lengthest strings width //get the matched the strings, who will be showed in the list vector<CString> match_strings(CString, vector<CString>& strings);  //create a list control BOOL create_listCtrl();public: //you must call this function to initialize the edit-list control void set_strings(vector<CString> & strings); BOOL MsgOfComplete();public: afx_msg void OnEnChange(); vector<CString> m_tipstrings; CMyTipListCtrl * m_pTipList;public: afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);public: virtual BOOL PreTranslateMessage(MSG* pMsg);};


下面是实现文件 TipEditBox.cpp:

// TipEditBox.cpp : implementation file//#include "stdafx.h"#include "TipEditBox.h"#define MyMsg_SEARCH_COMPLETE WM_USER + 100#define SELCOLOR RGB(0,128,255)#define UNSELCOLOR RGB(255,255,255)// CTipEditBoxIMPLEMENT_DYNAMIC(CTipEditBox, CEdit)CTipEditBox::CTipEditBox(){m_pTipList = NULL;}CTipEditBox::~CTipEditBox(){if(m_pTipList != NULL)release_listCtrl();}BEGIN_MESSAGE_MAP(CTipEditBox, CEdit)ON_CONTROL_REFLECT(EN_CHANGE, &CTipEditBox::OnEnChange)ON_WM_KEYDOWN()END_MESSAGE_MAP()void  CTipEditBox::set_strings(vector<CString> & strings){m_tipstrings.clear();if(strings.empty())return;for(int i = 0; i < strings.size(); i++)m_tipstrings.push_back(strings[i]);create_listCtrl();}BOOL CTipEditBox::create_listCtrl(){m_pTipList = new CMyTipListCtrl(this);if(!m_pTipList) return FALSE;BOOL ret = m_pTipList->Create(WS_CHILD|LVS_REPORT|LVS_SINGLESEL/*|LVS_SHOWSELALWAYS*/|LVS_ALIGNTOP|LVS_NOCOLUMNHEADER, CRect(0,0,0,0), AfxGetMainWnd(),2);m_pTipList->SetExtendedStyle(LVS_EX_FULLROWSELECT);m_pTipList->InsertColumn(0,_T(""));return ret;}void CTipEditBox::release_listCtrl(){if(m_pTipList)delete m_pTipList;m_pTipList = NULL;}void CTipEditBox::change_current_select(bool isDown){//get the next item need to selectint selIdx = m_pTipList->GetNextItem(-1,LVNI_ALL|LVNI_SELECTED);int count = m_pTipList->GetItemCount();int idxNext;if(isDown) //downidxNext = (selIdx + 1) % count;else       //up{if(selIdx == 0 || selIdx == -1)idxNext = count - 1;elseidxNext = selIdx - 1;}//set the item selected and set window text as this item text/*CString result = m_pTipList->GetItemText(idxNext,0);this->SetWindowText(result);*/if(selIdx != idxNext){m_pTipList->SetItemState(idxNext,LVIS_SELECTED,LVIS_SELECTED);//enable the scroll barm_pTipList->EnsureVisible(idxNext,FALSE);m_pTipList->RedrawItems(idxNext,idxNext);}}int CTipEditBox::get_strings_max_width(CDC* pDC, vector<CString>& strings){int max_length = 0;for(int i = 0; i < strings.size(); i++){CSize size = pDC->GetTextExtent(strings[i],strings[i].GetLength());if(max_length < size.cx)max_length = size.cx;}return max_length;}vector<CString> CTipEditBox::match_strings(CString str, vector<CString>& strings){vector<CString> result;for(int i = 0; i < strings.size(); i++){if(strings[i].Find(str) != -1)result.push_back(strings[i]);}return result;}BOOL CTipEditBox::MsgOfComplete(){CWnd * pWnd = this->GetParent();if(pWnd == NULL || pWnd->m_hWnd == NULL)pWnd = ::AfxGetMainWnd();if(pWnd == NULL || pWnd->m_hWnd == NULL)return FALSE;return pWnd->PostMessage(MyMsg_SEARCH_COMPLETE,0,0);}// CTipEditBox message handlersvoid CTipEditBox::OnEnChange(){// TODO:  If this is a RICHEDIT control, the control will not// send this notification unless you override the CEdit::OnInitDialog()// function and call CRichEditCtrl().SetEventMask()// with the ENM_CHANGE flag ORed into the mask.// TODO:  Add your control notification handler code here//get editor box's stringCString editStr;this->GetWindowText(editStr);//using editStr to match the resultvector<CString> search_result = match_strings(editStr,m_tipstrings);if(search_result.empty()){if(m_pTipList && m_pTipList->IsWindowVisible())m_pTipList->ShowWindow(SW_HIDE);return;}//get the list control's rectif(m_pTipList && m_pTipList->IsWindowVisible()){m_pTipList->ToNoSel = TRUE;m_pTipList->ShowWindow(SW_HIDE);int selIdx = m_pTipList->GetNextItem(-1,LVNI_ALL|LVNI_SELECTED);if(selIdx >= 0)m_pTipList->SetItemState(selIdx,0,LVNI_SELECTED);}//prepareCDC *pDC = m_pTipList->GetDC();int max_w = get_strings_max_width(pDC,search_result);m_pTipList->ReleaseDC(pDC);CWnd* pWnd = AfxGetMainWnd();CRect rc,rc1,rc2;this->GetWindowRect(rc);pWnd->ScreenToClient(rc);pWnd->GetWindowRect(rc2);int list_h, list_w,min_w;bool hasSBar = FALSE;//calculate width of the list controlmin_w = rc.Width();if(max_w < min_w)max_w = min_w;list_w = max_w < (rc2.Width() - rc.left) ? max_w : (rc2.Width() - rc.left);//calculate height of the list control#define ITEMHEIGHT 22#define MAXCOUNT 6int count = search_result.size();if(count > MAXCOUNT){hasSBar = TRUE;count = MAXCOUNT;}list_h = (rc2.Height() - rc.bottom) < count * ITEMHEIGHT ? (rc2.Height() - rc.bottom) : count * ITEMHEIGHT;#undef ITEMHEIGHT#undef MAXCOUNTrc1.left = rc.left;rc1.top = rc.bottom;rc1.right = rc1.left + list_w;rc1.bottom = rc1.top + list_h;//if editStr is not empty,display the list, and insert the items matchedif(!editStr.IsEmpty()){m_pTipList->MoveWindow(rc1,TRUE);m_pTipList->DeleteAllItems();int Bar_w = hasSBar ? 18 : 0;m_pTipList->SetColumnWidth(0,rc1.Width()-Bar_w);for(int i = 0; i < search_result.size(); i++)m_pTipList->InsertItem(i,search_result[i]);m_pTipList->FromNoSel = TRUE;m_pTipList->ShowWindow(SW_SHOW);m_pTipList->BringWindowToTop();
}else{m_pTipList->ToNoSel = TRUE;m_pTipList->ShowWindow(SW_HIDE);int selIdx = m_pTipList->GetNextItem(-1,LVNI_ALL|LVNI_SELECTED);if(selIdx >= 0)m_pTipList->SetItemState(selIdx,0,LVNI_SELECTED);}}void CTipEditBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags){// TODO: Add your message handler code here and/or call defaultCString text;this->GetWindowText(text);if(!text.IsEmpty() && m_pTipList && m_pTipList->IsWindowVisible() && (!m_tipstrings.empty())){switch(nChar){case VK_DOWN:change_current_select(true);return;case VK_UP:change_current_select(false);return;default:break;}}CEdit::OnKeyDown(nChar, nRepCnt, nFlags);}BOOL CTipEditBox::PreTranslateMessage(MSG* pMsg){// TODO: Add your specialized code here and/or call the base classif(pMsg->message == WM_KEYDOWN){switch(pMsg->wParam){case VK_RETURN:if(m_pTipList && m_pTipList->IsWindowVisible()){int idx = m_pTipList->GetNextItem(-1,LVNI_ALL|LVNI_SELECTED);CString str;if(idx >= 0){str = m_pTipList->GetItemText(idx,0);m_pTipList->isEnter = TRUE;m_pTipList->ToNoSel = TRUE;m_pTipList->SetItemState(idx,0,LVNI_SELECTED);}m_pTipList->ShowWindow(SW_HIDE);this->SetWindowText(str);this->SetSel(str.GetLength(),str.GetLength());}this->MsgOfComplete();return TRUE;case VK_ESCAPE:if(m_pTipList && m_pTipList->IsWindowVisible()){int idx = m_pTipList->GetNextItem(-1,LVNI_ALL|LVNI_SELECTED);if(idx >= 0){m_pTipList->isEnter = TRUE;m_pTipList->ToNoSel = TRUE;m_pTipList->SetItemState(idx,0,LVNI_SELECTED);}m_pTipList->ShowWindow(SW_HIDE);}return TRUE;default:break;}}return CEdit::PreTranslateMessage(pMsg);}// CMyTipListCtrlIMPLEMENT_DYNAMIC(CMyTipListCtrl, CListCtrl)int CMyTipListCtrl::m_counter = 0;CMyTipListCtrl::CMyTipListCtrl(CWnd* p_editBox){m_pEditBox = p_editBox;pre_selItem = -1;m_selItem = -1;FromNoSel = FALSE;ToNoSel = FALSE;isEnter = FALSE;}CMyTipListCtrl::~CMyTipListCtrl(){}BEGIN_MESSAGE_MAP(CMyTipListCtrl, CListCtrl)ON_NOTIFY_REFLECT(NM_CLICK, &CMyTipListCtrl::OnNMClick)ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, &CMyTipListCtrl::OnLvnItemchanged)ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CMyTipListCtrl::OnNMCustomdraw)ON_NOTIFY_REFLECT(LVN_ITEMCHANGING, &CMyTipListCtrl::OnLvnItemchanging)END_MESSAGE_MAP()// CMyTipListCtrl message handlersvoid CMyTipListCtrl::OnNMClick(NMHDR *pNMHDR, LRESULT *pResult){// TODO: Add your control notification handler code hereNM_LISTVIEW * pNMList = (NM_LISTVIEW*) pNMHDR;CString str = this->GetItemText(pNMList->iItem,0);isEnter = TRUE;ToNoSel = TRUE;SetItemState(pNMList->iItem,0,LVNI_SELECTED);m_pEditBox->SetWindowText(str);this->ShowWindow(SW_HIDE);((CTipEditBox*)m_pEditBox)->MsgOfComplete();*pResult = 0;}void CMyTipListCtrl::OnLvnItemchanged(NMHDR *pNMHDR, LRESULT *pResult){LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);// TODO: Add your control notification handler code here*pResult = 0;}void CMyTipListCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult){LPNMLVCUSTOMDRAW pNMCD = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);*pResult = 0;// TODO: Add your control notification handler code hereif(CDDS_PREPAINT == pNMCD->nmcd.dwDrawStage)*pResult = CDRF_NOTIFYITEMDRAW;else if(CDDS_ITEMPREPAINT == pNMCD->nmcd.dwDrawStage){if(pre_selItem == pNMCD->nmcd.dwItemSpec){pNMCD->clrTextBk = UNSELCOLOR;}else if(m_selItem == pNMCD->nmcd.dwItemSpec)pNMCD->clrTextBk = SELCOLOR;*pResult = 0;}m_counter = 0;}void CMyTipListCtrl::OnLvnItemchanging(NMHDR *pNMHDR, LRESULT *pResult){LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);// TODO: Add your control notification handler code herem_counter++;if(FromNoSel && !isEnter){pre_selItem = -1;m_selItem = pNMLV->iItem;FromNoSel = FALSE;}else if(ToNoSel){pre_selItem = pNMLV->iItem;m_selItem = -1;ToNoSel = FALSE;isEnter = FALSE;}else{if(m_counter == 1)pre_selItem = pNMLV->iItem;else if(m_counter == 2){m_selItem = pNMLV->iItem;this->RedrawItems(pre_selItem,pNMLV->iItem);}}*pResult = 0;}


读书人网 >编程

热点推荐