Search code examples
c#winformscheckboxcombobox

Disabling a ComboBox Item based on a CheckBox state


I have a scenario where I need to disable the specific items of a ComboBox based on the state of a CheckBox.

I have five CheckBoxes and five Items in a ComboBox.

If CheckBox1 is unchecked, Item1 should be disabled and the same applies for all the other ComboBox Items.
I tried with this code, but I was unsuccessful:

private void ComboBox_SelectNode_DrawItem(object sender, DrawItemEventArgs e)
{
    ComboBox comboBox = (ComboBox)sender;
    if (e.Index == 0)
    {
        if (this.isNode0Disabled)
        {
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.LightGray, e.Bounds);
        }
        else
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.Black, e.Bounds);
            e.DrawFocusRectangle();
        }
    }

    if (e.Index == 1)
    {
        if (this.isNode1Disabled)
        {
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.LightGray, e.Bounds);
        }
        else
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.Black, e.Bounds);
            e.DrawFocusRectangle();
        }
    }

    if (e.Index == 2)
    {
        if (this.isNode2Disabled)
        {
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.LightGray, e.Bounds);
        }
        else
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.Black, e.Bounds);
            e.DrawFocusRectangle();
        }
    }

    if (e.Index == 3)
    {
        if (this.isNode3Disabled)
        {
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.LightGray, e.Bounds);
        }
        else
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.Black, e.Bounds);
            e.DrawFocusRectangle();
        }
    }

    if (e.Index == 4)
    {
        if (this.isNode4Disabled)
        {
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.LightGray, e.Bounds);
        }
        else
        {
            e.DrawBackground();
            e.Graphics.DrawString(this.ComboBox_SelectNode.Items[e.Index].ToString(), this.myFont, Brushes.Black, e.Bounds);
            e.DrawFocusRectangle();
        }
    }
}

private void ComboBox_SelectNode_SelectedIndexChanged(object sender, EventArgs e)
{
    if (this.ComboBox_SelectNode.SelectedIndex == 0)
    {
        if (this.isNode0Disabled)
        {
            this.ComboBox_SelectNode.SelectedIndex = -1;
        }
    }

    if (this.ComboBox_SelectNode.SelectedIndex == 1)
    {
        if (this.isNode1Disabled)
        {
            this.ComboBox_SelectNode.SelectedIndex = -1;
        }
    }

    if (this.ComboBox_SelectNode.SelectedIndex == 2)
    {
        if (this.isNode2Disabled)
        {
            this.ComboBox_SelectNode.SelectedIndex = -1;
        }
    }

    if (this.ComboBox_SelectNode.SelectedIndex == 3)
    {
        if (this.isNode3Disabled)
        {
            this.ComboBox_SelectNode.SelectedIndex = -1;
        }
    }

    if (this.ComboBox_SelectNode.SelectedIndex == 4)
    {
        if (this.isNode4Disabled)
        {
            this.ComboBox_SelectNode.SelectedIndex = -1;
        }
    }
}

private void CheckBox_Node0_CheckedChanged(object sender, EventArgs e)
{
    if (this.checkBox_Node0.Checked == true)
    {
        this.isNode0Disabled = false;
    }

    if (this.checkBox_Node0.Checked == false)
    {
        this.isNode0Disabled = true;
    }
}

Modified Code:

private void ComboBox_SelectNode_DrawItem(object sender, DrawItemEventArgs e)
{
    TextFormatFlags comboTRFlags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
    if (e.Index < 0)
    {
        return;
    }

    var combo = sender as ComboBox;
    bool isCheckBoxChecked = this.comboCheckBoxes[e.Index].Checked;
    if (isCheckBoxChecked)
    {
        e.DrawBackground();
    }
    else
    {
        using (var brush = new SolidBrush(combo.BackColor))
        {
            e.Graphics.FillRectangle(brush, e.Bounds);
        }
    }

    string itemText = combo.GetItemText(combo.Items[e.Index]);
    Color textColor = isCheckBoxChecked ? e.ForeColor : SystemColors.GrayText;
    TextRenderer.DrawText(e.Graphics, itemText, combo.Font, e.Bounds, textColor, comboTRFlags);
}

private void ComboBox_SelectNode_MeasureItem(object sender, MeasureItemEventArgs e)
=> e.ItemHeight = this.ComboBox_SelectNode.Font.Height + 4;

And declared the event as follows:

this.ComboBox_SelectNode.DrawItem += new DrawItemEventHandler(ComboBox_SelectNode_DrawItem);
this.ComboBox_SelectNode.MeasureItem += new MeasureItemEventHandler(ComboBox_SelectNode_MeasureItem);

private CheckBox[] comboCheckBoxes;
this.comboCheckBoxes = new[] {this.checkBox_Node0, this.checkBox_Node1, this.checkBox_Node2, this.checkBox_Node3 , this.checkBox_Node4};

But when I run the application, these events are not firing.


Solution

  • You'ld probably make your life easier (and the code easier to manage) if you add your CheckBoxes to a collection, so you can verify the status of a CheckBox retrieving it from the collection, using the Index of the paired ComboBox Item.
    Something like this, using an array of CheckBox Controls:
    (Of course, adapt the CheckBoxes names to your design)

    private CheckBox[] comboCheckBoxes;
    public form1()
    {
        InitializeComponent();
        comboCheckBoxes = new[] { chkFirst, chkSecond, chkThird, chkFourth, chkLast};
    }
    

    You can then select the style of each Item's text before drawing it.

    I'm using TextRenderer.DrawText to draw the Items' text: the text alignment is rendered correctly using this method instead of Graphics.DrawString().

    TextFormatFlags comboTRFlags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
    
    private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
    {
        if (e.Index < 0) return;
        var combo = sender as ComboBox;
        bool isCheckBoxChecked = comboCheckBoxes[e.Index].Checked;
        if (isCheckBoxChecked) {
            e.DrawBackground();
        }
        else {
            using (var brush = new SolidBrush(combo.BackColor)) {
                e.Graphics.FillRectangle(brush, e.Bounds);
            }
        }
        string itemText = combo.GetItemText(combo.Items[e.Index]);
        Color textColor = isCheckBoxChecked ? e.ForeColor : SystemColors.GrayText;
        TextRenderer.DrawText(e.Graphics, itemText, combo.Font, e.Bounds, textColor, comboTRFlags);
    }
    
    private void comboBox1_MeasureItem(object sender, MeasureItemEventArgs e) 
        => e.ItemHeight = comboBox1.Font.Height + 4;
    
    private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
    {
        if (!comboCheckBoxes[comboBox1.SelectedIndex].Checked) {
            comboBox1.SelectedIndex = -1;
            return;
        }
    }
    

    This is how it works:

    CheckBox ComboBox Item enable disable