We have a Custom ComboBox working on a form that displays some shapes instead of text. To do that all I had to do was to override the OnDrawItem function and it displays what we want. Here is a snippet for reference:
protected override void OnDrawItem(DrawItemEventArgs e)
{
base.OnDrawItem(e);
e.DrawBackground();
if (e.Index >= 0)
{
Brush brush = new SolidBrush(Color.LightGray);
int size = this.Height/2;
int origenX = e.Bounds.X + 1;
int origenY = e.Bounds.Y + 3;
System.Drawing.Drawing2D.GraphicsPath path =
new System.Drawing.Drawing2D.GraphicsPath();
switch (e.Index)
{
case 0:
e.Graphics.FillRectangle(brush, origenX, origenY, size, size);
Rectangle r = new Rectangle(origenX, origenY, size, size);
ControlPaint.DrawBorder(e.Graphics, r, Color.Black,
ButtonBorderStyle.Solid);
break;
case 1:
path.AddEllipse(origenX, origenY, size, size);
e.Graphics.FillPath(brush, path);
e.Graphics.DrawPath(Pens.Black, path);
break;
}
}
}
So, if you add that to a form and add a couple of items to your collection all you see is a square and a circle in the drop down.
Ok, so, what I want to do now is add this same combo box to a DataGridView. I know that this control has a DataGridViewComboBoxColumn. I was trying to extend the control, however, I don't see this OnDrawItem function to override. I guess there exists something similar?
Any help would be appreciated.
Thanks!
You need to interperet the DataGridViewComboBox as your custom Combobox.
private void dgTest_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dgTest.CurrentCell.ColumnIndex == 0) // Which column ever is your DataGridComboBoxColumn
{
// This line will enable you to use the DataDridViewCOmboBox like your
// Custom ComboBox.
CustomComboBox combo = e.Control as CUstomComboBox;
}
}
Related
i want coloring each box(not text items) in combobox which have different items, example: mountain have it's box red, sea have it's box blue, etc. i've tried but it's really jumbled bunch of color which i've tried
i need to highlight every combobox items to show it's intended color, when clicking that combobox it's not showing its intended color
code below
private void cmbRegion_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
string text = ((ComboBox)sender).Items[e.Index].ToString();
Brush brush;
if (text.Equals("Mountain"))
{
brush = Brushes.Black;
cmbRegion.BackColor = Color.Red;
}
else if (text.Equals("Sea"))
{
brush = Brushes.Black;
cmbRegion.BackColor = Color.Blue;
}
else
{
brush = Brushes.Black;
cmbRegion.BackColor = Color.White;
}
// Draw the text
e.Graphics.DrawString(text, ((Control)sender).Font, brush, e.Bounds.X, e.Bounds.Y);
}
can it be done..?
Modified MSDN example to match your requirement. The important property here is the DrawMode.
private void Form1_Load(object sender, EventArgs e)
{
InitializeComboBox();
}
internal ComboBox ComboBox1;
private string[] items;
// This method initializes the owner-drawn combo box.
// The drop-down width is set much wider than the size of the combo box
// to accomodate the large items in the list. The drop-down style is set to
// ComboBox.DropDown, which requires the user to click on the arrow to
// see the list.
private void InitializeComboBox()
{
this.ComboBox1 = new ComboBox();
this.ComboBox1.DrawMode =
System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.ComboBox1.BackColor = Color.Red;
this.ComboBox1.Location = new System.Drawing.Point(10, 20);
this.ComboBox1.Name = "ComboBox1";
this.ComboBox1.Size = new System.Drawing.Size(100, 120);
this.ComboBox1.DropDownWidth = 250;
this.ComboBox1.TabIndex = 0;
this.ComboBox1.DropDownStyle = ComboBoxStyle.DropDown;
items = new string[] { "Mountain", "Sea", "Other" };
ComboBox1.DataSource = items;
this.Controls.Add(this.ComboBox1);
// Hook up the MeasureItem and DrawItem events
this.ComboBox1.DrawItem +=
new DrawItemEventHandler(ComboBox1_DrawItem);
this.ComboBox1.MeasureItem +=
new MeasureItemEventHandler(ComboBox1_MeasureItem);
this.ComboBox1.SelectedIndexChanged += ComboBox1_SelectedIndexChanged;
}
private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Color itemBackgroundColor = new Color();
switch (ComboBox1.SelectedIndex)
{
case 0:
itemBackgroundColor = Color.Red;
break;
case 1:
itemBackgroundColor = Color.Blue;
break;
case 2:
itemBackgroundColor = Color.Green;
break;
}
ComboBox1.BackColor = itemBackgroundColor;
}
// If you set the Draw property to DrawMode.OwnerDrawVariable,
// you must handle the MeasureItem event. This event handler
private void ComboBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
// No change to item height.
e.ItemHeight = e.ItemHeight;
}
// You must handle the DrawItem event for owner-drawn combo boxes.
// This event handler changes the color, size and font of an
// item based on its position in the array.
private void ComboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
string text = ((ComboBox)sender).Items[e.Index].ToString();
Color itemBackgroundColor = new Color();
switch (e.Index)
{
case 0:
itemBackgroundColor = Color.Red;
break;
case 1:
itemBackgroundColor = Color.Blue;
break;
case 2:
itemBackgroundColor = Color.Green;
break;
}
// Draw the background of the item.
e.DrawBackground();
// Create a square filled with the animals color. Vary the size
// of the rectangle based on the length of the animals name.
e.Graphics.FillRectangle(new SolidBrush(itemBackgroundColor), e.Bounds);
// Draw each string in the array, using a different size, color,
// and font for each item.
e.Graphics.DrawString(items[e.Index], e.Font, Brushes.Black, e.Bounds);
// Draw the focus rectangle if the mouse hovers over an item.
e.DrawFocusRectangle();
}
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);
}
}
}
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);
}
}
}
I have developed the following custom combo box to increase the height of the items. Once that is done, a blank space appears at the end of the drop down menu when there is a scrollbar.
How can I correct the issue?
class MyComboBoxXX : ComboBox
{
public MyComboBoxXX():base()
{
this.DrawMode = DrawMode.OwnerDrawVariable;
this.DropDownStyle = ComboBoxStyle.DropDownList;
this.MaxDropDownItems = 5;
this.IntegralHeight = false;
}
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
e.ItemHeight = 40;
this.DropDownHeight = 40 * 5;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
var index = e.Index;
if (index < 0 || index >= Items.Count) return;
using (var brush = new SolidBrush(e.ForeColor))
{
Rectangle rec = new Rectangle(e.Bounds.Left, e.Bounds.Top + ((e.Bounds.Height - ItemHeight) / 2), e.Bounds.Width, ItemHeight);
e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, new SolidBrush(this.ForeColor), rec);
}
e.DrawFocusRectangle();
}
}
If you look closely, it appears that the DropDown area has a 1 pixel border at the top, and also at the bottom. You can get rid of the space by adding 2 pixels to the DropDownHeight.
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
e.ItemHeight = 40;
this.DropDownHeight = (40 * 5) + 2; //add 2 pixels to include the border
}
Result:
I think you should reduce the DropDownHeight value to the appropriate number of items if it's less than the max number in your OnMeasureItem method override.
Need to store off the default ItemHeight since changing the DrawMode from Normal causes padding of 2 pixels to be added to the item height. Retrieving the Handle is required prior to the call to ItemHeight since it creates the handle. Without this, the value of the ItemHeight property is not correct.
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/ComboBox.cs
i have a listbox and i would want to display a label displaying:
scrolling through items XXX to XYY of ZZZ.
how do I do this because using SelectedIndex will not be useful, as i would like the label to update even when nothing is selected. (scrolling too, it does not select an item).
update:
for example I have 200 items in my listbox. at any one time i can only display only 10 items because of my listbox's height. so the label should read:
displaying items 1 to 10 of 200
or
displaying items 5 to 15 of 200
however i must take into account that there may not be any indices selected because i can simply scroll and not select anything.
You can get the top index value using listbox.TopIndex and the count using listbox.Items.Count but I can't see any way to get the bottom item withotu calculating it from the result of listbox.GetItemHeight() and listbox.ClientSize.Height:
int visibleCount = listBox1.ClientSize.Height / listBox1.ItemHeight;
this.Text = string.Format("{0:d} to {1:d} of {2:d}", listBox1.TopIndex + 1, listBox1.TopIndex + visibleCount, listBox1.Items.Count);
This can be done on a timer as I see no scroll event.
Just use the Scroll event of the ListBox. Oh wait, there isn't one. You can add one:
public class ListBoxEx : ListBox {
public event EventHandler Scrolling;
private const int WM_VSCROLL = 0x0115;
private void OnScrolling() {
if (Scrolling != null)
Scrolling(this, new EventArgs());
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (m.Msg == WM_VSCROLL)
OnScrolling();
}
}
Once you use this, it's just math (refactor as needed):
private void listBoxEx1_Resize(object sender, EventArgs e) {
DisplayRange();
}
private void listBoxEx1_Scrolling(object sender, EventArgs e) {
DisplayRange();
}
private void DisplayRange() {
int numFrom = listBoxEx1.TopIndex + 1;
int numTo = numFrom + (listBoxEx1.ClientSize.Height / listBoxEx1.ItemHeight) - 1;
this.Text = numFrom.ToString() + " to " + numTo.ToString();
}
If IntegralHeight=False then you might have to play with the range number to determine whether or not to include partial rows or not.
If using DrawMode=OwnerDrawVariable, then you need to loop through the visible rows with the MeasureItem event.
This is a Draw, you can do this
private int min = 1000;
private int max = 0;
private void comboBox3_DrawItem(object sender, DrawItemEventArgs e)
{
if (min >= e.Index) min = e.Index+1;
if (max <= e.Index) max = e.Index+1;
float size = 10;
System.Drawing.Font myFont;
FontFamily family = FontFamily.GenericSansSerif;
System.Drawing.Color animalColor = System.Drawing.Color.Black;
e.DrawBackground();
Rectangle rectangle = new Rectangle(2, e.Bounds.Top + 2,
e.Bounds.Height, e.Bounds.Height - 4);
e.Graphics.FillRectangle(new SolidBrush(animalColor), rectangle);
myFont = new Font(family, size, FontStyle.Bold);
e.Graphics.DrawString(comboBox3.Items[e.Index].ToString(), myFont, System.Drawing.Brushes.Black, new RectangleF(e.Bounds.X + rectangle.Width, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
e.DrawFocusRectangle();
label1.Text = String.Format("Values between {0} and {1}",min,max);
}
you have to find the right event to reset min and max values and the right values to redraw the combobox.
This code is not a solution, it's only an idea of how you can implement your requirement.
best regards.