How to dynamically change / set checkedListBox item fore colour
Asked Answered
T

5

13

I have code below. How can i set checkedListBox item fore colour depending on if item is checked or not checked?

private void FindSelectedUserRoles()
{
        lblSelectedUser.Text = Code.CommonUtilities.getDgvStringColValue(dataGridViewUserList, "UserName").Trim();

        //iterate all roles selected user is member of
        for (int i = 0; i < checkedListRoles.Items.Count; i++)
        {
            string roleName = checkedListRoles.Items[i].ToString();
            string selectedUserRoles = Code.MemberShipManager.GetSpecificUsersRoles(lblSelectedUser.Text.Trim());

            if (selectedUserRoles.Contains(roleName))
            {
                checkedListRoles.SetItemChecked(i, true);
                //here i want to set item fore colour to green

            }
            else if (selectedUserRoles.Contains(roleName) == false)
            {
                checkedListRoles.SetItemChecked(i, false);
                //and here, i want item fore colour to remain black
            }
        }
}
Timepleaser answered 11/7, 2013 at 8:15 Comment(0)
B
6

I think you have to draw your own CheckedListBox item like this:

public class CustomCheckedListBox : CheckedListBox
{
    public CustomCheckedListBox()
    {
        DoubleBuffered = true;
    }
    protected override void OnDrawItem(DrawItemEventArgs e)
    {            
        Size checkSize = CheckBoxRenderer.GetGlyphSize(e.Graphics, System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal);
        int dx = (e.Bounds.Height - checkSize.Width)/2;
        e.DrawBackground();
        bool isChecked = GetItemChecked(e.Index);//For some reason e.State doesn't work so we have to do this instead.
        CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(dx, e.Bounds.Top + dx), isChecked ? System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal : System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
        using (StringFormat sf = new StringFormat { LineAlignment = StringAlignment.Center })
        {
            using (Brush brush = new SolidBrush(isChecked ? CheckedItemColor : ForeColor))
            {
                e.Graphics.DrawString(Items[e.Index].ToString(), Font, brush, new Rectangle(e.Bounds.Height, e.Bounds.Top, e.Bounds.Width - e.Bounds.Height, e.Bounds.Height), sf);
            }
        }            
    }
    Color checkedItemColor = Color.Green;
    public Color CheckedItemColor
    {
        get { return checkedItemColor; }
        set
        {
            checkedItemColor = value;
            Invalidate();
        }
    }
}

If you want to set CheckedColor differently for each item, you have to store the CheckedColor setting for each item (such as in a Collection) and reference the CheckedColor using Index. However I think it's a little much work to do. So if you have such a requirement, going for ListView instead would be better.

Balaklava answered 11/7, 2013 at 8:44 Comment(8)
The line "bool isChecked = GetItemChecked(e.Index);" throws an error for me, if you drag and drop that item into the designer. To solve this you have to add Entreis, then the error isn't thrown anymore (index is -1 otherwise i think);Miculek
@Nerdintraining thanks for your response, I'm not so sure about that. As I understand the e.Index should be valid in the event handler (because the item does exist before the drawing is processed on it). The code here is of course not well tested. I'm also not sure how you could drag and drop a list item on the designer (as far as I remember that's not possible with the standard windows form designer). Your comment is still valuable for others to improve the code. Finally I've not programmed with winforms for years, not really interested in it now. Thanks.Balaklava
wow that was fast reaction^^ after 3 years only 25 minutes reaction time ^-^ Anyways, I offered you an edit :) And do you have any idea how to create a ControllBox that looks like this (or if one exists) linkMiculek
@Nerdintraining if you mean the checkBox in the image from that link, then I would say that it's not easy. The tick looks like hand-drawn. Maybe you need to prepare a transparent tick and try to render (or simply place) it on top of the checkBox, the checkbox's square should also be drawn inside and give a margin around (for the tick mark to be rendered). It becomes not easy when the tick mark can show on top of other controls around, meaning you can see all controls behind through the bounds of the tick mark. You can search for custom control related to transparent bground.Balaklava
As I said I've not programmed with Winforms for years, so I cannot help you more. The next UI tech you should follow is WPF :) it's really cool once you want to build rich UI, besides that it changes the way you program a Windows app significantly.Balaklava
haha ya, I've heard allot of good things for WPF and i will have a look into that when i get around to do it :)Miculek
@Nerdintraining'questionmark' It also shows me error when dragging the control from the designer look at the image, I do not understand what you propose here: To solve this you have to add Entreis, How do I solve the problem? Please.Taitaichung
@J.Rodríguez I've provided an edit that King King has to confirm. I will just quickly copy paste the solution in here if he again rejects the eddit for some weird reason: "If you see a System.ArgumentOutOfRangeException in the CheckedListBox, then just add a dummy Item to the CheckedListBox and rebuild. You can do this by clicking on the CheckedListBox in the designer and then edit the Items property in the properties window."Miculek
C
11

Since it's rather complicated to draw the thing yourself, you could actually let the original control draw itself -- just tweaking the color. This is my suggestion:

public class CustomCheckedListBox : CheckedListBox
{
    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        Color foreColor;
        if (e.Index >= 0)
        {
            foreColor = GetItemChecked(e.Index) ? Color.Green : Color.Red;
        }
        else
        {
            foreColor = e.ForeColor;
        }

        // Copy the original event args, just tweaking the fore color.
        var tweakedEventArgs = new DrawItemEventArgs(
            e.Graphics,
            e.Font,
            e.Bounds,
            e.Index,
            e.State,
            foreColor,
            e.BackColor);

        // Call the original OnDrawItem, but supply the tweaked color.
        base.OnDrawItem(tweakedEventArgs);
    }
}
Carbone answered 1/12, 2016 at 11:2 Comment(1)
This seems like a much simpler approach - nice!Transference
L
6

I think you should try ListView instead of checkedListBox. It has necessary properties and could be customized as you wish. Just set Checkboxes property to true, and then in your code add forecolor like that:

listView1.Items[i].ForeColor = Color.Red;
Lille answered 11/7, 2013 at 8:43 Comment(0)
B
6

I think you have to draw your own CheckedListBox item like this:

public class CustomCheckedListBox : CheckedListBox
{
    public CustomCheckedListBox()
    {
        DoubleBuffered = true;
    }
    protected override void OnDrawItem(DrawItemEventArgs e)
    {            
        Size checkSize = CheckBoxRenderer.GetGlyphSize(e.Graphics, System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal);
        int dx = (e.Bounds.Height - checkSize.Width)/2;
        e.DrawBackground();
        bool isChecked = GetItemChecked(e.Index);//For some reason e.State doesn't work so we have to do this instead.
        CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(dx, e.Bounds.Top + dx), isChecked ? System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal : System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
        using (StringFormat sf = new StringFormat { LineAlignment = StringAlignment.Center })
        {
            using (Brush brush = new SolidBrush(isChecked ? CheckedItemColor : ForeColor))
            {
                e.Graphics.DrawString(Items[e.Index].ToString(), Font, brush, new Rectangle(e.Bounds.Height, e.Bounds.Top, e.Bounds.Width - e.Bounds.Height, e.Bounds.Height), sf);
            }
        }            
    }
    Color checkedItemColor = Color.Green;
    public Color CheckedItemColor
    {
        get { return checkedItemColor; }
        set
        {
            checkedItemColor = value;
            Invalidate();
        }
    }
}

If you want to set CheckedColor differently for each item, you have to store the CheckedColor setting for each item (such as in a Collection) and reference the CheckedColor using Index. However I think it's a little much work to do. So if you have such a requirement, going for ListView instead would be better.

Balaklava answered 11/7, 2013 at 8:44 Comment(8)
The line "bool isChecked = GetItemChecked(e.Index);" throws an error for me, if you drag and drop that item into the designer. To solve this you have to add Entreis, then the error isn't thrown anymore (index is -1 otherwise i think);Miculek
@Nerdintraining thanks for your response, I'm not so sure about that. As I understand the e.Index should be valid in the event handler (because the item does exist before the drawing is processed on it). The code here is of course not well tested. I'm also not sure how you could drag and drop a list item on the designer (as far as I remember that's not possible with the standard windows form designer). Your comment is still valuable for others to improve the code. Finally I've not programmed with winforms for years, not really interested in it now. Thanks.Balaklava
wow that was fast reaction^^ after 3 years only 25 minutes reaction time ^-^ Anyways, I offered you an edit :) And do you have any idea how to create a ControllBox that looks like this (or if one exists) linkMiculek
@Nerdintraining if you mean the checkBox in the image from that link, then I would say that it's not easy. The tick looks like hand-drawn. Maybe you need to prepare a transparent tick and try to render (or simply place) it on top of the checkBox, the checkbox's square should also be drawn inside and give a margin around (for the tick mark to be rendered). It becomes not easy when the tick mark can show on top of other controls around, meaning you can see all controls behind through the bounds of the tick mark. You can search for custom control related to transparent bground.Balaklava
As I said I've not programmed with Winforms for years, so I cannot help you more. The next UI tech you should follow is WPF :) it's really cool once you want to build rich UI, besides that it changes the way you program a Windows app significantly.Balaklava
haha ya, I've heard allot of good things for WPF and i will have a look into that when i get around to do it :)Miculek
@Nerdintraining'questionmark' It also shows me error when dragging the control from the designer look at the image, I do not understand what you propose here: To solve this you have to add Entreis, How do I solve the problem? Please.Taitaichung
@J.Rodríguez I've provided an edit that King King has to confirm. I will just quickly copy paste the solution in here if he again rejects the eddit for some weird reason: "If you see a System.ArgumentOutOfRangeException in the CheckedListBox, then just add a dummy Item to the CheckedListBox and rebuild. You can do this by clicking on the CheckedListBox in the designer and then edit the Items property in the properties window."Miculek
T
1

Expanding on @Mattias' answer, I made this custom control to fit my needs. I needed it to have colors depending on other factors than the Checked value.

public class CheckedListBoxColorable : CheckedListBox
{
    /// <summary>
    /// Controls the forecolors of the objects in the Items collection.
    /// If the item is not represented, it will have the default forecolor.
    /// </summary>
    public Dictionary<object, Color> Colors { get; set; }

    public CheckedListBoxColorable()
    {
        this.DoubleBuffered = true; //prevent flicker, not sure if this is necessary.
    }

    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        //Default forecolor
        Color foreColor = e.ForeColor;

        //Item to be drawn
        object item = null;

        if (e.Index >= 0) //If index is -1, no customization is necessary
        {
            //Find the item to be drawn
            if (this.Items.Count > e.Index) item = this.Items[e.Index];

            //If the item was found and we have a color for it, get the custom forecolor
            if (item != null && this.Colors != null && this.Colors.ContainsKey(item))
            {
                foreColor = this.Colors[item];
            }
        }

        // Copy the original event args, just tweaking the forecolor.
        var tweakedEventArgs = new DrawItemEventArgs(
            e.Graphics,
            e.Font,
            e.Bounds,
            e.Index,
            e.State,
            foreColor,
            e.BackColor);

        // Call the original OnDrawItem, but supply the tweaked color.
        base.OnDrawItem(tweakedEventArgs);
    }
}

Usage:

//Set the colors I want for my objects
foreach (var obj in objects)
{
    //Add your own logic here to set the color depending on whatever criteria you have
    if (obj.SomeProperty) lstBoxes.Colors.Add(obj, Color.Green);
    else lstBoxes.Colors.Add(obj, Color.Red);
}
//Add the items to the checkedlistbox
lstBoxes.Items.AddRange(objects.ToArray());
Transference answered 4/9, 2018 at 14:1 Comment(0)
G
0

The accepted answer worked for me but it needs modifying if you want to disable the CustomCheckedListBox.

I modified the code as follows: -

I changed the 'CheckBoxRenderer.DrawCheckBox...' line to

if(Enabled)
{
    CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(dx, e.Bounds.Top + dx), isChecked ? System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal : System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
}
else
{
    CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point(dx, e.Bounds.Top + dx), isChecked ? System.Windows.Forms.VisualStyles.CheckBoxState.CheckedDisabled : System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedDisabled);
}

and then I changed the 'using (Brush brush = new SolidBrush...' line to

using (Brush brush = new SolidBrush(isChecked ? CheckedItemColor : (Enabled ? ForeColor : SystemColors.GrayText)))

This caused enabling/disabling to work for me.

Genesia answered 2/11, 2018 at 10:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.