Override DrawItem of ComboBox
Solution 1
Here you go:
public class myCombo : ComboBox
{
// expose properties as needed
public Color SelectedBackColor{ get; set; }
// constructor
public myCombo()
{
DrawItem += new DrawItemEventHandler(DrawCustomMenuItem);
DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
SelectedBackColor= Color.LightSeaGreen;
}
protected void DrawCustomMenuItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
// a dropdownlist may initially have no item selected, so skip the highlighting:
if (e.Index >= 0)
{
Graphics g = e.Graphics;
Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ?
new SolidBrush(SelectedBackColor) : new SolidBrush(e.BackColor);
Brush tBrush = new SolidBrush(e.ForeColor);
g.FillRectangle(brush, e.Bounds);
e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font,
tBrush, e.Bounds, StringFormat.GenericDefault);
brush.Dispose();
tBrush.Dispose();
}
e.DrawFocusRectangle();
}
}
You may consider exposing more Properties as you expand your customziation, so you can change them for each instance when you want to..
Also don't forget to dispose GDI objects you create, like brushes and pens!
Edit: Just noticed that BackColor
would hide the original property. Changed it to SelectedBackColor
, which actually says what it is!
Edit 2: As Simon noted in the comments, there is a HasFlag
method and so as of .Net 4.0 one can also write:
Brush brush = ((e.State.HasFlag(DrawItemState.Selected) ?
which is a little clearer and shorter.
Edit 3: Actually for drawing onto controls the use of TextRenderer.DrawText
is recommended over graphics.DrawString
..
Solution 2
Thanks for this helpful post. I made one small enhancement that others may find useful.
When the ComboBox has its DisplayMember property set to access a specific property of the displayed items, ToString() may not give the expected text. The fix for this is to use:
GetItemText(Items[e.Index])
to retrieve the required text in the call to DrawString().
Comments
-
JackMini36 almost 2 years
I changed the highlight color of various of the controls, and I am planning to make more changes. So I though is better to create my own controls and reuse them instead of making the changed for each and every one of them.
I created a new user control, and inherited from
System.Windows.Forms.ComboBox
. The problem is I cannot find a way to overrideonDraw
like I would foronClick
.So how I would go and override it? Here is the code I used for each control
onDraw
eventpublic void comboMasterUsers_DrawItem(object sender, DrawItemEventArgs e) { e.DrawBackground(); Graphics g = e.Graphics; Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ? Brushes.LightSeaGreen : new SolidBrush(e.BackColor); g.FillRectangle(brush, e.Bounds); e.Graphics.DrawString(comboMasterUsers.Items[e.Index].ToString(), e.Font, new SolidBrush(e.ForeColor), e.Bounds, StringFormat.GenericDefault); e.DrawFocusRectangle(); }
Thanks!
-
JackMini36 almost 10 yearsThanks, it works. But there is one problem. When I change the DropDownStyle to DropDownList, there is an error: InvalidArgument =value of -1 is not valid for index
-
TaW almost 10 yearssee my small correction wrt tBrush! GDI objects have a hideous tendency to silently leak..
-
TaW almost 10 yearsAnd another small correction ;-) I didn't notice the warning but now it looks OK..
-
TaW almost 10 yearsTo make a long, sad story short: Brushes, Pens and Bitmaps are special. They are not normal c# objects, which the GC collects; they are stored separately and it is your responsibilty to dispose of them. A GDI heritage from the 90s..
-
JackMini36 almost 10 yearsoh I see.. Thx for the info!
-
Simon Jackson over 8 yearsIn .Net 4 and above, I think it is cleaner to write "e.State.HasFlag(DrawItemState.Selected)" instead of "e.State & DrawItemState.Selected) == DrawItemState.Selected"
-
Elmue over 5 yearsInstead of disposing the Brushes you could use the using(...) clause. All your background drawing with SelectedBackColor and FillRectangle() is unnecessary because this is already done in e.DrawBackground();
-
Cheva over 5 yearsInstead of "this.Items[e.Index].ToString()" use :
var svalue = string.Empty; var pi = Items[e.Index].GetType().GetProperty(DisplayMember); if (pi != null) svalue = pi.GetValue(Items[e.Index]).ToString();
-
Cheva over 5 yearsGet item text:
string svalue = this.GetItemText(SelectedItem);