Page 1 of 2
Auto-completare CComboBox
Posted: 11 Feb 2011, 11:07
by mesajflaviu
Ovidiu Cucu wrote:Din cate stiu eu, acest ficer nu-l ai by default nici intr-un combobox normal.
Ok, acolo n-ar fi fost din cale-afara de greu de implementat. Folosesti CComboBox::FindString pe o anumita notificare, dupa care faci si dregi.
Daca insa inlocuim lista cu un tree, nu cred ca mai e chiar asa de simplu.
Ce ar trebui sa intample daca, de exemplu, in copacel am avea douasprezece frunzulitze numite "twelve"?
In acest control nu vreau sa "combin" nici un alt control ( tree sau de alta natura ) , ci doar sa ii pun o functionalitate de auto-completare .. doar ca mi-o ia inainte ...
Code: Select all
// ComboBoxExt.h : header file
//
class CComboBoxExt : public CComboBox
{
// Construction
public:
CComboBoxExt();
protected:
//{{AFX_MSG(CComboBoxExt)
afx_msg void OnEditupdate();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
Code: Select all
// ComboBoxExt.cpp : implementation file
//
BEGIN_MESSAGE_MAP(CComboBoxExt, CComboBox)
//{{AFX_MSG_MAP(CComboBoxExt)
ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CComboBoxExt::OnEditupdate()
{
// TODO: Add your control notification handler code here
CString sText;
GetWindowText(sText);
if(sText.IsEmpty())ShowDropDown(FALSE);
else ShowDropDown();
}
doar atat am facut , si cand tastez 'O' , imi alege automat 'One' din lista , si mi-l marcheaza , fara sa vreau lucru asta ...
am atasat si o mica aplicatie de test ...
Re: Auto-completare CComboBox
Posted: 11 Feb 2011, 14:20
by Ovidiu Cucu
O carpeala rapida ar fi cam asa (vezi te rog comentariile din cod).
Code: Select all
void CComboBoxExt::OnEditchange() // CBN_EDITCHANGE handler
{
GetWindowText(m_strTypedText);
int nIndex = FindString(-1, m_strTypedText);
if(m_strTypedText.IsEmpty() || (CB_ERR == nIndex))
{
// edit text is empty or is not found in list
ShowDropDown(FALSE); // hide drop-down list
}
else
{
if(GetDroppedState())
{
// autocomplete text
CString strCompleteText;
GetLBText(nIndex, strCompleteText);
SetWindowText(strCompleteText);
// select "not typed yet" text
_SelectAutocompleteEdit();
}
else
{ // just show drop-down list
// autocomplete will be done later
ShowDropDown();
}
}
}
void CComboBoxExt::OnDropdown()
{
// by default the item is selected so post an user message
PostMessage(WM_USR_AUTOCOMPLETE);
}
LRESULT CComboBoxExt::OnUsrAutocomplete(WPARAM wParam, LPARAM lParam)
{
_SelectAutocompleteEdit();
return 0;
}
void CComboBoxExt::_SelectAutocompleteEdit()
{
CEdit* pEdit = (CEdit*)GetWindow(GW_CHILD);
if(NULL != pEdit)
{
const int nCurrentLength = pEdit->GetWindowTextLength();
const int nTypedLength = m_strTypedText.GetLength();
if(nTypedLength < nCurrentLength)
{
// select "not typed yet" text
pEdit->SetSel(nTypedLength, nCurrentLength);
}
else
{
pEdit->SetSel(nCurrentLength, nCurrentLength);
}
}
}
E pe undeva pe-aproape, autocomplete-ul functioneaza, totusi nu-i 100% Ok.
De exemplu comportarea nu-i tocmai buna atunci cand stergi cu "backspace".
Nu prea poti prinde mesajele de keyboard in combo ca sa corectezi problema.
Cred ca o solutie mai eleganta ar fi un hook in care sa prinzi mesajele dupa ce au fost procesate de control.
Parca am vazut asa ceva chiar intr-un exemplu de la tine.
Re: Auto-completare CComboBox
Posted: 12 Feb 2011, 11:41
by cristianamarie
IAutoCompletee o sursa buna.
"Autocompletion is typically used with edit controls or with controls that have an embedded edit control such as the comboboxex control."
Re: Auto-completare CComboBox
Posted: 13 Feb 2011, 12:10
by Ovidiu Cucu
Am gasit in
MSDN Magazine un Q&A scris de Paul DiLascia care ar putea fi cu folos.
Desi un pic mai greu pentru un programator MFC, cred ca merita de incercat si varianta lui Cristian cu
IAutoComplete.
Re: Auto-completare CComboBox
Posted: 15 Feb 2011, 17:11
by mesajflaviu
Multumesc pentru indrumari , ma apuc de lucru , sa vad ce pot face ...
Re: Auto-completare CComboBox
Posted: 26 Feb 2011, 15:18
by mesajflaviu
Am gasit o solutie pintr-un articol , nu stiu daca e cea mai buna solutie dar functioneaza :
Code: Select all
// ComboBoxExt.h : header file
//
private:
WNDPROC fNextListProc;
static LRESULT WinProcForList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
CEdit m_Edit;
CListBox m_List;
iar implementarea :
Code: Select all
#define WndPropertyComboBoxEx "comboboxex"
void CComboBoxExt::OnDestroy()
{
if(m_Edit.GetSafeHwnd())m_Edit.UnsubclassWindow();
if(m_List.GetSafeHwnd())m_List.UnsubclassWindow();
CComboBox::OnDestroy();
// TODO: Add your message handler code here
}
Code: Select all
void CComboBoxExt::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
CComboBox::PreSubclassWindow();
COMBOBOXINFO ComboBoxInfo;
ComboBoxInfo.cbSize = sizeof(ComboBoxInfo);
GetComboBoxInfo(m_hWnd,&ComboBoxInfo);
m_Edit.SubclassWindow(ComboBoxInfo.hwndItem);
m_List.SubclassWindow(ComboBoxInfo.hwndList);
SetProp(ComboBoxInfo.hwndList, WndPropertyComboBoxEx, this);
fNextListProc = (WNDPROC)SetWindowLong(ComboBoxInfo.hwndList, GWL_WNDPROC, (LONG)WinProcForList);
}
Code: Select all
LRESULT CComboBoxExt::WinProcForList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CComboBoxExt* pInstance = (CComboBoxExt*)GetProp(hWnd,WndPropertyComboBoxEx);
ASSERT(pInstance != NULL);
if(msg == LB_FINDSTRING)
{
TRACE("Replacing LB_FINDSTRING with LB_FINDSTRINGEXACT when looking for: \"%s\"\n", (LPCTSTR)lParam);
msg = LB_FINDSTRINGEXACT;
}
return CallWindowProc(pInstance->fNextListProc, hWnd, msg, wParam, lParam);
}
se pare ca in momentul tastarii de litere in edit-ul controlului , se foloseste FindString pentru a cauta in dropdown list cuvinte care se potrivesc cu litera/literele testate , iar deturnarea mesajului LB_FINDSTRING cu LB_FINDSTRINGEXACT este solutia pentru a nu mai completa automat .
atasez si proiectelul pentru cei care intampina aceeasi problema ...
Re: Auto-completare CComboBox
Posted: 28 Feb 2011, 20:28
by mesajflaviu
Cred ca am intrat intr-o fundatura : exista si alta posibilitate de a avea un handle spre dropdown list , in afara de
ComboBoxInfo.hwndList , pentru a putea-o folosi in
Code: Select all
fNextListProc = (WNDPROC)SetWindowLong(ComboBoxInfo.hwndList, GWL_WNDPROC, (LONG)WinProcForList);
?
La lucru am Windows NT SP6 , iar structura COMBOBOXINFO si GetComboboxInfo(...) nu este disponibil decat din Windows2000 in sus ... cu structura am rezolvat-o ca am gasit codul sursa pe net , dar pentru functia GetComboBoxInfo(...) n-am gasit nicaieri sursa ....
Am incercat sa obtin acel handle si cu
Code: Select all
HWND CComboBoxExt::GetDropdownListBox()
dar nu merge ...
Re: Auto-completare CComboBox
Posted: 01 Mar 2011, 09:44
by mesajflaviu
Nu inteleg care e diferenta intre :
obtinut dupa metoda de mai sus si
in care m_List.m_hWnd este obtinut dupa metoda de
aici
Fiindca urmatoarea linie nu functioneaza decat cu
ComboBoxInfo.hwndList
Code: Select all
fNextListProc = (WNDPROC)SetWindowLong(ComboBoxInfo.hwndList, GWL_WNDPROC, (LONG)WinProcForList); // asa functioneaza
fNextListProc = (WNDPROC)SetWindowLong(m_List.m_hWnd, GWL_WNDPROC, (LONG)WinProcForList); // asa nu functioneaza !?
Re: Auto-completare CComboBox
Posted: 01 Mar 2011, 22:10
by Ovidiu Cucu
mesajflaviu wrote:La lucru am Windows NT SP6 , iar structura COMBOBOXINFO si GetComboboxInfo(...) nu este disponibil decat din Windows2000 in sus ... cu structura am rezolvat-o ca am gasit codul sursa pe net , dar pentru functia GetComboBoxInfo(...) n-am gasit nicaieri sursa ....
In documentatie lui GetComboboxInfo, scrie la Requirements:
Windows NT/2000/XP: Included in Windows NT 4.0 SP6 and later.
Windows 95/98/Me: Included in Windows 98 and later.
Deci, functia
exista si in NT 4.0 SP6.
Nu exista in SDK-ul pe care-l ai tu. Cel mai corect ar fi sa-ti pui un SDK mai nou.
Din pacate, ultima versiune care se pupa cu VC6.0 e "Platform SDK" din februarie 2003 si nu se mai gaseste pentru download.
Daca nu reusesti sa faci rost de ea, atunci ceea ce te salveaza in asemenea situatii este clasicul cuplu LoadLibrary(sau GetModuleHandle) si GetProcAddress.
Cam asa:
Code: Select all
typedef struct tagCOMBOBOXINFO {
DWORD cbSize;
RECT rcItem;
RECT rcButton;
DWORD stateButton;
HWND hwndCombo;
HWND hwndItem;
HWND hwndList;
} COMBOBOXINFO, *PCOMBOBOXINFO, *LPCOMBOBOXINFO;
typedef BOOL (WINAPI *pfnGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
//...
void CMyDialog::OnGetComboInfo()
{
COMBOBOXINFO cbi = {0};
cbi.cbSize = sizeof(COMBOBOXINFO);
HMODULE hModule = GetModuleHandle(_T("User32.dll"));
pfnGetComboBoxInfo pFn =
(pfnGetComboBoxInfo)GetProcAddress(hModule, "GetComboBoxInfo");
if(NULL != pFn)
{
BOOL bRet = pFn(m_combo.m_hWnd, &cbi);
// ...
}
}
[ Later Edit ]
Intradevar, in documentatia on-line spune ca pentru GetComboboxInfo trebuie cel putin Windows 2000, in contradictie cu ce scrie in help-ul de la VS6.0 (eu am editia din octombrie 2001).
S-ar putea sa fie o greseala, asa ca incearca codul pe care ti l-am dat si vezi daca merge.
Re: Auto-completare CComboBox
Posted: 01 Mar 2011, 23:34
by mesajflaviu
Multumesc mult Ovidiu pentru idee , maine dimineata la prima ora o sa incerc si revin .
Re: Auto-completare CComboBox
Posted: 01 Mar 2011, 23:53
by Ovidiu Cucu
mesajflaviu wrote:Nu inteleg care e diferenta intre :
obtinut dupa metoda de mai sus si
in care m_List.m_hWnd este obtinut dupa metoda de
aici
Al doilea va fi setat abia dupa ce dai prima data click pe butonelul de la combo.
Re: Auto-completare CComboBox
Posted: 02 Mar 2011, 12:42
by mesajflaviu
Ovidiu Cucu wrote:mesajflaviu wrote:La lucru am Windows NT SP6 , iar structura COMBOBOXINFO si GetComboboxInfo(...) nu este disponibil decat din Windows2000 in sus ... cu structura am rezolvat-o ca am gasit codul sursa pe net , dar pentru functia GetComboBoxInfo(...) n-am gasit nicaieri sursa ....
In documentatie lui GetComboboxInfo, scrie la Requirements:
Windows NT/2000/XP: Included in Windows NT 4.0 SP6 and later.
Windows 95/98/Me: Included in Windows 98 and later.
Deci, functia
exista si in NT 4.0 SP6.
Nu exista in SDK-ul pe care-l ai tu. Cel mai corect ar fi sa-ti pui un SDK mai nou.
Din pacate, ultima versiune care se pupa cu VC6.0 e "Platform SDK" din februarie 2003 si nu se mai gaseste pentru download.
Daca nu reusesti sa faci rost de ea, atunci ceea ce te salveaza in asemenea situatii este clasicul cuplu LoadLibrary(sau GetModuleHandle) si GetProcAddress.
Cam asa:
Code: Select all
typedef struct tagCOMBOBOXINFO {
DWORD cbSize;
RECT rcItem;
RECT rcButton;
DWORD stateButton;
HWND hwndCombo;
HWND hwndItem;
HWND hwndList;
} COMBOBOXINFO, *PCOMBOBOXINFO, *LPCOMBOBOXINFO;
typedef BOOL (WINAPI *pfnGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
//...
void CMyDialog::OnGetComboInfo()
{
COMBOBOXINFO cbi = {0};
cbi.cbSize = sizeof(COMBOBOXINFO);
HMODULE hModule = GetModuleHandle(_T("User32.dll"));
pfnGetComboBoxInfo pFn =
(pfnGetComboBoxInfo)GetProcAddress(hModule, "GetComboBoxInfo");
if(NULL != pFn)
{
BOOL bRet = pFn(m_combo.m_hWnd, &cbi);
// ...
}
}
[ Later Edit ]
Intradevar, in documentatia on-line spune ca pentru GetComboboxInfo trebuie cel putin Windows 2000, in contradictie cu ce scrie in help-ul de la VS6.0 (eu am editia din octombrie 2001).
S-ar putea sa fie o greseala, asa ca incearca codul pe care ti l-am dat si vezi daca merge.
Am incercat aceasta metoda si functioneaza , dar nu cum mi-ar trebui , in sensul ca eu am incercat sa obtin acel handler in PreSubclassWindow(...) si acolo nu merge ...
Re: Auto-completare CComboBox
Posted: 02 Mar 2011, 13:15
by mesajflaviu
In schimb , am reusit sa obtin handle spre dropdown list in modul urmator :
Code: Select all
// CComboBoxExt.h
protected:
HWND m_hListBox;
//{{AFX_MSG(CComboBoxExt)
afx_msg LRESULT OnCtlColorListBox(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
WNDPROC fNextListProc;
static LRESULT WinProcForList(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
apoi
Code: Select all
// CComboBoxExt.cpp
static WNDPROC m_pWndProc = NULL;
extern "C" LRESULT FAR PASCAL ComboBoxListBoxProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
TRACE("\n Execution of ComboBoxListBoxProc \n");
if(nMsg == LB_FINDSTRING)
{
TRACE("Replacing LB_FINDSTRING with LB_FINDSTRINGEXACT when looking for: \"%s\"\n", (LPCTSTR)lParam);
nMsg = LB_FINDSTRINGEXACT;
}
return CallWindowProc(m_pWndProc, hWnd, nMsg, wParam, lParam);
}
Code: Select all
LRESULT CComboBoxExt::OnCtlColorListBox(WPARAM wParam, LPARAM lParam)
{
// If the listbox hasn't been subclassed yet, do so...
if(m_hListBox == NULL)
{
HWND hWnd = reinterpret_cast<HWND>(lParam);
if(hWnd != 0 && hWnd != m_hWnd)
{
// Save the listbox handle
m_hListBox = hWnd;
// Do the subclassing
m_pWndProc = reinterpret_cast<WNDPROC>(GetWindowLong(m_hListBox, GWL_WNDPROC));
SetWindowLong(m_hListBox, GWL_WNDPROC, reinterpret_cast<long>(ComboBoxListBoxProc));
}
}
return Default();
}
si merge bine si sub Windows NT , dar are o mica constrangere ... de vreme ce obtin m_hListBox in OnCtlColorListBox(...) , subclasarea se face doar dupa ce dropdown-ul este TRUE cel putin o data ( dupa ce apas acel butonel de pe combo ) ...
Re: Auto-completare CComboBox
Posted: 02 Mar 2011, 14:51
by Ovidiu Cucu
Ceva gen...
Code: Select all
#include "MyListBox.h"
class CMyCombo : public CComboBox
{
CMyListBox m_listbox;
//...
};
Code: Select all
void CMyCombo::PreSubclassWindow()
{
COMBOBOXINFO cbi = {0};
cbi.cbSize = sizeof(COMBOBOXINFO);
HMODULE hModule = GetModuleHandle(_T("User32.dll"));
pfnGetComboBoxInfo pFn =
(pfnGetComboBoxInfo)GetProcAddress(hModule, "GetComboBoxInfo");
if(NULL != pFn)
{
if(pFn(m_hWnd, &cbi))
{
m_listbox.SubclassWindow(cbi.hwndList);
}
}
}
...merge fara probleme. Te duci acum in CMyListBox (derivat din CListBox) si tratezi ce mesaje vrea muschii tai.
Nu trebuie sa "cobori" atat de jos incat sa te joci direct cu SetWindowLong.
Iar solutia cu CTLCOLOR_LISTBOX din MSDN e probabil de pe vremea lui Pazvante Chioru.

Re: Auto-completare CComboBox
Posted: 03 Mar 2011, 10:33
by mesajflaviu
Am rezolvat problema in stilul simplu pe care mi l-a aratat Ovidiu prima data , un pic cosmetizat ... si eu incercam sa o intorc pe dupa sura ...