views:

292

answers:

1

I have a .net 3.5 windows application with a bound combobox. I have overridden the DrawItem event to color the background of individual items based on certain conditions. I have asynchronous threads running which update the condition values and call Invalidate() to get the combobox to redraw.

This all works very well, except when the combobox is expanded - DrawItem is only called for the item that's highlighted in the list. The other items only refresh when the user does something, like mouse over another item in the list or click a different control. I would like the other items to redraw automatically while the list is open. How do I make this happen? Is it possible?

thanks

edit: I determined that whatever item is highlighted in the expanded list is redrawn in the main combobox display. no items in the list are redrawn in the expanded part of the control.

edit: here's a stripped down example form. for anyone who wants to test you should be able to create a new windows forms app and add this in a new class file and it should build.

to reproduce the behavior, click the "detect" button, then open the combobox and hover over COM1. test results will finish in the order of 1,3,2. you can see that while the combobox is open, COM3 stays yellow until you move the highlight over it and then off.

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.IO.Ports;
using System.Diagnostics;
using System.Drawing;

namespace combocolor
{
public partial class Demo2 : Form
{
private System.ComponentModel.IContainer components = null;
private System.Windows.Forms.Label ComPortLabel;
private System.Windows.Forms.ComboBox ComPortComboBox;
private System.Windows.Forms.ErrorProvider MainErrorProvider;
private System.Windows.Forms.Button autoQueryButton;

delegate PortTestResult TestPortDelegate(string portName);
delegate void UpdateTestResultsDelegate(PortTestResult portTestResult);

string[] _ports = new string[] { "COM1", "COM2", "COM3" };
Dictionary<string, int> _autoDetectResults = null;

public Demo2()
{
    InitializeComponent();
}

private void Settings_Load(object sender, EventArgs e)
{
    this.ComPortComboBox.Items.AddRange(this._ports);
    this.ComPortComboBox.SelectedIndex = 0;
}

private void autoQueryButton_Click(object sender, EventArgs e)
{
    // start port testing
    this._autoDetectResults = new Dictionary<string, int>(this._ports.Length);
    foreach (string portName in this._ports)
    {
        this._autoDetectResults.Add(portName, 0);
        this.ComPortComboBox.Invalidate();

        try
        {
            TestPortDelegate testDel = new TestPortDelegate(TestSerialPort);    // check port on a new thread
            testDel.BeginInvoke(portName, new AsyncCallback(TestFinishedCallback), testDel);
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
        }
    }
}

private void ComPortComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
    SolidBrush backgroundBrush, foregroundBrush;
    Dictionary<int, Color> colormap = new Dictionary<int, Color>();

    colormap.Add(0, Color.Yellow);
    colormap.Add(-1, Color.Red);
    colormap.Add(1, Color.LimeGreen);
    string itemText = (string)ComPortComboBox.Items[e.Index];

    // select a background color based on autodetect status
    if (this._autoDetectResults == null)
    {
        backgroundBrush = new System.Drawing.SolidBrush(e.BackColor);
    }
    else
    {
        int key = this._autoDetectResults[itemText];
        backgroundBrush = new SolidBrush(colormap[key]);
    }

    if ((e.State & DrawItemState.Selected) > 0 && (e.State & DrawItemState.ComboBoxEdit) == 0)
    {
        e.DrawBackground(); // draws the blue highlight
        foregroundBrush = new SolidBrush(e.ForeColor); // text color
    }
    else
    {
        e.Graphics.FillRectangle(backgroundBrush, e.Bounds); // draws the color based on the autodetect results
        foregroundBrush = new SolidBrush(this.ComPortComboBox.ForeColor); // text color 
    }

    e.Graphics.DrawString(itemText, e.Font, foregroundBrush, e.Bounds); // draw the text
}

private PortTestResult TestSerialPort(string portName)
{
    PortTestResult result = new PortTestResult();
    result.PortName = portName;

    // simulated results
    switch (portName)
    {
        case "COM1":
            System.Threading.Thread.Sleep(2000);
            result.UseThisPort = false;
            break;
        case "COM2":
            System.Threading.Thread.Sleep(6000);
            result.UseThisPort = true;
            break;
        case "COM3":
            System.Threading.Thread.Sleep(4000);
            result.UseThisPort = false;
            break;
    }

    return result;
}

private void TestFinishedCallback(IAsyncResult ar)
{
    // get the results from the test function
    TestPortDelegate testPortDelegate = (TestPortDelegate)((System.Runtime.Remoting.Messaging.AsyncResult)ar).AsyncDelegate;
    PortTestResult portTestResult = testPortDelegate.EndInvoke(ar);
    UpdateTestResults(portTestResult); // pass the results along to update the UI
}

private void UpdateTestResults(PortTestResult portTestResult)
{
    if (this.ComPortComboBox.InvokeRequired)
    {
        UpdateTestResultsDelegate updateTestResultsDelegate = new UpdateTestResultsDelegate(UpdateTestResults);
        this.Invoke(updateTestResultsDelegate, portTestResult);
    }
    else
    { // set status based on test result
        if (portTestResult.UseThisPort)
        {
            this._autoDetectResults[portTestResult.PortName] = 1;   // 1 for a good response
            this.ComPortComboBox.SelectedItem = portTestResult.PortName;
        }
        else
        {
            this._autoDetectResults[portTestResult.PortName] = -1; // -1 for a bad response
        }
        this.ComPortComboBox.Invalidate();  // force the combobox to redraw with new colors
    }
}


protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}
private void InitializeComponent()
{
    this.components = new System.ComponentModel.Container();
    this.ComPortComboBox = new System.Windows.Forms.ComboBox();
    this.ComPortLabel = new System.Windows.Forms.Label();
    this.MainErrorProvider = new System.Windows.Forms.ErrorProvider(this.components);
    this.autoQueryButton = new System.Windows.Forms.Button();
    ((System.ComponentModel.ISupportInitialize)(this.MainErrorProvider)).BeginInit();
    this.SuspendLayout();
    this.ComPortComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
    this.ComPortComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
    this.ComPortComboBox.FormattingEnabled = true;
    this.ComPortComboBox.Location = new System.Drawing.Point(71, 12);
    this.ComPortComboBox.Name = "ComPortComboBox";
    this.ComPortComboBox.Size = new System.Drawing.Size(136, 21);
    this.ComPortComboBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.ComPortComboBox_DrawItem);
    this.ComPortLabel.Location = new System.Drawing.Point(12, 15);
    this.ComPortLabel.Name = "ComPortLabel";
    this.ComPortLabel.Size = new System.Drawing.Size(53, 13);
    this.ComPortLabel.Text = "&Com Port:";
    this.autoQueryButton.Location = new System.Drawing.Point(213, 11);
    this.autoQueryButton.Name = "autoQueryButton";
    this.autoQueryButton.Size = new System.Drawing.Size(49, 21);
    this.autoQueryButton.Text = "detect";
    this.autoQueryButton.UseVisualStyleBackColor = false;
    this.autoQueryButton.Click += new System.EventHandler(this.autoQueryButton_Click);
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(282, 47);
    this.Controls.Add(this.autoQueryButton);
    this.Controls.Add(this.ComPortLabel);
    this.Controls.Add(this.ComPortComboBox);
    this.Name = "Demo";
    this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
    this.Load += new System.EventHandler(this.Settings_Load);
    ((System.ComponentModel.ISupportInitialize)(this.MainErrorProvider)).EndInit();
    this.ResumeLayout(false);
    this.PerformLayout();
}
}

public class PortTestResult
{
    public string PortName { get; set; }
    public bool UseThisPort { get; set; }
}
}
A: 

This CodeProject article has great information on customizing the combobox: Article

Stewbob