自动完成是个很酷也很实用的功能,比如在浏览器地址栏输入几个字母,相关的记录就会在下拉框中陈列出来。
最近在做公司产品UI部分的改善,原版本是MFC做的,我决定用WTL,于是就遇到自动完成控件的问题。遍寻Internet,WTL版的只找到一个用IEnumString+IE组件实现的,但是其个性化修改比较困难。so我决定自己用WTL改写该控件,在此向原作者 Andreas Kapust 致谢!
该控件包含四个文件:ACEdit.h, ACEdit.cpp, ACListWnd.h 和 ACListWnd.cpp。使用时用CACEdit 声明一个变量并将其与Edit控件或ComboBox控件建立关联(用DDX_CONTROL或者SubClassWindow)不可使用Attach,因为窗口过程函数必须要被替换。具体使用见代码。
因为我本人也是个VC菜鸟,望有同学发现错误或不足之处能不吝指点。感谢!另外在WTL动态创建的窗口销毁上我还是不太明了,请大虾赐教!
此为 _MODE_FIND_ALL_ 模式的效果
此为 _MODE_FILESYSTEM_ 模式的效果
原来不能发附件?贴下代码算了,要demo工程的邮件我 mforestlaw@163.com
ACEdit.h
/*************************************************************************
Author : Andreas Kapust
WTL Edition : Forest.Law
Contact me at : mforestlaw@163.com
**************************************************************************/
#pragma once #include "ACListWnd.h" class CACEdit : public CWindowImpl<CACEdit, CEdit>,
public CWinDataExchange<CACEdit>,
public CMessageFilter {
public:
CACEdit();
virtual ~CACEdit();
virtual BOOL PreTranslateMessage(MSG* pMsg); void SetMode(int nMode = _MODE_STANDARD_);
void SetSeparator(LPCTSTR lpszString, TCHAR lpszPrefixChar = );
int AddString(LPCTSTR lpszString);
int GetLBText(int nIndex, LPTSTR lpszText);
void GetLBText(int nIndex, CString& rString);
int SetDroppedWidth(UINT nWidth);
int FindString(int nStartAfter, LPCTSTR lpszString);
int SelectString(int nStartAfter, LPCTSTR lpszString);
void ShowDropDown(BOOL bShow = TRUE);
void ResetContent();
int GetCurSel();
void Init();
void AddSearchString(LPCTSTR lpszString);
void AddSearchStrings(LPCTSTR lpszStrings[]);
void RemoveSearchAll();
void SetStartDirectory(LPCTSTR lpszString);
void SetFontHeight(long lHeight);
void SetClearKillFocus(); CACListWnd m_Liste; protected:
BEGIN_MSG_MAP_EX(CACEdit)
REFLECTED_COMMAND_CODE_HANDLER(EN_KILLFOCUS, OnKillFocus)
REFLECTED_COMMAND_CODE_HANDLER(CBN_KILLFOCUS, OnKillFocus)
MSG_WM_KEYDOWN(OnKeyDown)
REFLECTED_COMMAND_CODE_HANDLER(EN_CHANGE, OnChange)
REFLECTED_COMMAND_CODE_HANDLER(CBN_EDITCHANGE, OnChange)
REFLECTED_COMMAND_CODE_HANDLER(CBN_DROPDOWN, OnCloseList)
MSG_WM_ERASEBKGND(OnEraseBkgnd)
MESSAGE_HANDLER(ENAC_UPDATE, OnUpdateFromList)
MSG_WM_DESTROY(OnDestroy)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP() LRESULT OnKillFocus(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled);
void OnKeyDown(TCHAR vkey, UINT repeats, UINT code);
LRESULT OnChange(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled);
LRESULT OnCloseList(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled);
LRESULT OnEraseBkgnd(HDC hDC);
LRESULT OnUpdateFromList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
void OnDestroy(); void ReadDirectory(CString strDir);
int FindSepLeftPos(int nPos, bool bFindSepLeftPos = false);
int FindSepLeftPos2(int nPos);
int FindSepRightPos(int nPos);
bool HandleKey(UINT nChar, bool bFromChild); CString m_strEditText;
CString m_strSeparator;
CString m_strLastDirectory;
TCHAR m_prefixChar;
int m_nMode;
bool m_bCursorMode;
int m_nType;
CEdit* m_pEdit;
TCHAR m_szDrive[_MAX_DRIVE];
TCHAR m_szDir[_MAX_DIR];
TCHAR m_szFname[_MAX_FNAME];
TCHAR m_szExt[_MAX_EXT];
CBitmap m_backBmp;
BOOL m_bClearTextKillFocus;
};
ACEdit.cpp
#include "stdafx.h"
#include "ACEdit.h"
#include <io.h> #define _EDIT_ 1
#define _COMBOBOX_ 2 CACEdit::CACEdit() {
m_nMode = _MODE_STANDARD_;
m_nType = -;
m_pEdit = NULL;
m_bCursorMode = false;
m_prefixChar = ;
m_bClearTextKillFocus = FALSE;
} CACEdit::~CACEdit() { } BOOL CACEdit::PreTranslateMessage(MSG* pMsg) {
if(pMsg->message == WM_KEYDOWN) {
if(m_Liste.IsWindowVisible()) {
if(m_nType == _COMBOBOX_) {
if(pMsg->wParam == VK_DOWN || pMsg->wParam == VK_UP) {
if(HandleKey(pMsg->wParam, false)) {
return TRUE;
}
}
}
if(pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN) {
if(HandleKey(pMsg->wParam, false)) {
return TRUE;
}
}
}
}
return FALSE;
} void CACEdit::SetMode(int nMode) {
if(m_nType == -) {
Init();
}
m_nMode = nMode; if(nMode == _MODE_CURSOR_O_LIST_) {
m_nMode |= _MODE_STANDARD_;
} if(nMode & _MODE_FILESYSTEM_) {
m_strSeparator = _T("\\");
} if(nMode & _MODE_FIND_ALL_) {
m_Liste.m_lMode |= _MODE_FIND_ALL_;
SetSeparator(_T(" "));
}
} void CACEdit::SetSeparator(LPCTSTR lpszString,TCHAR lpszPrefixChar) {
m_strSeparator = lpszString;
m_Liste.m_prefixChar = m_prefixChar = lpszPrefixChar;
SetMode(_MODE_SEPARATION_);
} int CACEdit::AddString(LPCTSTR lpszString) {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->AddString(lpszString);
}
return CB_ERR;
} int CACEdit::GetLBText(int nIndex, LPTSTR lpszText) {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->GetLBText(nIndex, lpszText);
}
return CB_ERR;
} void CACEdit::GetLBText(int nIndex, CString& rString) {
if(m_nType == _COMBOBOX_) {
((CComboBox*)this)->GetLBText(nIndex, rString);
}
} int CACEdit::SetDroppedWidth(UINT nWidth) {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->SetDroppedWidth(nWidth);
}
return CB_ERR;
} int CACEdit::FindString(int nStartAfter, LPCTSTR lpszString) {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->FindString(nStartAfter, lpszString);
}
return CB_ERR;
} int CACEdit::SelectString(int nStartAfter, LPCTSTR lpszString) {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->SelectString(nStartAfter, lpszString);
}
return CB_ERR;
} void CACEdit::ShowDropDown(BOOL bShow) {
if(m_nType == _COMBOBOX_) {
((CComboBox*)this)->ShowDropDown(bShow);
}
} void CACEdit::ResetContent() {
if(m_nType == _COMBOBOX_) {
((CComboBox*)this)->ResetContent();
}
} int CACEdit::GetCurSel() {
if(m_nType == _COMBOBOX_) {
return ((CComboBox*)this)->GetCurSel();
}
return CB_ERR;
} void CACEdit::Init() {
// m_backBmp.LoadBitmap(IDB_QUREY);
CMessageLoop* pLoop = _Module.GetMessageLoop();
ATLASSERT(pLoop != NULL);
pLoop->AddMessageFilter(this); WNDCLASS wndcls;
wndcls.style = CS_CLASSDC | CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW;
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = ;
wndcls.hInstance = _Module.GetModuleInstance();
wndcls.hIcon = NULL;
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hbrBackground = (HBRUSH)COLOR_WINDOW;
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = _T("ACListWnd");
RegisterClass(&wndcls); CRect rcWnd;
CRect rcWnd1;
GetWindowRect(rcWnd); HWND hListe = CreateWindowEx(WS_EX_TOOLWINDOW,
_T("ACListWnd"),
NULL,
WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS | WS_OVERLAPPED,
rcWnd.left,
rcWnd.top + rcWnd.Height(),
rcWnd.Width(),
rcWnd.Height(),
GetDesktopWindow(),
NULL,
_Module.GetModuleInstance(),
NULL);
::SetWindowPos(hListe, HWND_TOPMOST, rcWnd.left, rcWnd.top + rcWnd.Height(),
rcWnd.Width(), rcWnd.Height(), NULL);
m_Liste.SubclassWindow(hListe); CString strClassName;
::GetClassName(this->m_hWnd, strClassName.GetBuffer(), );
strClassName.ReleaseBuffer(); if (strClassName.Compare(_T("Edit")) == ) {
m_nType = _EDIT_;
} else {
if (strClassName.Compare(_T("ComboBox")) == ) {
m_nType = _COMBOBOX_;
m_pEdit = (CEdit*)&GetWindow(GW_CHILD);
ATLASSERT(m_pEdit);
::GetClassName(m_pEdit->m_hWnd, strClassName.GetBuffer(), );
strClassName.ReleaseBuffer();
ATLASSERT(strClassName.Compare(_T("Edit")) == );
}
} if(m_nType == -) {
ATLASSERT();
return;
} m_Liste.Init(this);
} void CACEdit::AddSearchString(LPCTSTR lpszString) {
if(m_nType == -) {
ATLASSERT();
return;
}
m_Liste.AddSearchString(lpszString);
} void CACEdit::AddSearchStrings(LPCTSTR lpszStrings[]) {
int i = ;
LPCTSTR str = NULL;
if(m_nType == -) {
ATLASSERT();
return;
}
m_Liste.RemoveAll();
do {
str = lpszStrings[i];
if(str) {
m_Liste.AddSearchString(str);
}
i++;
} while(str);
m_Liste.SortSearchList();
} void CACEdit::RemoveSearchAll() {
if(m_nType == -) {
ATLASSERT();
return;
}
m_Liste.RemoveAll();
} void CACEdit::SetStartDirectory(LPCTSTR lpszString) {
if(m_nType == -) {
ATLASSERT();
return;
}
if(m_nMode & _MODE_FS_START_DIR_) {
ReadDirectory(lpszString);
}
} void CACEdit::SetFontHeight(long lHeight) {
m_Liste.SetFontHeight(lHeight);
} void CACEdit::SetClearKillFocus() {
m_bClearTextKillFocus = TRUE;
} LRESULT CACEdit::OnKillFocus(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled) {
if(m_Liste.m_hWnd) {
m_Liste.ShowWindow(false);
}
if (m_bClearTextKillFocus) {
SetWindowText(_T(""));
Invalidate();
}
return ;
} void CACEdit::OnKeyDown(TCHAR vkey, UINT repeats, UINT code) {
if(!HandleKey(vkey, false)) {
DefWindowProc();
}
} LRESULT CACEdit::OnChange(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled) {
CString strText;
int nPos=; if(m_nType == -) {
ATLASSERT();
return ;
} GetWindowText(m_strEditText);
int nLen = m_strEditText.GetLength(); if(m_nMode & _MODE_FILESYSTEM_ || m_nMode & _MODE_FS_START_DIR_) {
if(!m_bCursorMode) {
CPoint point;
if(m_nType == _EDIT_) {
GetCaretPos(&point);
nPos = LOWORD(((CEdit*)this)->CharFromPos(point));
} if(m_nType == _COMBOBOX_) {
GetCaretPos(&point);
nPos = m_pEdit->CharFromPos(point);
} if(m_nMode & _MODE_FS_START_DIR_) {
if(nLen) {
m_Liste.FindString(-, m_strEditText);
} else {
m_Liste.ShowWindow(false);
}
} else {
if(nLen > && nPos == nLen) {
if(_taccess(m_strEditText, ) == ) {
ReadDirectory(m_strEditText);
}
m_Liste.FindString(-, m_strEditText);
} else {
m_Liste.ShowWindow(false);
}
}
}
} if(m_nMode & _MODE_SEPARATION_) {
if(!m_bCursorMode) {
CPoint point;
if(m_nType == _EDIT_) {
GetCaretPos(&point);
nPos = LOWORD(((CEdit*)this)->CharFromPos(point));
} if(m_nType == _COMBOBOX_) {
GetCaretPos(&point);
nPos = m_pEdit->CharFromPos(point);
} int nLeft = FindSepLeftPos(nPos - );
int nRight = FindSepRightPos(nPos);
strText = m_strEditText.Mid(nLeft, nRight - nLeft);
m_Liste.FindString(-, strText);
}
} if(m_nMode & _MODE_STANDARD_) {
if(!m_bCursorMode) {
m_Liste.FindString(-, m_strEditText);
}
} SendMessage(ENAC_UPDATE, EN_UPDATE, GetDlgCtrlID());
return ;
} LRESULT CACEdit::OnCloseList(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled) {
m_Liste.ShowWindow(false);
return ;
} LRESULT CACEdit::OnEraseBkgnd(HDC hDC) {
CDC dc(hDC);
RECT updatarect;
GetClientRect(&updatarect); CBrush BackBrush = CreatePatternBrush(m_backBmp);
CBrush oldBrush = dc.SelectBrush(BackBrush); CPen penBlack = CreatePen(PS_NULL, , RGB(, , ));
CPen oldPen = dc.SelectPen(penBlack); dc.Rectangle(&updatarect); dc.SelectBrush(oldBrush);
dc.SelectPen(oldPen); return TRUE;
} LRESULT CACEdit::OnUpdateFromList(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
if(lParam == WM_KEYDOWN) {
HandleKey(VK_DOWN, true);
}
return ;
} void CACEdit::OnDestroy() {
m_Liste.SendMessage(WM_CLOSE);
} void CACEdit::ReadDirectory(CString strDir) {
if(strDir.Right() != _T('\\')) {
_tsplitpath_s(strDir, m_szDrive, m_szDir, m_szFname, m_szExt);
strDir.Format(_T("%s%s"), m_szDrive, m_szDir);
}
TCHAR ch = (TCHAR)towupper(strDir.GetAt());
strDir.SetAt(, ch); CString m_Name, m_File, strDir1 = strDir;
if(strDir.Right() != _T('\\')) {
strDir += _T("\\");
}
if(m_strLastDirectory.CompareNoCase(strDir) == && m_Liste.m_searchList.GetSize()) {
return;
}
m_strLastDirectory = strDir;
strDir += _T("*.*"); WIN32_FIND_DATA findData;
HANDLE findHandle = FindFirstFile(strDir, &findData);
if(INVALID_HANDLE_VALUE == findHandle) {
return;
}
while(FindNextFile(findHandle, &findData)) {
m_File = findData.cFileName;
if(findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ||
findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
continue;
}
if(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if(_MODE_ONLY_FILES & m_nMode) {
continue;
}
if(m_File.CompareNoCase(_T(".")) == ||
m_File.CompareNoCase(_T("..")) == ) {
continue;
}
if(m_File.Right() != _T('\\')) {
m_File += _T("\\");
}
} else {
if(_MODE_ONLY_DIRS & m_nMode) {
continue;
}
} if(m_nMode & _MODE_FS_START_DIR_) {
m_Name = m_File;
} else {
m_Name = strDir1;
if(m_Name.Right() != _T('\\')) {
m_Name += _T("\\");
}
m_Name += m_File;
}
AddSearchString(m_Name);
}
FindClose(findHandle);
} int CACEdit::FindSepLeftPos(int nPos, bool bFindSepLeftPos) {
int nLen = m_strEditText.GetLength();
TCHAR ch;
if(nPos >= nLen && nLen != ) {
nPos = nLen -;
} int i = nPos;
for(; i>=; i--) {
ch = m_strEditText.GetAt(i);
if(m_prefixChar == ch) {
return i + (bFindSepLeftPos ? : );
}
if(m_strSeparator.Find(ch) != -) {
break;
}
} return i + ;
} int CACEdit::FindSepLeftPos2(int nPos) {
int nLen = m_strEditText.GetLength();
TCHAR ch; if(nPos >= nLen && nLen != ) {
nPos = nLen - ;
} if(nLen == ) {
return ;
} for(int i=nPos; i>=; i--) {
ch = m_strEditText.GetAt(i);
if(m_prefixChar == ch) {
return ;
}
} return ;
} int CACEdit::FindSepRightPos(int nPos) {
int nLen = m_strEditText.GetLength();
TCHAR ch; int i = nPos;
for(; i<nLen; i++) {
ch = m_strEditText.GetAt(i);
if(m_strSeparator.Find(ch) != -) {
break;
}
}
return i;
} bool CACEdit::HandleKey(UINT nChar, bool bFromChild) {
if (nChar == VK_ESCAPE || nChar == VK_RETURN) {
m_Liste.ShowWindow(false);
return true;
} if (nChar == VK_DOWN || nChar == VK_UP ||
nChar == VK_PRIOR || nChar == VK_NEXT ||
nChar == VK_HOME || nChar == VK_END) {
if(!m_Liste.IsWindowVisible() && (m_nMode & _MODE_CURSOR_O_LIST_)) {
GetWindowText(m_strEditText);
if(m_strEditText.IsEmpty()) {
m_Liste.CopyList();
return true;
}
}
if(m_Liste.IsWindowVisible()) {
int nPos;
if(m_nMode & _MODE_STANDARD_ ||
m_nMode & _MODE_FILESYSTEM_ ||
m_nMode & _MODE_FS_START_DIR_) {
m_bCursorMode = true;
if(!bFromChild) {
m_strEditText = m_Liste.GetNextString(nChar);
} else {
m_strEditText = m_Liste.GetString();
}
if(m_nMode & _MODE_FILESYSTEM_) {
if(m_strEditText.Right() == _T('\\')) {
m_strEditText = m_strEditText.Mid(, m_strEditText.GetLength() - );
}
} m_Liste.SelectItem(-);
SetWindowText(m_strEditText);
nPos = m_strEditText.GetLength(); if(m_nType == _COMBOBOX_) {
m_pEdit->SetSel(nPos, nPos, true);
m_pEdit->SetModify(true);
}
if(m_nType == _EDIT_) {
this->SetSel(, nPos, true);
this->SetModify(true);
} SendMessage(ENAC_UPDATE, WM_KEYDOWN, GetDlgCtrlID());
m_bCursorMode = false;
return true;
} if(m_nMode & _MODE_SEPARATION_) {
CString strText;
CString strLeft;
CString strRight;
int nLeft;
int nRight;
int nPos = ;
int nLen; m_bCursorMode = true;
GetWindowText(m_strEditText);
CPoint point;
if(m_nType == _EDIT_) {
GetCaretPos(&point);
nPos = LOWORD(((CEdit*)this)->CharFromPos(point));
} if(m_nType == _COMBOBOX_) {
GetCaretPos(&point);
nPos = m_pEdit->CharFromPos(point);
} nLeft = FindSepLeftPos(nPos - , true);
nRight = FindSepRightPos(nPos); strText = m_strEditText.Left(nLeft); if(!bFromChild) {
strText += m_Liste.GetNextString(nChar);
} else {
strText += m_Liste.GetString();
}
m_Liste.SelectItem(-);
strText += m_strEditText.Mid(nRight);
nLen = m_Liste.GetString().GetLength(); SetWindowText(strText);
SendMessage(ENAC_UPDATE, WM_KEYDOWN, GetDlgCtrlID()); nRight = FindSepLeftPos2(nPos - );
nLeft -= nRight;
nLen += nRight; if(m_nType == _EDIT_) {
((CEdit*)this)->SetModify(true);
((CEdit*)this)->SetSel(nLeft, nLeft + nLen, false);
} if(m_nType == _COMBOBOX_) {
m_pEdit->SetModify(true);
m_pEdit->SetSel(nLeft, nLeft + nLen, true);
} m_bCursorMode = false;
return true;
}
}
}
return false;
}
ACListWnd.h
#pragma once #define ENAC_UPDATE WM_USER + 1200
#define IDTimerInstall 10
#define _MAX_ENTRYS_ 5 #define _MODE_ONLY_FILES (1L << 16)
#define _MODE_ONLY_DIRS (1L << 17)
#define _MODE_STANDARD_ (1L << 0)
#define _MODE_SEPARATION_ (1L << 1)
#define _MODE_FILESYSTEM_ (1L << 2)
#define _MODE_FS_START_DIR_ (1L << 3)
#define _MODE_CURSOR_O_LIST_ (1L << 4)
#define _MODE_FIND_ALL_ (1L << 5)
#define _MODE_FS_ONLY_FILE_ (_MODE_FILESYSTEM_ | _MODE_ONLY_FILES)
#define _MODE_FS_ONLY_DIR_ (_MODE_FILESYSTEM_ | _MODE_ONLY_DIRS)
#define _MODE_SD_ONLY_FILE_ (_MODE_FS_START_DIR_ | _MODE_ONLY_FILES)
#define _MODE_SD_ONLY_DIR_ (_MODE_FS_START_DIR_ | _MODE_ONLY_DIRS) class CACListWnd : public CWindowImpl<CACListWnd> {
public:
CACListWnd();
virtual ~CACListWnd(); void Init(CWindow* pWindow);
bool EnsureVisible(int nItem, bool bWait);
bool SelectItem(int nItem);
int FindString(int nStartAfter, LPCTSTR lpszString, bool bDisplayOnly = false);
int FindStringExact(int nIndexStart, LPCTSTR lpszFind);
int SelectString(LPCTSTR lpszString);
bool GetText(int nItem, CString& strText);
void AddSearchString(LPCTSTR lpszString);
void RemoveAll();
CString GetString();
CString GetNextString(int nChar);
void CopyList();
void SortSearchList();
void DrawItem(CDC* pDC, long lItem, long lWidth);
void SetFontHeight(long lHeight); CString m_strDisplay;
TCHAR m_prefixChar;
long m_lMode;
long m_lFontHeight;
CSimpleArray<CString> m_searchList; protected:
BEGIN_MSG_MAP_EX(CACListWnd)
MSG_WM_PAINT(OnPaint)
MSG_WM_SIZE(OnSize)
MSG_WM_ERASEBKGND(OnEraseBkgnd)
MSG_WM_NCPAINT(OnNcPaint)
MSG_WM_KEYDOWN(OnKeyDown)
MSG_WM_NCCALCSIZE(OnNcCalcSize)
MSG_WM_VSCROLL(OnVScroll)
MSG_WM_ACTIVATEAPP(OnActivateApp)
MSG_WM_NCHITTEST(OnNcHitTest)
MSG_WM_LBUTTONDOWN(OnLButtonDown)
MSG_WM_RBUTTONDOWN(OnRButtonDown)
MSG_WM_SETCURSOR(OnSetCursor)
MSG_WM_SHOWWINDOW(OnShowWindow)
MSG_WM_NCLBUTTONDOWN(OnNcLButtonDown)
MSG_WM_MOUSEMOVE(OnMouseMove)
MSG_WM_TIMER(OnTimer)
MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
END_MSG_MAP() void OnPaint(HDC hDC);
void OnSize(UINT nType, CSize size);
LRESULT OnEraseBkgnd(HDC hDC);
void OnNcPaint(HRGN hRgn);
void OnKeyDown(TCHAR vkey, UINT repeats, UINT code);
LRESULT OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam);
void OnVScroll(int nSBCode, short nPos, HWND hWnd);
void OnActivateApp(BOOL bActive, DWORD dwThreadID);
LRESULT OnNcHitTest(CPoint point);
void OnLButtonDown(UINT nFlags, CPoint point);
void OnRButtonDown(UINT nFlags, CPoint point);
LRESULT OnSetCursor(HWND hWnd, UINT nHitTest, UINT nMessage);
void OnShowWindow(BOOL bShow, int nStatus);
void OnNcLButtonDown(UINT nHitTest, CPoint point);
void OnMouseMove(UINT nFlags, CPoint point);
void OnTimer(UINT nIDEvent);
void OnGetMinMaxInfo(LPMINMAXINFO lpMMI); int HitTest(CPoint point);
void SetScroller();
void SetProp();
long ScrollBarWidth();
void InvalidateAndScroll();
void SortList(CSimpleArray<CString>& List);
static int CompareString(const void* p1, const void* p2); CScrollBar m_vertBar;
CScrollBar m_horzBar;
CRect m_lastSize;
CRect m_parentRect;
CEdit* m_pEdit;
int m_nIDTimer;
long m_lTopIndex;
long m_lCount;
long m_lItemHeight;
long m_lVisibleItems;
long m_lSelItem;
CSimpleArray<CString> m_displayList;
};
ACListWnd.cpp
#include "stdafx.h"
#include "ACListWnd.h" void DoPaintMessageLoop() {
MSG msg;
while(::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
} CACListWnd::CACListWnd() {
m_lTopIndex = ;
m_lCount = ;
m_lItemHeight = ;
m_lSelItem = -;
m_lVisibleItems = ;
m_pEdit = NULL;
m_lastSize.SetRectEmpty();
m_prefixChar = ;
m_lMode = ;
m_lFontHeight = ;
} CACListWnd::~CACListWnd() {
m_searchList.RemoveAll();
m_displayList.RemoveAll();
} void CACListWnd::Init(CWindow* pWindow) {
ATLASSERT(m_vertBar.Create(this->m_hWnd,
CRect(, , GetSystemMetrics(SM_CYVSCROLL), ),
NULL,
WS_CHILD | WS_VISIBLE|SBS_VERT|SBS_LEFTALIGN,
NULL)); SetScroller();
m_pEdit = (CEdit*)pWindow;
m_lCount = m_displayList.GetSize();
m_vertBar.SetScrollPos(, false);
SetProp(); CDC dc(GetDC());
LOGFONT logFont = {};
CFont((HFONT)GetStockObject(DEFAULT_GUI_FONT)).GetLogFont(&logFont); if (m_lFontHeight > ) {
logFont.lfHeight = m_lFontHeight;
logFont.lfWeight = FW_BOLD;
dc.SelectFont(CreateFontIndirect(&logFont));
} m_lItemHeight = abs(logFont.lfHeight);
} bool CACListWnd::EnsureVisible(int nItem, bool bWait) {
if(nItem > m_lTopIndex && nItem < m_lTopIndex + m_lVisibleItems) {
return false;
} if(nItem > m_lTopIndex) {
int nLen = nItem;
for(int i=m_lTopIndex; i<nLen; i++) {
if(i >= m_lCount - m_lVisibleItems || i + m_lVisibleItems > nItem) {
break;
}
m_lTopIndex++;
if(bWait) {
InvalidateAndScroll();
Sleep();
DoPaintMessageLoop();
}
}
InvalidateAndScroll();
return true;
} if(nItem < m_lTopIndex) {
while(nItem < m_lTopIndex) {
if(m_lTopIndex > ) {
m_lTopIndex--;
} else {
break;
}
if(bWait) {
InvalidateAndScroll();
Sleep();
DoPaintMessageLoop();
}
}
InvalidateAndScroll();
return true;
}
return false;
} bool CACListWnd::SelectItem(int nItem) {
if(nItem > m_lCount) {
return false;
}
if(nItem == -) {
EnsureVisible(m_lSelItem, false);
Invalidate();
return false;
}
m_lSelItem = nItem;
if(!EnsureVisible(nItem,true)) {
Invalidate();
}
return true;
} int CACListWnd::FindString(int nStartAfter, LPCTSTR lpszString, bool bDisplayOnly) {
long lCount = m_displayList.GetSize();
if(!bDisplayOnly) {
CString str1;
CString str2(lpszString);
if(!m_pEdit) {
ShowWindow(false);
return -;
}
if(nStartAfter > m_searchList.GetSize()) {
ShowWindow(false);
return -;
}
if(str2.IsEmpty()) {
ShowWindow(false);
return -;
}
m_displayList.RemoveAll();
str2.MakeUpper();
for(int i=nStartAfter + ; i<m_searchList.GetSize(); i++) {
if(m_prefixChar) {
str1 = m_prefixChar;
} else {
str1 = _T("");
}
str1 += m_searchList[i];
str1.MakeUpper();
if(m_lMode & _MODE_FIND_ALL_) {
if(str1.Find(str2) >= ) {
m_displayList.Add(m_searchList[i]);
}
} else {
if(str1.Find(str2) == ) {
m_displayList.Add(m_searchList[i]);
}
}
}
} m_lCount = m_displayList.GetSize();
if(m_lCount) {
CRect rcWnd;
int nWidth;
m_pEdit->GetWindowRect(rcWnd); SetScroller();
SetProp();
ShowWindow(true);
Invalidate(); int nHeight = m_lCount * m_lItemHeight + * GetSystemMetrics(SM_CYBORDER);
if(m_lCount > _MAX_ENTRYS_) {
nHeight = _MAX_ENTRYS_ * m_lItemHeight + * GetSystemMetrics(SM_CYBORDER);
} if(!m_lastSize.IsRectEmpty()) {
nWidth = m_lastSize.Width();
nHeight = m_lastSize.Height();
rcWnd.top += rcWnd.Height();
rcWnd.right = rcWnd.left + nWidth;
rcWnd.bottom = rcWnd.top + nHeight;
MoveWindow(rcWnd.left, rcWnd.top, rcWnd.Width(), rcWnd.Height());
} else {
MoveWindow(rcWnd.left, rcWnd.top + rcWnd.Height(), rcWnd.Width(), nHeight);
} if(lCount != m_displayList.GetSize()) {
m_lSelItem = -;
}
SortList(m_displayList);
} else {
ShowWindow(false);
}
return ;
} int CACListWnd::FindStringExact(int nIndexStart, LPCTSTR lpszFind) {
if(nIndexStart > m_searchList.GetSize()) {
return -;
}
for(int i=nIndexStart + ; i<m_searchList.GetSize(); i++) {
if(m_searchList[i].Compare(lpszFind) == ) {
return i;
}
}
return -;
} int CACListWnd::SelectString(LPCTSTR lpszString) {
int nItem = FindString(-, lpszString);
SelectItem(nItem);
return nItem;
} bool CACListWnd::GetText(int nItem, CString& strText) {
if(nItem < || nItem > m_searchList.GetSize()) {
return false;
}
strText = m_searchList[nItem];
return true;
} void CACListWnd::AddSearchString(LPCTSTR lpszString) {
m_searchList.Add(lpszString);
} void CACListWnd::RemoveAll() {
m_searchList.RemoveAll();
m_displayList.RemoveAll();
} CString CACListWnd::GetString() {
int i = m_displayList.GetSize();
if(i == ) {
return _T("");
}
if(i <= m_lSelItem || m_lSelItem == -) {
i = ;
} else {
i = m_lSelItem;
}
return m_displayList[i];
} CString CACListWnd::GetNextString(int nChar) {
switch(nChar) {
case VK_DOWN:
m_lSelItem++;
break;
case VK_UP:
m_lSelItem--;
break;
case VK_PRIOR:
m_lSelItem -= m_lVisibleItems;
if(m_lSelItem < ) {
m_lSelItem = ;
}
break;
case VK_NEXT:
m_lSelItem += m_lVisibleItems;
if(m_lSelItem >= m_lCount - ) {
m_lSelItem = m_lCount - ;
}
break;
case VK_HOME:
m_lSelItem = ;
break;
case VK_END:
m_lSelItem = m_lCount - ;
break;
} if(m_lSelItem < ) {
m_lSelItem = m_lCount - ;
}
if(m_lSelItem >= m_lCount) {
m_lSelItem = ;
}
if(EnsureVisible(m_lSelItem, m_lCount > ? false : true)) {
InvalidateAndScroll();
}
return GetString();
} void CACListWnd::CopyList() {
m_displayList = m_searchList;
m_lCount = m_displayList.GetSize();
if(m_lCount) {
FindString(, _T(""), true);
}
} void CACListWnd::SortSearchList() {
SortList(m_searchList);
} void CACListWnd::DrawItem(CDC* pDC, long lItem, long lWidth) {
long y = lItem - m_lTopIndex;
CRect rcLabel(, y * m_lItemHeight, lWidth, (y + ) * m_lItemHeight);
pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
if(lItem == m_lSelItem) {
rcLabel.left = ;
pDC->FillSolidRect(rcLabel, ::GetSysColor(COLOR_HIGHLIGHT));
pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
rcLabel.left = ;
}
if(m_prefixChar) {
m_strDisplay = m_prefixChar + m_displayList[lItem];
} else {
m_strDisplay = m_displayList[lItem];
}
pDC->DrawText(m_strDisplay, -, rcLabel, DT_LEFT | DT_SINGLELINE |
DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);
} void CACListWnd::SetFontHeight(long lHeight) {
m_lFontHeight = lHeight;
} void CACListWnd::OnPaint(HDC hDC) {
CRect rcWnd;
CRect rect;
CRect rc;
CDC* pDC = NULL; GetClientRect(rc);
rcWnd = rect = rc; rc.left = rc.right - GetSystemMetrics(SM_CXHSCROLL);
rc.top = rc.bottom - GetSystemMetrics(SM_CYVSCROLL); rect.right -= ScrollBarWidth(); CPaintDC dc(this->m_hWnd);
CDC MemDC = CreateCompatibleDC(dc); CBitmap bitmap = CreateCompatibleBitmap(dc, rect.Width(), rect.Height());
CBitmap oldBitmap = MemDC.SelectBitmap(bitmap); MemDC.SetWindowOrg(rect.left, rect.top); long lWidth = rcWnd.Width() - ScrollBarWidth(); MemDC.FillSolidRect(rcWnd, ::GetSysColor(COLOR_WINDOW)); int i = ;
if (m_lFontHeight <= ) {
MemDC.SelectFont((HFONT)GetStockObject(DEFAULT_GUI_FONT));
MemDC.SetBkMode(TRANSPARENT);
for(i=m_lTopIndex; i<m_lCount; i++) {
DrawItem(&MemDC, i, lWidth);
}
} else {
LOGFONT logFont = {};
CFont((HFONT)GetStockObject(DEFAULT_GUI_FONT)).GetLogFont(&logFont);
logFont.lfHeight = m_lFontHeight;
logFont.lfWeight = FW_BOLD;
logFont.lfItalic = TRUE;
CFont font = CreateFontIndirect(&logFont);
CFont oldFont = MemDC.SelectFont(font);
MemDC.SetBkMode(TRANSPARENT);
for(i=m_lTopIndex; i<m_lCount; i++) {
DrawItem(&MemDC, i, lWidth);
}
MemDC.SelectFont(oldFont);
} CPen pen1 = CreatePen(PS_SOLID, , ::GetSysColor(COLOR_WINDOW));
CPen pen2 = CreatePen(PS_SOLID, , ::GetSysColor(COLOR_BTNFACE));
CPen pen3 = CreatePen(PS_SOLID, , ::GetSysColor(COLOR_3DSHADOW)); pDC = &dc;
if(m_vertBar.IsWindowVisible()) {
dc.FillSolidRect(rc, ::GetSysColor(COLOR_BTNFACE));
} else {
pDC = &MemDC;
} CPen oldPen = pDC->SelectPen(pen1);
int nBottom= rcWnd.bottom - GetSystemMetrics(SM_CXHSCROLL) - ;
lWidth = GetSystemMetrics(SM_CXHSCROLL); int a = ;
for(i=; i<; i++, a++) {
if(a == ) {
pDC->SelectPen(pen1);
} else if(a == ) {
pDC->SelectPen(pen2);
} else if(a == ) {
pDC->SelectPen(pen3);
} else {
a = ;
}
pDC->MoveTo(rc.left + i - , rcWnd.bottom);
pDC->LineTo(rc.left + i + lWidth, nBottom);
}
dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), MemDC,
rect.left, rect.top, SRCCOPY);
pDC->SelectPen(oldPen);
MemDC.SelectBitmap(oldBitmap);
} void CACListWnd::OnSize(UINT nType, CSize size) {
SetScroller();
SetProp();
if(!m_lastSize.IsRectEmpty()) {
GetWindowRect(m_lastSize);
}
} LRESULT CACListWnd::OnEraseBkgnd(HDC hDC) {
return ;
} void CACListWnd::OnNcPaint(HRGN hRgn) {
CWindowDC dc(this->m_hWnd);
CRect rectClient;
CRect rectWindow;
CRect rcWnd;
GetClientRect(rectClient);
GetWindowRect(rectWindow);
ScreenToClient(rectWindow); rectClient.OffsetRect(-(rectWindow.left), -(rectWindow.top));
dc.ExcludeClipRect(rectClient); rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top); dc.FillSolidRect(rectWindow, ::GetSysColor(COLOR_WINDOWTEXT));
} void CACListWnd::OnKeyDown(TCHAR vkey, UINT repeats, UINT code) {
if (vkey == VK_ESCAPE) {
ShowWindow(false);
}
} LRESULT CACListWnd::OnNcCalcSize(BOOL bCalcValidRects, LPARAM lParam) {
NCCALCSIZE_PARAMS* lpncsp = (NCCALCSIZE_PARAMS*)lParam;
return ::InflateRect(lpncsp->rgrc, -GetSystemMetrics(SM_CXBORDER),
-GetSystemMetrics(SM_CYBORDER));
} void CACListWnd::OnVScroll(int nSBCode, short nPos, HWND hWnd) {
long lOldTopIndex = m_lTopIndex;
switch(nSBCode) {
case SB_ENDSCROLL:
break;
case SB_PAGEUP:
m_lTopIndex -= m_lVisibleItems;
if(m_lTopIndex < ) {
m_lTopIndex = ;
}
break;
case SB_PAGEDOWN:
m_lTopIndex += m_lVisibleItems;
if(m_lTopIndex >= m_lCount - m_lVisibleItems) {
m_lTopIndex = m_lCount-m_lVisibleItems;
}
break;
case SB_LINEUP:
m_lTopIndex--;
if(m_lTopIndex < ) {
m_lTopIndex = ;
}
break;
case SB_LINEDOWN:
m_lTopIndex++;
if(m_lTopIndex >= m_lCount - m_lVisibleItems) {
m_lTopIndex = m_lCount - m_lVisibleItems;
}
break;
case SB_THUMBTRACK:
m_lTopIndex = nPos;
break;
} m_vertBar.SetScrollPos(m_lTopIndex, true); if(lOldTopIndex != m_lTopIndex) {
Invalidate();
}
} void CACListWnd::OnActivateApp(BOOL bActive, DWORD dwThreadID) {
ShowWindow(false);
} LRESULT CACListWnd::OnNcHitTest(CPoint point) {
CRect rectClient;
GetWindowRect(rectClient);
rectClient.left = rectClient.right - GetSystemMetrics(SM_CYVSCROLL);
rectClient.top = rectClient.bottom - GetSystemMetrics(SM_CXVSCROLL);
if(rectClient.PtInRect(point)) {
return HTBOTTOMRIGHT;
} else {
return HTCLIENT;
}
} void CACListWnd::OnLButtonDown(UINT nFlags, CPoint point) {
int nSel = HitTest(point);
if(nSel >= ) {
if(!EnsureVisible(nSel, true)) {
Invalidate();
}
m_lSelItem = nSel;
m_pEdit->SendMessage(ENAC_UPDATE, GetDlgCtrlID(), WM_KEYDOWN);
DoPaintMessageLoop();
Sleep();
ShowWindow(false);
} else {
CRect rect;
GetClientRect(rect);
if(!rect.PtInRect(point)) {
ShowWindow(false);
}
}
} void CACListWnd::OnRButtonDown(UINT nFlags, CPoint point) {
ShowWindow(false);
} LRESULT CACListWnd::OnSetCursor(HWND hWnd, UINT nHitTest, UINT nMessage) {
CRect rectClient;
GetWindowRect(rectClient);
ScreenToClient(&rectClient); rectClient.left = rectClient.right - GetSystemMetrics(SM_CYVSCROLL);
rectClient.top = rectClient.bottom - GetSystemMetrics(SM_CXVSCROLL); CPoint ptCursor;
GetCursorPos(&ptCursor);
ScreenToClient(&ptCursor); SetCursor(LoadCursor(NULL, IDC_ARROW));
return TRUE;
} void CACListWnd::OnShowWindow(BOOL bShow, int nStatus) {
if(bShow) {
m_nIDTimer = SetTimer(IDTimerInstall, , NULL);
m_pEdit->GetParent().GetWindowRect(m_parentRect);
} else {
if(m_nIDTimer) {
KillTimer(IDTimerInstall);
}
m_nIDTimer = ;
m_lSelItem = -;
m_lTopIndex = ;
}
} void CACListWnd::OnNcLButtonDown(UINT nHitTest, CPoint point) {
if(OnNcHitTest(point) == HTBOTTOMRIGHT) {
GetWindowRect(m_lastSize);
}
} void CACListWnd::OnMouseMove(UINT nFlags, CPoint point) {
int nSel = HitTest(point);
if(nSel >= ) {
Invalidate();
}
} void CACListWnd::OnTimer(UINT nIDEvent) {
CRect parentRect;
switch(nIDEvent) {
case IDTimerInstall:
m_pEdit->GetParent().GetWindowRect(parentRect);
if(!parentRect.EqualRect(m_parentRect)) {
ShowWindow(false);
}
break;
default:
break;
}
} void CACListWnd::OnGetMinMaxInfo(LPMINMAXINFO lpMMI) {
if(this->m_hWnd) {
long lMinY1 = * GetSystemMetrics(SM_CYHSCROLL) + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CXHTHUMB);
long lMinY2 = m_lCount * m_lItemHeight + * GetSystemMetrics(SM_CYBORDER);
if(m_lVisibleItems > m_lCount - && lMinY2 < lMinY1) {
lpMMI->ptMinTrackSize.y = lMinY2;
} else {
lpMMI->ptMinTrackSize.y = lMinY1;
} lpMMI->ptMinTrackSize.x = * GetSystemMetrics(SM_CXHSCROLL); if(m_pEdit != NULL) {
RECT rect;
m_pEdit->GetWindowRect(&rect);
lpMMI->ptMinTrackSize.x = rect.right - rect.left;
}
}
} int CACListWnd::HitTest(CPoint point) {
CRect rcItem;
CRect rcWnd; GetClientRect(rcWnd);
long lWidth = rcWnd.Width() - ScrollBarWidth(); for(int i=m_lTopIndex; i<m_lCount; i++) {
long y = i - m_lTopIndex;
rcItem.SetRect(, y * m_lItemHeight, lWidth, (y + ) * m_lItemHeight);
if(PtInRect(&rcItem, point)) {
return m_lSelItem = (y + m_lTopIndex);
}
}
return -;
} void CACListWnd::SetScroller() {
CRect rcWnd;
CRect rcBar;
GetClientRect(rcWnd);
if(m_vertBar.m_hWnd) {
rcBar = rcWnd;
rcBar.top = -;
rcBar.left = rcWnd.Width() - GetSystemMetrics(SM_CYVSCROLL);
rcBar.bottom -= GetSystemMetrics(SM_CYHSCROLL);
m_vertBar.MoveWindow(rcBar);
rcBar.top = rcWnd.bottom - ;
rcBar.bottom = rcWnd.bottom;
m_vertBar.SetScrollPos(m_lTopIndex, true);
}
} void CACListWnd::SetProp() {
CRect rcWnd;
CRect rcBar; if(!m_lCount) {
return;
} GetWindowRect(rcWnd);
ScreenToClient(rcWnd); SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE | SIF_RANGE;
si.nMin = ;
si.nMax = m_lCount - ;
m_lVisibleItems = si.nPage = rcWnd.Height() / m_lItemHeight;
si.nTrackPos = ;
m_vertBar.SetScrollRange(, m_lCount - );
m_vertBar.SetScrollInfo(&si);
if(m_lVisibleItems > m_lCount - ) {
m_vertBar.ShowWindow(false);
} else {
m_vertBar.ShowWindow(true);
} if(m_lTopIndex + m_lVisibleItems > m_lCount) {
m_lTopIndex = m_lCount - m_lVisibleItems;
if(m_lTopIndex < ) {
m_lTopIndex = ;
}
m_vertBar.SetScrollPos(m_lTopIndex, true);
}
} long CACListWnd::ScrollBarWidth() {
if(m_vertBar.IsWindowVisible()) {
return GetSystemMetrics(SM_CYVSCROLL);
} else {
return ;
}
} void CACListWnd::InvalidateAndScroll() {
m_vertBar.SetScrollPos(m_lTopIndex, true);
Invalidate();
DoPaintMessageLoop();
} void CACListWnd::SortList(CSimpleArray<CString>& List) {
int nCount = List.GetSize();
if (nCount > ) {
CSimpleArray<CString> List2 = List;
LPCTSTR* ppSortArray = new LPCTSTR[nCount + ];
int i=;
for(; i<nCount; i++) {
ppSortArray[i] = (LPCTSTR)List2[i];
}
List.RemoveAll();
qsort(ppSortArray, nCount, sizeof(LPCTSTR), CompareString); for(i=; i<nCount; i++) {
List.Add((LPCTSTR)ppSortArray[i]);
}
List2.RemoveAll();
delete[] ppSortArray;
}
} int CACListWnd::CompareString(const void* p1, const void* p2) {
return _stricmp(*(char**)p1, *(char**)p2);
}