autocombobox.cpp
来自「管理项目进度工具的原代码」· C++ 代码 · 共 474 行
CPP
474 行
// autocombobox.cpp : implementation file//#include "stdafx.h"#include "autocombobox.h"#include "holdredraw.h"#include "dialoghelper.h"
#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/////////////////////////////////////////////////////////////////////////////void AFXAPI DDX_AutoCBString(CDataExchange* pDX, int nIDC, CString& value){ HWND hWndCtrl = pDX->PrepareCtrl(nIDC); if (pDX->m_bSaveAndValidate) { ::DDX_CBString(pDX, nIDC, value); } else { // try exact first int nIndex = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)(LPCTSTR)value); // then partial if (nIndex == CB_ERR)
{
nIndex = ::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)value);
// if there's a match, check its not partial
if (nIndex != CB_ERR)
{
CString sItem;
int nLen = ::SendMessage(hWndCtrl, CB_GETLBTEXTLEN, nIndex, 0);
::SendMessage(hWndCtrl, CB_GETLBTEXT, nIndex, (LPARAM)(LPCTSTR)sItem.GetBuffer(nLen + 1));
sItem.ReleaseBuffer();
if (value.CompareNoCase(sItem))
nIndex = CB_ERR;
}
} if (nIndex == CB_ERR) { if (value.IsEmpty())
::SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)-1, 0L);
else
::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)value);
} else { // select it SendMessage(hWndCtrl, CB_SETCURSEL, nIndex, 0L); } }}/////////////////////////////////////////////////////////////////////////////// CAutoComboBoxCAutoComboBox::CAutoComboBox(BOOL bAllowDelete, BOOL bCaseSensitive) :
m_bAllowDelete(bAllowDelete), m_hwndListbox(NULL),
m_bCaseSensitive(bCaseSensitive)
{}CAutoComboBox::~CAutoComboBox(){}BEGIN_MESSAGE_MAP(CAutoComboBox, CComboBox) //{{AFX_MSG_MAP(CAutoComboBox) ON_WM_SIZE()
ON_WM_CTLCOLOR()
//}}AFX_MSG_MAP
// ON_WM_KILLFOCUS()
// ON_CONTROL_REFLECT_EX(CBN_KILLFOCUS, OnKillfocus)
ON_CONTROL_REFLECT_EX(CBN_SELENDOK, OnSelEndOK)
ON_CONTROL_REFLECT_EX(CBN_SELCHANGE, OnSelChange)
ON_CONTROL_REFLECT_EX(CBN_CLOSEUP, OnCloseUp)
END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CAutoComboBox message handlers
BOOL CAutoComboBox::OnCloseUp()
{
// notify parent of (possible) selection change
NotifyParent(CBN_SELCHANGE);
return FALSE; // pass to parent
}
BOOL CAutoComboBox::OnSelEndOK()
{
OnSelChange();
HandleReturnKey();
return FALSE; // continue routing
}
BOOL CAutoComboBox::OnSelChange()
{
// make sure the edit control is up to date
if (IsHooked())
SetWindowText(GetSelectedItem());
// eat notification if dropped down
// ie we don't send selection notifications until the dropdown closes
return GetDroppedState();
}
CString CAutoComboBox::GetSelectedItem() const
{
CString sSel;
int nSel = GetCurSel();
if (nSel != CB_ERR)
GetLBText(nSel, sSel);
return sSel;
}
/*
BOOL CAutoComboBox::OnKillfocus()
{
HandleReturnKey();
return FALSE; // continue routing
}
*/
int CAutoComboBox::FindStringExact(int nIndexStart, const CString& sItem, BOOL bCaseSensitive) const{ int nFind = nIndexStart; // default if (!sItem.IsEmpty()) { // because more than one item might exist if were doing a case-sensitive
// search we can't just stop if the first find doesn't exactly match
// because there still may be further matches
BOOL bContinue = TRUE;
while (bContinue)
{ int nPrevFind = nFind;
nFind = CComboBox::FindStringExact(nFind, sItem);
// if no match then definitely done
if (nFind <= nPrevFind)
{
nFind = -1;
bContinue = FALSE;
}
else if (!bCaseSensitive)
bContinue = FALSE;
else
{
// else if (bCaseSensitive)
ASSERT (nFind != CB_ERR);
ASSERT (bCaseSensitive);
// test for real exactness because FindStringExact is not case sensitive CString sFind; GetLBText(nFind, sFind); bContinue = !(sItem == sFind); // differ in case
} } }
else // special case: look for empty item
{
nFind = GetCount();
while (nFind--)
{
CString sLBItem;
GetLBText(nFind, sLBItem);
if (sLBItem.IsEmpty())
break;
}
} return nFind;}int CAutoComboBox::AddUniqueItem(const CString& sItem)
{
int nIndex = CB_ERR;
if (!sItem.IsEmpty())
{
int nFind = FindStringExact(-1, sItem, m_bCaseSensitive);
if (nFind == CB_ERR)
{
nIndex = CComboBox::AddString(sItem); // add at end
if (nIndex != CB_ERR)
RefreshMaxDropWidth();
}
else // reinsert as text may have changed
nIndex = InsertUniqueItem(nFind, sItem);
}
return nIndex;
}
int CAutoComboBox::InsertUniqueItem(int nIndex, const CString& sItem){ if (!sItem.IsEmpty()) { int nFind = FindStringExact(-1, sItem, m_bCaseSensitive); if (nFind != CB_ERR)
{
CString sLBItem;
GetLBText(nFind, sLBItem);
if (nIndex != nFind || sItem != sLBItem)
{
// save selection so we can restore it
int nSel = GetCurSel();
CString sSelItem;
if (nSel != CB_ERR)
GetLBText(nSel, sSelItem);
DeleteString(nFind); // remove original nIndex = CComboBox::InsertString(nIndex, sItem); // re-insert
if (nIndex != CB_ERR)
RefreshMaxDropWidth();
// restore selection
if (nSel != CB_ERR)
SelectString(-1, sSelItem);
return nIndex;
}
}
else
{
nIndex = CComboBox::InsertString(nIndex, sItem); // re-insert
if (nIndex != CB_ERR)
RefreshMaxDropWidth();
return nIndex;
} } return CB_ERR; // invalid item}void CAutoComboBox::RefreshMaxDropWidth()
{
CDialogHelper::RefreshMaxDropWidth(*this);
}
void CAutoComboBox::OnSize(UINT nType, int cx, int cy) { CWnd* pEdit = GetDlgItem(1001); // if the edit control does not have the focus then hide the selection if (pEdit && pEdit != GetFocus()) { CHoldRedraw hr(*pEdit); CComboBox::OnSize(nType, cx, cy); pEdit->SendMessage(EM_SETSEL, (UINT)-1, 0); } else CComboBox::OnSize(nType, cx, cy);}int CAutoComboBox::AddUniqueItems(const CStringArray& aItems){ int nItem = aItems.GetSize(), nCount = 0; while (nItem--) { if (AddUniqueItem(aItems[nItem]) != CB_ERR) nCount++; } return nCount ? 0 : CB_ERR;}int CAutoComboBox::AddUniqueItems(const CAutoComboBox& cbItems){ CStringArray aItems; if (cbItems.GetItems(aItems)) return AddUniqueItems(aItems); // else return 0;}int CAutoComboBox::GetItems(CStringArray& aItems) const{ int nItem = GetCount(); aItems.RemoveAll(); aItems.SetSize(nItem); while (nItem--) { CString sItem; GetLBText(nItem, sItem); aItems.SetAt(nItem, sItem); // maintain order } return aItems.GetSize();}
// messages destined for the edit control
LRESULT CAutoComboBox::WindowProc(HWND /*hRealWnd*/, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg)
{
case WM_KEYDOWN:
// <Ctrl> + <Delete>
if (m_bAllowDelete && wp == VK_DELETE && GetDroppedState() && (GetKeyState(VK_CONTROL) & 0x8000))
{
if (DeleteSelectedLBItem())
{
// eat message else it'll go to the edit window
return 0L;
}
}
// <Return>
else if (wp == VK_RETURN)
{
if (GetDroppedState())
ShowDropDown(FALSE);
// can be simple or not dropped
// if (IsSimpleCombo() || !GetDroppedState())
HandleReturnKey();
return 0L;
}
else if (wp == VK_DOWN && !GetDroppedState())
{
ShowDropDown();
return 0L; // eat
}
break;
case WM_GETDLGCODE:
// special handling of the return key
if (lp)
{
LPMSG pMsg = (LPMSG)lp;
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
{
// can be simple or not dropped
if (IsSimpleCombo() || !GetDroppedState())
return DLGC_WANTALLKEYS;
}
}
break;
case WM_KILLFOCUS:
HandleReturnKey();
break;
}
return CSubclassWnd::Default();
}
void CAutoComboBox::HandleReturnKey()
{
if (IsHooked())
{
CString sEdit;
GetCWnd()->GetWindowText(sEdit);
int nAdd = AddUniqueItem(sEdit);
if (nAdd != CB_ERR)
CComboBox::GetParent()->SendMessage(WM_ACB_ITEMADDED,
MAKEWPARAM(CWnd::GetDlgCtrlID(), nAdd),
(LPARAM)(LPCTSTR)sEdit);
else // send a possible selection change
{
SelectString(-1, sEdit);
NotifyParent(CBN_SELCHANGE);
}
}
}
void CAutoComboBox::NotifyParent(UINT nIDNotify)
{
CWnd* pParent = CWnd::GetParent();
if (pParent)
{
UINT nID = CWnd::GetDlgCtrlID();
pParent->SendMessage(WM_COMMAND, MAKEWPARAM(nID, nIDNotify), (LPARAM)m_hWnd);
}
}
BOOL CAutoComboBox::IsSimpleCombo()
{
return ((CComboBox::GetStyle() & 0xf) == CBS_SIMPLE);
}
HBRUSH CAutoComboBox::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
// save handle of listbox window
if (m_bAllowDelete && !m_hwndListbox && nCtlColor == CTLCOLOR_LISTBOX)
m_hwndListbox = pWnd->GetSafeHwnd();
// and hook edit box
if (nCtlColor == CTLCOLOR_EDIT && !IsHooked())
HookWindow(pWnd->GetSafeHwnd());
return hbr;
}
BOOL CAutoComboBox::DeleteSelectedLBItem()
{
ASSERT(m_bAllowDelete);
int nSelItem = ::SendMessage(m_hwndListbox, LB_GETCURSEL, 0, 0);
if (nSelItem >= 0)
{
CString sCurItem, sSelItem;
int nCurSel = GetCurSel();
// save existing selection
if (nCurSel != -1)
GetWindowText(sCurItem);
GetLBText(nSelItem, sSelItem); // need this for notifying parent
::SendMessage(m_hwndListbox, LB_DELETESTRING, nSelItem, 0);
// restore combo selection
if (!sCurItem.IsEmpty())
SelectString(0, sCurItem);
// notify parent that we've been fiddling
CComboBox::GetParent()->SendMessage(WM_ACB_ITEMDELETED,
MAKEWPARAM(CWnd::GetDlgCtrlID(), nSelItem),
(LPARAM)(LPCTSTR)sSelItem);
return TRUE;
}
// else
return FALSE;
}
void CAutoComboBox::OnKillFocus(CWnd* pNewWnd)
{
CComboBox::OnKillFocus(pNewWnd);
HandleReturnKey();
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?