Auto-completare CComboBox

Intrebari legate de programarea cu biblioteci precum MFC, ATL, WTL si GDI+.
mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Auto-completare CComboBox

Post by mesajflaviu » 11 Feb 2011, 11:07

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 ...
Attachments
TestCombo.rar
(79.37 KiB) Downloaded 291 times



User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Auto-completare CComboBox

Post by Ovidiu Cucu » 11 Feb 2011, 14:20

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.

User avatar
cristianamarie
Membru++
Membru++
Posts: 480
Joined: 12 Mar 2009, 18:47
Judet: Iaşi
Location: Iasi

Re: Auto-completare CComboBox

Post by cristianamarie » 12 Feb 2011, 11:41

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."
Nuclear launch detected

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Auto-completare CComboBox

Post by Ovidiu Cucu » 13 Feb 2011, 12:10

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.

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 15 Feb 2011, 17:11

Multumesc pentru indrumari , ma apuc de lucru , sa vad ce pot face ...

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 26 Feb 2011, 15:18

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 ...
Attachments
TestCombo.rar
(80.18 KiB) Downloaded 271 times

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 28 Feb 2011, 20:28

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 ...

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 01 Mar 2011, 09:44

Nu inteleg care e diferenta intre :

Code: Select all

ComboBoxInfo.hwndList
obtinut dupa metoda de mai sus si

Code: Select all

m_List.m_hWnd
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 !?

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Auto-completare CComboBox

Post by Ovidiu Cucu » 01 Mar 2011, 22:10

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.

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 01 Mar 2011, 23:34

Multumesc mult Ovidiu pentru idee , maine dimineata la prima ora o sa incerc si revin .

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Auto-completare CComboBox

Post by Ovidiu Cucu » 01 Mar 2011, 23:53

mesajflaviu wrote:Nu inteleg care e diferenta intre :

Code: Select all

ComboBoxInfo.hwndList
obtinut dupa metoda de mai sus si

Code: Select all

m_List.m_hWnd
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.

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 02 Mar 2011, 12:42

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 ...

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 02 Mar 2011, 13:15

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 ) ...

User avatar
Ovidiu Cucu
Fondator
Fondator
Posts: 3778
Joined: 11 Jul 2007, 16:10
Judet: Iaşi
Location: Iasi
Contact:

Re: Auto-completare CComboBox

Post by Ovidiu Cucu » 02 Mar 2011, 14:51

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. ;)

mesajflaviu
Membru++
Membru++
Posts: 687
Joined: 10 Sep 2008, 21:40
Judet: Cluj

Re: Auto-completare CComboBox

Post by mesajflaviu » 03 Mar 2011, 10:33

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 ...
Attachments
TestCombo.rar
(79.79 KiB) Downloaded 264 times
Last edited by mesajflaviu on 21 Mar 2011, 23:18, edited 2 times in total.

Post Reply