Oct 072013
 

Code Snippet to Resize CComboBox Drop Down List

I had this post in my draft for a long time. This code snippet resizes a combo box’s drop down list. Please note an application sends the CB_SETDROPPEDWIDTH message to set the minimum allowable width, in pixels, of the list box of a combo box with the CBS_DROPDOWN or CBS_DROPDOWNLIST style.

Note: MFC function CComboBox::SetDroppedWidth is just a wrapper for CB_SETDROPPEDWIDTH.

// Fill out combo box with strings of random width, for demo purpose only
void FillCombo(CComboBox& Combo)
{
	LPCTSTR lpAVeryLongString = _T("This is a very long string, the purpose of which is to test out width calculation of a combo drop down");
	TCHAR Buf[200] = {0};
 
	const int Len = _tcslen(lpAVeryLongString);
 
	Combo.Clear();
 
	for(int Index = 0; Index < 100; ++Index)
	{
		const int TrimLen = rand() % Len;
		_stprintf_s(Buf, _T("%.*s"), TrimLen, lpAVeryLongString);
		Combo.AddString(Buf);
	}
}
 
// Resize combo box drop down width based on contents
void AutoAdjustComboWidth(CComboBox& Combo)
{
	if(!Combo.GetSafeHwnd() || !::IsWindow(Combo.GetSafeHwnd()))
	{
		ASSERT(FALSE);
		return;
	}
 
	CDC& ComboDC = *Combo.GetDC();
	const int RestorePoint = ComboDC.SaveDC();
	CFont& ComboFont = *Combo.GetFont();
 
	ComboDC.SelectObject(&ComboFont);
 
	int MaxX = -1;
 
	const UINT ItemCount = Combo.GetCount();
	for(UINT Index = 0; Index < ItemCount; ++Index)
	{
		CString Text;
		Combo.GetLBText(Index, Text);
 
		// Get width of item string.
		const SIZE sz = ComboDC.GetTextExtent(Text);
 
		if(sz.cx > MaxX)
		{
			MaxX = sz.cx;
		}
	}// End for
 
	if(MaxX >  0)
	{
		const int ScrollWidth = ::GetSystemMetrics(SM_CXVSCROLL);
		const int Border = ::GetSystemMetrics(SM_CXEDGE) * 2;
		Combo.SetDroppedWidth(MaxX + ScrollWidth * 2);
	}
 
	ComboDC.RestoreDC(RestorePoint);
}

Some people might find this inconvenient as the list box width grows bigger. The way to workaround this issue is to use CComboBox::SetHorizontalExtent function as demonstrated here.

If you see bugs let me know,will fix those.

Aug 112009
 

The main reason for posting this issue is to help MFC/Win32 beginners. But anyway it’s a good read ;), so once upon a time…

If you’re a beginner with window’s ComboBox controls then you might end up with a bald head thinking and thinking about a weird behavior of combo’s after it’s creation. The combo as such is a great control but there are certain important things to keep in mind while creating a combo.

So what are the problems? Well make it singular, “Problem”. One and the only problem with this control is shown in this screenshot…

Drop down not visible

Drop down not visible

I’ve clicked the drop down arrow but cannot see the dropdown. This one is created via resource editor. The problem here is, just adding a combo is not enough but we’ve also got to set the size of the drop down via resource editor. So follow these steps, as shown in the screen shot to fix this issue…

Resizing a combo drop down

Resizing a combo drop down

So that’s it. The white drag rectangle denotes combo drop down size. Press Ctrl + T to test this. Please note that this is not a bug but a feature of resource editor since there isn’t an another way to set drop down size for a combo via resource editor. But I agree that they should’ve set a better default drop down size instead of a zero height drop down.

Now this was via resource editor. It’s bit different when creating dynamically via code using CComboBox::Create function. Note that you have to give a valid height value. This height value will be the dropdown size for this combo.

See this e.g.

m_Combo.Create( WS_VISIBLE | WS_TABSTOP | WS_BORDER | CBS_DROPDOWNLIST | CBS_SORT,
                CRect( 10, 10, 200, 300 ),
                this,
                IDC_COMBO1 + 1 );
m_Combo.SetFont( GetFont(), TRUE );

Hope you’ve noticed the last big value given to Combo via CRect, 300. That’s the drop down height. Normally beginners set it to 30 or 40 which results in this issue.

See the resultant screenshot…

Result of properly creating a combo

Result of properly creating a combo

Not a big deal for experts but for beginners, well I’ve been a beginner too, it’s one painful issue. 🙂

May 282009
 

CComboBox has a function called SetHorizontalExtent which doesn’t work. SDK equivalent is CB_SETHORIZONTALEXTENT which also doesn’t work. The reason for this bug is pretty lame, because WS_HSCROLL style for combo box is not set, which in turn the VS dialog editor does not provide :(. So a workaround is to open .rc file in a text editor and add WS_HSCROLL style manually for this combo.

This is how a combo box will look with a horizontal scrollbar…

Horizontal scrollbar in a combo box

Horizontal scrollbar in a combo box

Here is a sample code from MSDN which is adapted a bit for this purpose…

void CDialoTestDlg::AddHScroll( CComboBox& Cmb )
{
  // Find the longest string in the combo box.
  CString str;
  CSize sz;
  int dx = 0;

  TEXTMETRIC tm = { 0 };
  CDC* pDC = Cmb.GetDC();
  CFont* pFont = Cmb.GetFont();

  // Select the listbox font, save the old font
  CFont* pOldFont = pDC->SelectObject(pFont);

  // Get the text metrics for avg char width
  pDC->GetTextMetrics(&tm);

  for (int i = 0; i < Cmb.GetCount(); i++)
  {
    Cmb.GetLBText(i, str);
    sz = pDC->GetTextExtent(str);

    // Add the avg width to prevent clipping
    sz.cx += tm.tmAveCharWidth;

    if (sz.cx > dx)
      dx = sz.cx;
  }

  // Select the old font back into the DC
  pDC->SelectObject(pOldFont);
  Cmb.ReleaseDC(pDC);

  // Set the horizontal extent so every character of all strings can
  // be scrolled to.
  Cmb.SetHorizontalExtent(dx);
}// End AddHScroll

Setting horizontal extent is useful to prevent the combo’s drop down width from extending beyond screen limits. Note that there is another function called SetDroppedWidth which sets the drop down’s width but with no scrolling.

Nov 302007
 

We know that a combobox is a combination of different controls. Well now the question is there an API to get ample information on these controls nested in a combo?  The answer is yes, use GetComboBoxInfo.

Takes a COMBOBOXINFO pointer as parameter and on return will hold necessary information…

Usage:

COMBOBOXINFO CmbInfo = { 0 };
CmbInfo.cbSize = sizeof( CmbInfo );
VERIFY( GetComboBoxInfo( hWndComboHandle, &CmbInfo ));

As a result of this call CmbInfo is going to contain the list control handle, edit control handle and other information pertaining to a combo box.

An equivalent combobox message exists CB_GETCOMBOBOXINFO.