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);
}
}
}
Related
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
}
}
I am using a ComboBox in Text and DropDown mode (the default) and I want to have an ItemHeight of X (e.g. 40) but have the ComboBox's Height set to Y (e.g. 20).
The reason for this is that I am intending to use the ComboBox for a Quick Search feature in which the user keys in text and detailed results are rendered in the list items. Only one line of input is required.
Unfortunately, Winforms automatically locks the ComboBox's Height to the ItemHeight and I can see no way to change this.
How can I make the ComboBox's Height differ to the ItemHeight?
What you have to do is, first of all, change DrawMode from Normal to OwnerDrawVariable. Then you've got to handle 2 events: DrawItem and MeasureItem . They would be something like:
private void comboBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
e.ItemHeight = 40; //Change this to your desired item height
}
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
ComboBox box = sender as ComboBox;
if (Object.ReferenceEquals(null, box))
return;
e.DrawBackground();
if (e.Index >= 0)
{
Graphics g = e.Graphics;
using (Brush brush = ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
? new SolidBrush(SystemColors.Highlight)
: new SolidBrush(e.BackColor))
{
using (Brush textBrush = new SolidBrush(e.ForeColor))
{
g.FillRectangle(brush, e.Bounds);
g.DrawString(box.Items[e.Index].ToString(),
e.Font,
textBrush,
e.Bounds,
StringFormat.GenericDefault);
}
}
}
e.DrawFocusRectangle();
}
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.
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 can I set the background color of a specific item in a System.Windows.Forms.ListBox?
I would like to be able to set multiple ones if possible.
Thanks for the answer by Grad van Horck. It guided me in the correct direction.
To support text (not just background color), here is my fully working code:
//global brushes with ordinary/selected colors
private SolidBrush reportsForegroundBrushSelected = new SolidBrush(Color.White);
private SolidBrush reportsForegroundBrush = new SolidBrush(Color.Black);
private SolidBrush reportsBackgroundBrushSelected = new SolidBrush(Color.FromKnownColor(KnownColor.Highlight));
private SolidBrush reportsBackgroundBrush1 = new SolidBrush(Color.White);
private SolidBrush reportsBackgroundBrush2 = new SolidBrush(Color.Gray);
//custom method to draw the items, don't forget to set DrawMode of the ListBox to OwnerDrawFixed
private void lbReports_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
bool selected = ((e.State & DrawItemState.Selected) == DrawItemState.Selected);
int index = e.Index;
if (index >= 0 && index < lbReports.Items.Count)
{
string text = lbReports.Items[index].ToString();
Graphics g = e.Graphics;
//background:
SolidBrush backgroundBrush;
if (selected)
backgroundBrush = reportsBackgroundBrushSelected;
else if ((index % 2) == 0)
backgroundBrush = reportsBackgroundBrush1;
else
backgroundBrush = reportsBackgroundBrush2;
g.FillRectangle(backgroundBrush, e.Bounds);
//text:
SolidBrush foregroundBrush = (selected) ? reportsForegroundBrushSelected : reportsForegroundBrush;
g.DrawString(text, e.Font, foregroundBrush, lbReports.GetItemRectangle(index).Location);
}
e.DrawFocusRectangle();
}
The above adds to the given code and will show the proper text plus highlight the selected item.
Probably the only way to accomplish that is to draw the items yourself.
Set the DrawMode to OwnerDrawFixed and code something like this on the DrawItem event:
private void listBox_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(Color.Silver), e.Bounds);
// Print text
e.DrawFocusRectangle();
}
The second option would be using a ListView, although they have an other way of implementations (not really data bound, but more flexible in way of columns).
// Set the background to a predefined colour
MyListBox.BackColor = Color.Red;
// OR: Set parts of a color.
MyListBox.BackColor.R = 255;
MyListBox.BackColor.G = 0;
MyListBox.BackColor.B = 0;
If what you mean by setting multiple background colors is setting a different background color for each item, this isn't possible with a ListBox, but it is with a ListView, with something like:
// Set the background of the first item in the list
MyListView.Items[0].BackColor = Color.Red;
public MainForm()
{
InitializeComponent();
this.listbox1.DrawItem += new DrawItemEventHandler(this.listbox1_DrawItem);
}
private void listbox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
e.DrawBackground();
Brush myBrush = Brushes.Black;
var item = listbox1.Items[e.Index];
if(e.Index % 2 == 0)
{
e.Graphics.FillRectangle(new SolidBrush(Color.Gold), e.Bounds);
}
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
e.Font, myBrush,e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
}