WTL版本ACEdit控件,改写自MFC版,附带源码

  自动完成是个很酷也很实用的功能,比如在浏览器地址栏输入几个字母,相关的记录就会在下拉框中陈列出来。

  最近在做公司产品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 = 0);
    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 = -1;
    m_pEdit = NULL;
    m_bCursorMode = false;
    m_prefixChar = 0;
    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 == -1) {
        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 = 0;
    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(32), 32);
    strClassName.ReleaseBuffer();

    if (strClassName.Compare(_T("Edit")) == 0) {
        m_nType = _EDIT_;
    } else {
        if (strClassName.Compare(_T("ComboBox")) == 0) {
            m_nType = _COMBOBOX_;
            m_pEdit = (CEdit*)&GetWindow(GW_CHILD);
            ATLASSERT(m_pEdit);
            ::GetClassName(m_pEdit->m_hWnd, strClassName.GetBuffer(32), 32);
            strClassName.ReleaseBuffer();
            ATLASSERT(strClassName.Compare(_T("Edit")) == 0);
        }
    }

    if(m_nType == -1) {
        ATLASSERT(0);
        return;
    }

    m_Liste.Init(this);
}

void CACEdit::AddSearchString(LPCTSTR lpszString) {
    if(m_nType == -1) {
        ATLASSERT(0); 
        return;
    } 
    m_Liste.AddSearchString(lpszString);
}

void CACEdit::AddSearchStrings(LPCTSTR lpszStrings[]) {
    int i = 0;
    LPCTSTR str = NULL;
    if(m_nType == -1) {
        ATLASSERT(0); 
        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 == -1) {
        ATLASSERT(0); 
        return;
    }
    m_Liste.RemoveAll();
}

void CACEdit::SetStartDirectory(LPCTSTR lpszString) {
    if(m_nType == -1) {
        ATLASSERT(0); 
        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 0;
}

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=0;

    if(m_nType == -1) {
        ATLASSERT(0); 
        return 0;
    }

    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(-1, m_strEditText);
                } else {
                    m_Liste.ShowWindow(false);
                }
            } else {
                if(nLen > 2 && nPos == nLen) {            
                    if(_taccess(m_strEditText, 0) == 0) {
                        ReadDirectory(m_strEditText);
                    }
                    m_Liste.FindString(-1, 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 - 1);
            int nRight = FindSepRightPos(nPos);
            strText = m_strEditText.Mid(nLeft, nRight - nLeft);
            m_Liste.FindString(-1, strText);
        }
    }

    if(m_nMode & _MODE_STANDARD_) {
        if(!m_bCursorMode) {
            m_Liste.FindString(-1, m_strEditText);
        }
    }

    SendMessage(ENAC_UPDATE, EN_UPDATE, GetDlgCtrlID());
    return 0;
}

LRESULT CACEdit::OnCloseList(UINT wNotifyCode, UINT wID, HWND hWndCtl, BOOL& bHandled) {
    m_Liste.ShowWindow(false);
    return 0;
}

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, 1, RGB(0, 0, 0));
    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 0;
}

void CACEdit::OnDestroy() {
    m_Liste.SendMessage(WM_CLOSE);
}

void CACEdit::ReadDirectory(CString strDir) {
    if(strDir.Right(1) != _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(0));
    strDir.SetAt(0, ch);

    CString m_Name, m_File, strDir1 = strDir;
    if(strDir.Right(1) != _T('\\')) {
        strDir += _T("\\");
    }
    if(m_strLastDirectory.CompareNoCase(strDir) == 0 && 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(".")) == 0 ||
                m_File.CompareNoCase(_T("..")) == 0) {
                    continue;
            }
            if(m_File.Right(1) != _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(1) != _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 != 1) {
        nPos =  nLen -1;
    }

    int i = nPos;
    for(; i>=0; i--) {
        ch = m_strEditText.GetAt(i);
        if(m_prefixChar == ch) {
            return i + (bFindSepLeftPos ? 1 : 0);
        }
        if(m_strSeparator.Find(ch) != -1) {
            break;
        }
    }

    return  i + 1;
}

int CACEdit::FindSepLeftPos2(int nPos) {
    int nLen = m_strEditText.GetLength(); 
    TCHAR ch;

    if(nPos >= nLen && nLen != 1) {
        nPos =  nLen - 1;
    }

    if(nLen == 1) {
        return 0;
    }

    for(int i=nPos; i>=0; i--) {
        ch = m_strEditText.GetAt(i);
        if(m_prefixChar == ch) {
            return 1;
        }
    }

    return  0;
}

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) != -1) {
            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(1) == _T('\\')) {
                        m_strEditText = m_strEditText.Mid(0, m_strEditText.GetLength() - 1);
                    }
                }

                m_Liste.SelectItem(-1);
                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(0, 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 = 0;
                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 - 1, true);
                nRight = FindSepRightPos(nPos);

                strText = m_strEditText.Left(nLeft);

                if(!bFromChild) {
                    strText += m_Liste.GetNextString(nChar);
                } else {
                    strText += m_Liste.GetString();
                }
                m_Liste.SelectItem(-1);
                strText += m_strEditText.Mid(nRight);
                nLen = m_Liste.GetString().GetLength();

                SetWindowText(strText);
                SendMessage(ENAC_UPDATE, WM_KEYDOWN, GetDlgCtrlID());
                
                nRight = FindSepLeftPos2(nPos - 1);
                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 = 0;
    m_lCount = 0;
    m_lItemHeight = 16;
    m_lSelItem = -1;
    m_lVisibleItems = 0;
    m_pEdit = NULL;
    m_lastSize.SetRectEmpty();
    m_prefixChar = 0;
    m_lMode = 0;
    m_lFontHeight = 0;
}

CACListWnd::~CACListWnd() {
    m_searchList.RemoveAll();
    m_displayList.RemoveAll();
}

void CACListWnd::Init(CWindow* pWindow) {
    ATLASSERT(m_vertBar.Create(this->m_hWnd, 
                               CRect(0, 0, GetSystemMetrics(SM_CYVSCROLL), 100),
                               NULL, 
                               WS_CHILD | WS_VISIBLE|SBS_VERT|SBS_LEFTALIGN,
                               NULL));

    SetScroller();
    m_pEdit = (CEdit*)pWindow;
    m_lCount = m_displayList.GetSize();
    m_vertBar.SetScrollPos(0, false);
    SetProp();

    CDC dc(GetDC());
    LOGFONT logFont = {0};
    CFont((HFONT)GetStockObject(DEFAULT_GUI_FONT)).GetLogFont(&logFont);

    if (m_lFontHeight > 0) {
        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(10);
                DoPaintMessageLoop();
            }
        }
        InvalidateAndScroll();
        return true;
    }

    if(nItem < m_lTopIndex) {
        while(nItem < m_lTopIndex) {
            if(m_lTopIndex > 0) {
                m_lTopIndex--;
            } else {
                break;
            }
            if(bWait) {
                InvalidateAndScroll();
                Sleep(10);
                DoPaintMessageLoop();
            }
        }
        InvalidateAndScroll();
        return true;
    }
    return false;
}

bool CACListWnd::SelectItem(int nItem) {
    if(nItem > m_lCount) {
        return false;
    }
    if(nItem == -1) {
        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 -1;
        }
        if(nStartAfter > m_searchList.GetSize()) {
            ShowWindow(false);
            return -1;
        }
        if(str2.IsEmpty()) {
            ShowWindow(false);
            return -1;
        }
        m_displayList.RemoveAll();
        str2.MakeUpper();
        for(int i=nStartAfter + 1; 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) >= 0) {
                    m_displayList.Add(m_searchList[i]);
                }
            } else {
                if(str1.Find(str2) == 0) {
                    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 + 2 * GetSystemMetrics(SM_CYBORDER);
        if(m_lCount > _MAX_ENTRYS_) {
            nHeight = _MAX_ENTRYS_ * m_lItemHeight + 2 * 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 = -1;
        }
        SortList(m_displayList);
    } else {
        ShowWindow(false);
    }
    return 1;
}

int CACListWnd::FindStringExact(int nIndexStart, LPCTSTR lpszFind) {
    if(nIndexStart > m_searchList.GetSize()) {
        return -1;
    }
    for(int i=nIndexStart + 1; i<m_searchList.GetSize(); i++) {
        if(m_searchList[i].Compare(lpszFind) == 0) {
            return i;
        }
    }
    return -1;
}

int CACListWnd::SelectString(LPCTSTR lpszString) {
    int nItem = FindString(-1, lpszString);
    SelectItem(nItem);
    return nItem;
}

bool CACListWnd::GetText(int nItem, CString& strText) {
    if(nItem < 0 || 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 == 0) {
        return _T("");
    }
    if(i <= m_lSelItem || m_lSelItem == -1) {
        i = 0;
    } 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 < 0) {
            m_lSelItem = 0;
        }
        break;
    case VK_NEXT:
        m_lSelItem += m_lVisibleItems;
        if(m_lSelItem >= m_lCount - 1) {
            m_lSelItem = m_lCount - 1;
        }
        break;
    case VK_HOME:
        m_lSelItem = 0;
        break;
    case VK_END:
        m_lSelItem = m_lCount - 1;
        break;
    }

    if(m_lSelItem < 0) {
        m_lSelItem = m_lCount - 1;
    }
    if(m_lSelItem >= m_lCount) {
        m_lSelItem = 0;
    }
    if(EnsureVisible(m_lSelItem, m_lCount > 50 ? false : true)) {
        InvalidateAndScroll();
    }
    return GetString();
}

void CACListWnd::CopyList() {
    m_displayList = m_searchList;
    m_lCount = m_displayList.GetSize();
    if(m_lCount) {
        FindString(0, _T(""), true);
    }
}

void CACListWnd::SortSearchList() {
    SortList(m_searchList);
}

void CACListWnd::DrawItem(CDC* pDC, long lItem, long lWidth) {
    long y = lItem - m_lTopIndex;
    CRect rcLabel(2, y * m_lItemHeight, lWidth, (y + 1) * m_lItemHeight);
    pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
    if(lItem == m_lSelItem) {
        rcLabel.left = 0;
        pDC->FillSolidRect(rcLabel, ::GetSysColor(COLOR_HIGHLIGHT));
        pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
        rcLabel.left = 2;
    }
    if(m_prefixChar) {
        m_strDisplay = m_prefixChar + m_displayList[lItem];
    } else {
        m_strDisplay = m_displayList[lItem];
    }
    pDC->DrawText(m_strDisplay, -1, 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 = 0;
    if (m_lFontHeight <= 0) {
        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 = {0};
        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, 1, ::GetSysColor(COLOR_WINDOW));
    CPen pen2 = CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_BTNFACE));
    CPen pen3 = CreatePen(PS_SOLID, 1, ::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) - 1;
    lWidth = GetSystemMetrics(SM_CXHSCROLL);

    int a = 1;
    for(i=0; i<20; i++, a++) {
        if(a == 1) {
            pDC->SelectPen(pen1);
        } else if(a == 2) {
            pDC->SelectPen(pen2);
        } else if(a == 3) {
            pDC->SelectPen(pen3);
        } else {
            a = 0;
        }
        pDC->MoveTo(rc.left + i - 1, 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 0;
}

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 < 0) {
            m_lTopIndex = 0;
        }
        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 < 0) {
            m_lTopIndex = 0;
        }
        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 >= 0) {
        if(!EnsureVisible(nSel, true)) {
            Invalidate();
        }
        m_lSelItem = nSel;
        m_pEdit->SendMessage(ENAC_UPDATE, GetDlgCtrlID(), WM_KEYDOWN);
        DoPaintMessageLoop();
        Sleep(100);
        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, 200, NULL);
        m_pEdit->GetParent().GetWindowRect(m_parentRect);
    } else {
        if(m_nIDTimer) {
            KillTimer(IDTimerInstall);
        }
        m_nIDTimer = 0;
        m_lSelItem = -1;
        m_lTopIndex = 0;
    }
}

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 >= 0) {
        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 = 2 * GetSystemMetrics(SM_CYHSCROLL) + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CXHTHUMB);
        long lMinY2 = m_lCount * m_lItemHeight + 2 * GetSystemMetrics(SM_CYBORDER);
        if(m_lVisibleItems > m_lCount - 1 &&  lMinY2 < lMinY1) {
            lpMMI->ptMinTrackSize.y = lMinY2;
        } else {
            lpMMI->ptMinTrackSize.y = lMinY1;
        }

        lpMMI->ptMinTrackSize.x = 4 * 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(2, y * m_lItemHeight, lWidth, (y + 1) * m_lItemHeight);
        if(PtInRect(&rcItem, point)) {
            return m_lSelItem = (y + m_lTopIndex);
        }
    }
    return -1;
}

void CACListWnd::SetScroller() {
    CRect rcWnd;
    CRect rcBar;
    GetClientRect(rcWnd);
    if(m_vertBar.m_hWnd) {
        rcBar = rcWnd;
        rcBar.top = -1;
        rcBar.left = rcWnd.Width() - GetSystemMetrics(SM_CYVSCROLL);
        rcBar.bottom -= GetSystemMetrics(SM_CYHSCROLL);
        m_vertBar.MoveWindow(rcBar);
        rcBar.top = rcWnd.bottom - 20;
        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 = 0;
    si.nMax =  m_lCount - 1;
    m_lVisibleItems = si.nPage = rcWnd.Height() / m_lItemHeight;
    si.nTrackPos = 2;
    m_vertBar.SetScrollRange(0, m_lCount - 1);
    m_vertBar.SetScrollInfo(&si);
    if(m_lVisibleItems > m_lCount - 1) {
        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 < 0) {
            m_lTopIndex = 0;
        }
        m_vertBar.SetScrollPos(m_lTopIndex, true);
    }
}

long CACListWnd::ScrollBarWidth() {
    if(m_vertBar.IsWindowVisible()) {
        return GetSystemMetrics(SM_CYVSCROLL);
    } else {
        return 0;
    }
}

void CACListWnd::InvalidateAndScroll() {
    m_vertBar.SetScrollPos(m_lTopIndex, true);
    Invalidate();
    DoPaintMessageLoop();
}

void CACListWnd::SortList(CSimpleArray<CString>& List) {
    int nCount = List.GetSize();    
    if (nCount > 1) {
        CSimpleArray<CString> List2 = List;
        LPCTSTR* ppSortArray = new LPCTSTR[nCount + 1];
        int i=0;
        for(; i<nCount; i++) {
            ppSortArray[i] = (LPCTSTR)List2[i];
        }        
        List.RemoveAll();
        qsort(ppSortArray, nCount, sizeof(LPCTSTR), CompareString);

        for(i=0; 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);
}

 

posted @ 2014-02-24 16:17  琼'  阅读(1874)  评论(1编辑  收藏  举报