I'm currently in the process of dusting off the bugs from a tool written back in 2011. One of the bugs involves an editable ComboBox control paired with a separate Button control. When the button is clicked a popup menu is shown, and when an item in this menu is clicked, the text of the ComboBox is updated.

The problem with this scenario is by default, as soon as the ComboBox loses focus, the SelectionStart, SelectionLength and SelectedText properties are reset, thus preventing my little menu from replacing the selected text. While this article doesn't solve this problem (I'll save that for the next article!), it does describe how you can get the hWnd, or window handle (in .NET terms the Handle) of the edit component, thus opening the door for a little Win32 API fun.

There are various approaches to doing this - you could use FindWindow and search for a child window with a class of edit, or use SendMessage with the CB_GETCOMBOBOXINFO message. Or, easiest of all, we can use the GetComboBoxInfo function.

A simple demonstration application
A simple demonstration application

Interop Declarations

Using this API is very simple, as there's one method and two simple structs.

csharp
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);

[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
  public int cbSize;
  public RECT rcItem;
  public RECT rcButton;
  public int stateButton;
  public IntPtr hwndCombo;
  public IntPtr hwndEdit;
  public IntPtr hwndList;
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
  public int left;
  public int top;
  public int right;
  public int bottom;
}

Calling the API

There is one (well, many, but lets start simple!) caveat which you may fall foul of if you are new to Win32 API programming - often the contents of structs need to be initialized with their size first. But how to you know how big a structure is? Lucky for you, you don't need to calculate it manually - the Marshal.SizeOf function will handle this for you.

If you forget to set the size, then the structure simply won't be populated.

csharp
NativeMethods.COMBOBOXINFO info;

info = new NativeMethods.COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);

if (NativeMethods.GetComboBoxInfo(control.Handle, ref info))
{
  // the info structure is now populated, go wild!
}

Getting the edit control handle

With the above in place, then getting the handle of the edit component is very straightforward.

csharp
private IntPtr GetEditHandle(ComboBox control)
{
  NativeMethods.COMBOBOXINFO info;

  info = new NativeMethods.COMBOBOXINFO();
  info.cbSize = Marshal.SizeOf(info);

  return NativeMethods.GetComboBoxInfo(control.Handle, ref info) ? info.hwndEdit : IntPtr.Zero;
}

Bear in mind that the edit control is a Win32 control - not a managed .NET control. In order to do anything with this therefore, you need to use additional Win32 API methods, or perhaps bind it to a NativeWindow class for easy subclassing. I'll briefly cover some of this in a future article.

Other goodies

The COMBOBOXINFO structure has other information, such as the handle of the list control, which you see if you set the DropDownStyle property of the ComboBox to Simple and the state of the dropdown button. You can view the MSDN Documentation to learn more about the structure.

Update History

  • 2013-09-29 - First published
  • 2020-11-21 - Updated formatting

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Files


Comments