The standard ComboBox in NETCF has a couple of “issues”, however it’s possible to workaround them with a bit of tweaking. I rolled together a number of these fixes into a ComboBoxEx class. Heres the code (C#), I hope this (with a few more missing features) will be in the next SDF build with designer support like our other controls:-
namespace OpenNETCF.Windows.Forms
{
/// <summary>
/// Extended ComboBox control.
/// </summary>
public class ComboBoxEx : ComboBox, IWin32Window
{
//windows messages
private const int WM_SETREDRAW = 0x0b;
private const int CB_SETCURSEL = 0x014E;
private const int CB_DELETESTRING = 0x0144;
private const int CB_INSERTSTRING = 0x014A;
//native window handle
private IntPtr m_handle;
//databound collection
private IBindingList thebindinglist = null;
//is display updatable?
private bool m_updatable = true;
/// <summary>
/// Gets the window handle that the control is bound to.
/// </summary>
public IntPtr Handle
{
get
{
if(m_handle == IntPtr.Zero)
{
this.Capture = true;
m_handle = Win32Window.GetCapture();
this.Capture = false;
}
return m_handle;
}
}
// Redraw code courtesy of Alex Feinman – http://blog.opennetcf.org/afeinman/PermaLink,guid,9305a1d9-e24e-4310-89e2-f80808076a37.aspx
/// <summary>
/// Maintains performance when items are added to the <see cref=”ComboBoxEx”/> one at a time.
/// </summary>
public void BeginUpdate()
{
m_updatable = false;
Win32Window.SendMessage(this.Handle, WM_SETREDRAW, 0, 0);
}
/// <summary>
/// Resumes painting the <see cref=”ComboBoxEx”/> control after painting is suspended by the <see cref=”BeginUpdate”/> method.
/// </summary>
public void EndUpdate()
{
m_updatable = true;
Win32Window.SendMessage(this.Handle, WM_SETREDRAW, 1, 0);
}
//ComboBox doesn’t support ItemChanges in a datasource implementing IBindingList
//The following workaround forces the list to update if an item is changed
//data source has changed
protected override void OnDataSourceChanged(EventArgs e)
{
//remove event handler
if(thebindinglist != null)
{
thebindinglist.ListChanged-= new ListChangedEventHandler(ComboBoxEx_ListChanged);
//reset our handle to the bound data
thebindinglist = null;
}
//get the underlying ibindinglist (if there is one)
if(this.DataSource is IListSource)
{
IList thelist = ((IListSource)this.DataSource).GetList();
if(thelist is IBindingList)
{
thebindinglist = (IBindingList)thelist;
}
}
else if(this.DataSource is IBindingList)
{
thebindinglist = (IBindingList)this.DataSource;
}
if(thebindinglist != null)
{
//hook up event for data changed
thebindinglist.ListChanged+=new ListChangedEventHandler(ComboBoxEx_ListChanged);
}
base.OnDataSourceChanged (e);
}
//called when a change occurs in the bound collection
private void ComboBoxEx_ListChanged(object sender, ListChangedEventArgs e)
{
if(m_updatable)
{
if (e.ListChangedType == ListChangedType.ItemChanged)
{
//update the item
//delete old item
Win32Window.SendMessage(this.Handle, CB_DELETESTRING, e.NewIndex, 0);
//get display text for new item
string newval = this.GetItemText(this.Items[e.NewIndex]);
//marshal to native memory
IntPtr pString = MarshalEx.StringToHGlobalUni(newval);
//send message to native control
Win32Window.SendMessage(this.Handle, CB_INSERTSTRING, e.NewIndex, pString);
//free native memory
MarshalEx.FreeHGlobal(pString);
}
}
}
/// <summary>
/// Get or Set the selected index in collection.
/// </summary>
/// <remarks>Overridden to overcome issue with setting value to -1 (http://support.microsoft.com/default.aspx?scid=kb;en-us;327244)</remarks>
public override int SelectedIndex
{
get
{
return base.SelectedIndex;
}
set
{
if(value == -1)
{
Win32Window.SendMessage(this.Handle, CB_SETCURSEL, -1, 0);
}
else
{
base.SelectedIndex = value;
}
}
}
}
}
So whats in the above code? Well it integrates Alex Feinman’s recent tip to implement Begin/EndUpdate methods to suspend redrawing when populating the control.
Secondly if works around a bug in the DataBinding support for ComboBox. If you make an edit to an existing item in the bound collection the combo wont normally update (It does correctly react to additions and deletions).
Thirdly it overcomes a known issue with resetting the selected index property (by assigning -1) which normally has to be done twice to take effect – details in this KB article – Thanks to Tim Wilson for posting this KB on the newsgroup.
As a Bonus I’ve added a Handle property so you can get at the native control handle – which in conjunction with Win32Window methods allows you to tweak other aspects of the control.
UPDATE: Changed the databinding code to update a single item via P/Invoke rather than refreshing the list. Changed the SelectedIndex to set via P/Invoke – avoids raising the SelectedIndexChanged event.