CameTooFar

A Nerd's False Positive.

How to set Individual Color for ComboBoxItem – C#


Recently, I’s populating some details, say Product Names, into a ComboBox. Yup! populating is simple and direct. After populating I noticed that, the data loaded contains Product Names both New and Obsolete data(means the production has been stopped, long time back) and the only way to differentiate between them is to colorize the products. If that was in Web, we can inject <Style> to achieve the same and it’s straight-forward. But, in Windows Application? There starts the game!

Directly setting the ForeColor of a ComboBoxItem won’t be a solution. If you set the  ForeColor as say, Red. Then, all the ComboBoxItem will have its ForeColor as Red. Then how can you set the ForeColor of Individual ComboBoxItems? Though sounds weird, the solution is tricky.

All you need to do is to handle the DrawItem event of the ComboBox and draw the ComboBoxItem Text (product-name, here) using the DrawString method. DrawItem allows you to control the drawing of a ComboBoxItem. And, DrawString allows you to draw the ComboBoxItem Text in a specified Rectangle, using specified Color (using Brush)and Font.

To simulate the same. I’m binding the ComboBox with List<Products>.

The Products class looks like:

   1:  
   2: public class Products
   3:     {
   4:         /// <summary>
   5:         /// Name of the Product
   6:         /// </summary>
   7:         public string ProductName { get; set; }
   8:  
   9:         /// <summary>
  10:         /// Status of the Product
  11:         /// 1: New/Available Product
  12:         /// 0: Obsolete Product
  13:         /// </summary>
  14:         public short ProductStatus { get; set; }
  15:     }

Now, Bind the ComboBox with List<Products> in the Form_Load().

   1: /// <summary>
   2: /// Bind Product Details with the ComboBox
   3: /// </summary>
   4: private void BindProducts()
   5: {
   6:     // Add Dummy Data
   7:  
   8:     List<Products> listPdt = new List<Products>();
   9:     Products pdt = null;
  10:  
  11:     pdt = new Products();
  12:     pdt.ProductName = "Product 1";
  13:     pdt.ProductStatus = 1;
  14:     listPdt.Add(pdt);
  15:  
  16:     pdt = new Products();
  17:     pdt.ProductName = "Product 2";
  18:     pdt.ProductStatus = 1;
  19:     listPdt.Add(pdt);
  20:  
  21:     pdt = new Products();
  22:     pdt.ProductName = "Product 3";
  23:     pdt.ProductStatus = 1;
  24:     listPdt.Add(pdt);
  25:  
  26:     pdt = new Products();
  27:     pdt.ProductName = "Product 4";
  28:     pdt.ProductStatus = 0;
  29:     listPdt.Add(pdt);
  30:  
  31:     pdt = new Products();
  32:     pdt.ProductName = "Product 5";
  33:     pdt.ProductStatus = 1;
  34:     listPdt.Add(pdt);
  35:  
  36:     // Bind
  37:  
  38:     comboProduct.DataSource = listPdt;
  39:     comboProduct.DisplayMember = "ProductName";
  40:     comboProduct.ValueMember = "ProductStatus";
  41: }

And, the DrawItem Code looks like

   1: private void comboProduct_DrawItem(object sender, DrawItemEventArgs e)
   2: {
   3:     short status = 0;
   4:  
   5:     Brush brush = null;
   6:     ComboBox combo;
   7:  
   8:     try
   9:     {
  10:         e.DrawBackground();
  11:  
  12:         combo = (ComboBox)sender;
  13:         Products pdt = (Products)combo.Items[e.Index];
  14:  
  15:         status = pdt.ProductStatus;
  16:  
  17:         if (status == 0)
  18:         {
  19:             brush = Brushes.Red;
  20:         }
  21:         else
  22:         {
  23:             brush = Brushes.Black;
  24:         }
  25:  
  26:         e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  27:         e.Graphics.DrawString(pdt.ProductName, combo.Font, brush, e.Bounds.X, e.Bounds.Y);
  28:     }
  29:     catch (Exception exp)
  30:     {
  31:         MessageBox.Show(exp.Message);
  32:     }
  33: }
 
A sample screenshot looks like:
 
Combo_Draw_Bg_Selected Combo_Draw_Bg_Selection
 
Have you noticed the blue background selection when the mouse is hovered over the ComboBoxItem and also when the selection is completed. The blue background selection is really annoying, since it takes  away our effort of coloring a ComboBoxItem and clearly a affects readability. And, the next question will be how’ll you remove the annoying blue selection background in the ComboBox?
 
That's bit tricky.
 
The blue selection of ComboBoxItem is actually a  Rectangle with blue background color. All you need to do is to draw the Rectangle through your code; fill it with White color and set it as the background for the ComboBoxItem, using the DrawItem event. Sounds nice, Huh? Here is the code fragment:
 
   1:  
   2: e.Graphics.DrawRectangle(new Pen(Color.White), e.Bounds);
   3: e.Graphics.FillRectangle(Brushes.White, e.Bounds);
 
Hence, the complete code for the DrawItem event looks like
 
   1: private void comboProduct_DrawItem(object sender, DrawItemEventArgs e)
   2: {
   3:     short status = 0;
   4:  
   5:     Brush brush = null;
   6:     ComboBox combo;
   7:  
   8:     try
   9:     {
  10:         e.DrawBackground();
  11:  
  12:         combo = (ComboBox)sender;
  13:         Products pdt = (Products)combo.Items[e.Index];
  14:  
  15:         status = pdt.ProductStatus;
  16:  
  17:         // Determine Status and apply Color
  18:         if (status == 0)
  19:         {
  20:             brush = Brushes.Red;
  21:         }
  22:         else
  23:         {
  24:             brush = Brushes.Black;
  25:         }
  26:  
  27:         // Handle default Blue background selection
  28:         e.Graphics.DrawRectangle(new Pen(Color.White), e.Bounds);
  29:         e.Graphics.FillRectangle(Brushes.White, e.Bounds);
  30:  
  31:         // Draw the Colored String
  32:         e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  33:         e.Graphics.DrawString(pdt.ProductName, combo.Font, brush, e.Bounds.X, e.Bounds.Y);
  34:     }
  35:     catch (Exception exp)
  36:     {
  37:         MessageBox.Show(exp.Message);
  38:     }
  39: }
 
Note:
1. Change the OwnerDraw property of the ComboBox from Normal to DrawModeVariable. Else, the code written in the DrawItem wont have any effect, since the coloring is handled internally.
 
Hope this helps.
Thanks.