How to dynamically change / set checkedListBox item fore colour - c#

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
}
}
}

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);
}
}

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.

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;

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());

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.

Related

c# custom listbox item without word wrap using horizontal scroll

So I made a custom listBox in C# windows forms. But it is wrapping the text it holds instead of showing a horizontal scroll bar which is what I want.
The code below is for the listbox:
public class MyList : ListBox
{
public MyList()
{
base.ItemHeight = 20;
base.DrawMode = DrawMode.OwnerDrawFixed;
HorizontalScrollbar = true;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
if (e.State == DrawItemState.Focus)
e.DrawFocusRectangle();
int index = e.Index;
if (index < 0 || index >= Items.Count) return;
var item = Items[index];
string text = (item == null) ? "(null)" : item.ToString();
e.DrawBackground();
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(Color.Transparent), e.Bounds);
using (var brush = new SolidBrush(e.ForeColor))
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
e.Graphics.DrawString(text, e.Font, brush, e.Bounds);
}
}
}
I am pretty certain it has something to do with the e.Bounds but I am not sure how to set an "infinite" value and to enable scrolling.
EDIT: In the constructor, I do have HorizontalScrollbar = true but it still doesn't show it. Again I think I need to modify the e.Bounds
Thanks all.
Okay. Figured it out.
You must measure the string length of the item the listbox holds then compare that to the listBox.HorizontalExtent and if it is greater, make that value the new HorizontalExtent. For drawing the text without it wrapping, you simply do not pass a width parameter to it. Just a position.
Code:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.State == DrawItemState.Focus)
e.DrawFocusRectangle();
int index = e.Index;
if (index < 0 || index >= listBox1.Items.Count) return;
var item = listBox1.Items[index];
string text = (item == null) ? "(null)" : item.ToString();
int newHE = (int)(e.Graphics.MeasureString(text, e.Font).Width + 2);
if (listBox1.HorizontalExtent < newHE)
{
listBox1.HorizontalExtent = newHE;
}
using (var brush = new SolidBrush(e.ForeColor))
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
e.Graphics.DrawString(text, e.Font, brush, 0f, e.Bounds.Top + 3); //I do +3 because I wanted to shimmy the text down a bit from the top
}
}

Show a Label with semi-transparent BackColor above other controls?

I've got a custom control with two PictureBox controls that are animated and a label control over them.
The child indexes are set so that label is always on top but the picture boxes are interchanging so when animated they display different images each time.
As I understand, label needs to have a parent control on top of which it can support a semi transparent color (Argb). Since the label has active picture box as its parent it will also be animated with which is not what I want at all.
Is there a way to fix a child position relative to parents parent?
To have a transparent label control, you can override the OnPaint method and draw all controls that intersects with label, at last draw the background and text of the label.
Also when moving your picture boxes, don't forget to call the Invalidate() method of the transparent label.
Screenshot
Sample Implementation
public class TransparentLabel : Label
{
public TransparentLabel()
{
this.transparentBackColor = Color.Blue;
this.opacity = 50;
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
using (var b = new SolidBrush(Color.FromArgb(this.Opacity, this.TransparentBackColor)))
{
e.Graphics.FillRectangle(b, this.ClientRectangle);
}
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
}
}
}
private int opacity;
public int Opacity
{
get { return opacity; }
set
{
if (value >= 0 && value <= 255)
opacity = value;
this.Invalidate();
}
}
public Color transparentBackColor;
public Color TransparentBackColor
{
get { return transparentBackColor; }
set
{
transparentBackColor = value;
this.Invalidate();
}
}
[Browsable(false)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
base.BackColor = Color.Transparent;
}
}
}

Change ListBox item spacing

I have this interface:
What I want to do is align or space the names on the left that are in a ListBox with the grid on the right so that each name is inline with each grid row.
I did try this:
lstNames.ItemHeight = 15;
But this does not effect it. Note: My listbox is created dynamically and populated using a database.
Any Tips on how to achieve this?
You have to change DrawMode property to OwnerDrawFixed to use custom ItemHeight.
When you use DrawMode.OwnerDrawFixed you have to paint/draw items "manually".
Referenced from Max of this Stackoverflow posting Combobox appearance
public class ComboBoxEx : ComboBox
{
public ComboBoxEx()
{
base.DropDownStyle = ComboBoxStyle.DropDownList;
base.DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
if(e.State == DrawItemState.Focus)
e.DrawFocusRectangle();
var index = e.Index;
if(index < 0 || index >= Items.Count) return;
var item = Items[index];
string text = (item == null)?"(null)":item.ToString();
using(var brush = new SolidBrush(e.ForeColor))
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
e.Graphics.DrawString(text, e.Font, brush, e.Bounds);
}
}
}

How to add padding between items in a listbox?

I'm wondering if there's a way to add padding between my line items. It's a form intended to be used on a tablet, and space between each one would make it easier to select different items.
Anyone know how I can do this?
There is an ItemHeight property.
You have to change DrawMode property to OwnerDrawFixed to use custom ItemHeight.
When you use DrawMode.OwnerDrawFixed you have to paint/draw items "manually".
Here is an example: Combobox appearance
Code from link above (written/provided by max):
public class ComboBoxEx : ComboBox
{
public ComboBoxEx()
{
base.DropDownStyle = ComboBoxStyle.DropDownList;
base.DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
if(e.State == DrawItemState.Focus)
e.DrawFocusRectangle();
var index = e.Index;
if(index < 0 || index >= Items.Count) return;
var item = Items[index];
string text = (item == null)?"(null)":item.ToString();
using(var brush = new SolidBrush(e.ForeColor))
{
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
e.Graphics.DrawString(text, e.Font, brush, e.Bounds);
}
}
}

Dynamically Changing Features of an Overridden TreeView Class

I am overriding a TreeView so that I can highlight the nodes using better colors. As part of the applications options I want to enable the user to change the TreeView's font and the font color when both selected and deselected. The code is below:
class MyTreeView : TreeView
{
// Create a Font object for the node tags and HotTracking.
private Font hotFont;
private Font tagFont = new Font("Helvetica", Convert.ToSingle(8.0), FontStyle.Bold);
#region Accessors.
public Font hotTrackFont
{
get { return this.hotFont; }
set { this.hotFont = value; }
}
//public string unFocusedColor
//{
// get { return this.strDeselectedColor; }
// set { this.strDeselectedColor = value; }
//}
//public string focusedColor
//{
// get { return this.strSelectedColor; }
// set { this.strSelectedColor = value; }
//}
#endregion
public MyTreeView()
{
this.HotTracking = true;
this.DrawMode = TreeViewDrawMode.OwnerDrawText;
hotFont = new Font(this.Font.FontFamily, this.Font.Size, FontStyle.Underline);
}
// Override the drawMode of TreeView.
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
TreeNodeStates treeState = e.State;
Font treeFont = e.Node.NodeFont ?? e.Node.TreeView.Font;
// Colors.
Color foreColor = e.Node.ForeColor;
// Like with the hotFont I want to be able to change these dynamically...
string strDeselectedColor = #"#6B6E77", strSelectedColor = #"#94C7FC";
Color selectedColor = System.Drawing.ColorTranslator.FromHtml(strSelectedColor);
Color deselectedColor = System.Drawing.ColorTranslator.FromHtml(strDeselectedColor);
// New brush.
SolidBrush selectedTreeBrush = new SolidBrush(selectedColor);
SolidBrush deselectedTreeBrush = new SolidBrush(deselectedColor);
// Set default font color.
if (foreColor == Color.Empty)
foreColor = e.Node.TreeView.ForeColor;
// Draw bounding box and fill.
if (e.Node == e.Node.TreeView.SelectedNode)
{
// Use appropriate brush depending on if the tree has focus.
if (this.Focused)
{
foreColor = SystemColors.HighlightText;
e.Graphics.FillRectangle(selectedTreeBrush, e.Bounds);
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
else
{
foreColor = SystemColors.HighlightText;
e.Graphics.FillRectangle(deselectedTreeBrush, e.Bounds);
ControlPaint.DrawFocusRectangle(e.Graphics, e.Bounds, foreColor, SystemColors.Highlight);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
}
else
{
if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, hotFont, e.Bounds,
System.Drawing.Color.Black, TextFormatFlags.GlyphOverhangPadding);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, treeFont, e.Bounds,
foreColor, TextFormatFlags.GlyphOverhangPadding);
}
}
}
}
As you can see I am currently changing the Font hotFont using an accessor to the class which seems to work. However, when I try to edit the colors of the focused/unfocused node, VS2010 crashes! What is it that is causing this behaviour exactly and how can I achieve what I want?
The posted code does not recreate the error when I enabled those commented out properties and added the variables to the control's scope.
A couple of notes though:
Instead of string properties, I would use an actual color:
private Color _UnfocusedColor = ColorTranslator.FromHtml(#"#94C7FC");
private Color _FocusedColor = ColorTranslator.FromHtml(#"#6B6E77");
public Color UnfocusedColor
{
get { return _UnfocusedColor; }
set { _UnfocusedColor = value; }
}
public Color FocusedColor
{
get { return _FocusedColor; }
set { _FocusedColor = value; }
}
Also, make sure to dispose of your drawing objects, such as the SolidBrush objects, or wrap them up in a Using(...){} block.

Categories

Resources