I have a WinForms App and I was wondering if there was a more elegant way of disabling Combobox item without changing the SelectedIndex property -1 for all disabled values.
I have been googling and a lot of the solutions involve ASP.Net DropDownLists but this LINK looks promising. I think I may have to build my own ComboBox control but before I re-invent the wheel I figure I would ask here if it was possible.
UPDATE
Here is the final solution, thanks to Arif Eqbal:
//Add a Combobox to a form and name it comboBox1
//
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
this.comboBox1.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.comboBox1_DrawItem);
this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
}
private void Form1_Load(object sender, EventArgs e)
{
this.comboBox1.Items.Add("Test1");
this.comboBox1.Items.Add("Test2");
this.comboBox1.Items.Add("Test3");
this.comboBox1.Items.Add("Test4");
this.comboBox1.Items.Add("Test5");
this.comboBox1.Items.Add("Test6");
this.comboBox1.Items.Add("Test7");
}
Font myFont = new Font("Aerial", 10, FontStyle.Underline|FontStyle.Regular);
Font myFont2 = new Font("Aerial", 10, FontStyle.Italic|FontStyle.Strikeout);
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == 1 || e.Index == 4 || e.Index == 5)//We are disabling item based on Index, you can have your logic here
{
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont2, Brushes.LightSlateGray, e.Bounds);
}
else
{
e.DrawBackground();
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.Black, e.Bounds);
e.DrawFocusRectangle();
}
}
void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 1 || comboBox1.SelectedIndex == 4 || comboBox1.SelectedIndex == 5)
comboBox1.SelectedIndex = -1;
}
}
}
Try this... Does it serve your purpose:
I assume you have a combobox called ComboBox1 and you want to disable the second item i.e. an item with index 1.
Set the DrawMode property of the combobox to OwnerDrawFixed then handle these two events as shown below:
Font myFont = new Font("Aerial", 10, FontStyle.Regular);
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == 1) //We are disabling item based on Index, you can have your logic here
{
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.LightGray, e.Bounds);
}
else
{
e.DrawBackground();
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.Black, e.Bounds);
e.DrawFocusRectangle();
}
}
void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 1)
comboBox1.SelectedIndex = -1;
}
Here's my answer based 100% on Arif Eqbal's.
The improvements are:
reuse the Font from the ComboBox instead of creating new ones (so that if you change it in the designer, you won't have to update the code)
reuse the default SystemBrushes (so it should match your theme ; it won't work if you manually change the colors used in the ComboBox though)
for the disabled items I had to redraw the background, else each time the grayed items are redrawn, their color get closer and closer to black
create a dedicated IsItemDisabled method to avoid copy/paste
// Don't forget to change DrawMode, else the DrawItem event won't be called.
// this.comboBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
if (IsItemDisabled(e.Index))
{
// NOTE we must draw the background or else each time we hover over the text it will be redrawn and its color will get darker and darker.
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, SystemBrushes.GrayText, e.Bounds);
}
else
{
e.DrawBackground();
// Using winwaed's advice for selected items:
// Set the brush according to whether the item is selected or not
Brush brush = ( (e.State & DrawItemState.Selected) > 0) ? SystemBrushes.HighlightText : SystemBrushes.ControlText;
e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, brush, e.Bounds);
e.DrawFocusRectangle();
}
}
void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (IsItemDisabled(comboBox1.SelectedIndex))
comboBox1.SelectedIndex = -1;
}
bool IsItemDisabled(int index)
{
// We are disabling item based on Index, you can have your logic here
return index % 2 == 1;
}
Here's a further modification. The problem with the above solutions is that a selected item isn't visible because the font foreground and the background selection are both dark. Hence the font should be set according to the value of e.State:
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
ComboBox comboBox = (ComboBox)sender;
if (e.Index >= 0)
{
if (IsItemDisabled(e.Index))
{
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, Brushes.LightSlateGray, e.Bounds);
}
else
{
e.DrawBackground();
// Set the brush according to whether the item is selected or not
Brush br = ( (e.State & DrawItemState.Selected) > 0) ? SystemBrushes.HighlightText : SystemBrushes.ControlText;
e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, br, e.Bounds);
e.DrawFocusRectangle();
}
}
}
Related
I want to align DataGridViewComboBoxColumn selections text center when in editmode(when click dropdown). I found an approach here:Align Text in Combobox, to align combobox, and tried to add EditingControlShowing event to DGV, and add DrawItem event like above approach to align combobox, but it didn't work. Here is the code:
private void MyDGV_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control.GetType() == typeof(DataGridViewComboBoxEditingControl))
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
cb.DrawItem += new DrawItemEventHandler(cbxDesign_DrawItem);
}
}
}
private void cbxDesign_DrawItem(object sender, DrawItemEventArgs e)
{
// By using Sender, one method could handle multiple ComboBoxes
ComboBox cbx = sender as ComboBox;
if (cbx != null)
{
// Always draw the background
e.DrawBackground();
// Drawing one of the items?
if (e.Index >= 0)
{
// Set the string alignment. Choices are Center, Near and Far
StringFormat sf = new StringFormat();
sf.LineAlignment = StringAlignment.Center;
sf.Alignment = StringAlignment.Center;
// Set the Brush to ComboBox ForeColor to maintain any ComboBox color settings
// Assumes Brush is solid
Brush brush = new SolidBrush(cbx.ForeColor);
// If drawing highlighted selection, change brush
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
brush = SystemBrushes.HighlightText;
// Draw the string
e.Graphics.DrawString(cbx.Items[e.Index].ToString(), cbx.Font, brush, e.Bounds, sf);
}
}
}
Any suggestions? Thanks!
In your code you need to add these properties for the code to work:
private void MyDGV_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control.GetType() == typeof(DataGridViewComboBoxEditingControl))
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
//add these 2
cb.DrawMode = DrawMode.OwnerDrawFixed;
cb.DropDownStyle = ComboBoxStyle.DropDownList;
cb.DrawItem += new DrawItemEventHandler(cbxDesign_DrawItem);
}
}
}
How to change an item's color when focusing on it in the winforms listbox?
I tried with the MouseHover event of the listbox. But nothing happens.
private void lstNumbers_MouseHover(object sender, EventArgs e)
{
Point point = lstNumbers.PointToClient(Cursor.Position);
int index = lstNumbers.IndexFromPoint(point);
if (index < 0) return;
lstNumbers.GetItemRectangle(index).Inflate(1, 2);
}
I got the solution from this answer.
We need to keep track of the item,
public partial class Form1 : Form
{
private int _MouseIndex = -1;
public Form1()
{ InitializeComponent(); }
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
Brush textBrush = SystemBrushes.WindowText;
if (e.Index > -1)
{
if (e.Index == _MouseIndex)
{
e.Graphics.FillRectangle(SystemBrushes.HotTrack, e.Bounds);
textBrush = SystemBrushes.HighlightText;
}
else
{
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds);
textBrush = SystemBrushes.HighlightText;
}
else
e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
}
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), e.Font, textBrush, e.Bounds.Left + 2, e.Bounds.Top);
}
}
private void listBox1_MouseMove(object sender, MouseEventArgs e)
{
int index = listBox1.IndexFromPoint(e.Location);
if (index != _MouseIndex)
{
_MouseIndex = index;
listBox1.Invalidate();
}
}
private void listBox1_MouseLeave(object sender, EventArgs e)
{
if (_MouseIndex > -1)
{
_MouseIndex = -1;
listBox1.Invalidate();
}
}
}
I think the problem maybe that your not actually changing the colour of the item your hovering over:
lstNumbers.GetItemRectangle(index).Inflate(1, 2); //This is trying to inflate the item
You need to do something to change the color.
Also there is the ItemMouseHover event which you can utilise. Something like:
private void lstNumbers_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e)
{
e.Item.BackColor = Color.Green;
}
I hope this helps you!
I created a font list in a combobox. I set it's DrawMode to OwnerDrawFixed and the method DrawItem is simple:
void cmbFonts_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
e.DrawBackground();
Font newFont =
new Font(cmbFonts.Items[e.Index].ToString(), this.DefaultFontSize);
e.Graphics.DrawString(cmbFonts.Items[e.Index].ToString(),
newFont,
new SolidBrush(Color.Black),
new Rectangle(e.Bounds.Location, e.Bounds.Size));
e.DrawFocusRectangle();
}
In gerneral, it works correctly. The problem appears on mouse scrolling. Then some items look like random graphics until they are focused. Anybody knows solution for the problem?
Always call e.DrawBackground(), regardless of the index. Fix:
void cmbFonts_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index >= 0) {
// etc...
}
}
How can I set the color of the font for different items in a DataGridViewComboBoxCell? For example if I have 10 items, how would I make items 3 and 5 Red and leave the others black?
EDIT: This is for a winform application and the DataGridViewComboBox is not data bound
edit2 : Maybe I could do it here in the editcontrolshowing?
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name == "MyCombo")
{
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)dataGridView1.CurrentCell;
for (int i = 0; i < comboCell.Items.Count; ++i)
{
string contract = comboCell.Items[i].ToString();
if (contract.ToUpper().Contains("NO"))
{
// can I set this item have a red font color???
}
}
}
for rows do below -
hook up OnRowDataBound event then do stuff
ASPX (Grid):
<asp:.... OnRowDataBound="RowDataBound"..../>
Code Behind:
protected void RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowIndex == -1)
{
return;
}
if(e.Row.Cells[YOUR_COLUMN_INDEX].Text=="NO"){
e.Row.BackColor=Color.Red;
}
}
FOR WinForms:
hook the **DataBindingComplete** event and do stuff in it:
private void dataGridView1_DataBindingComplete(object sender,
DataGridViewBindingCompleteEventArgs e)
{
if (e.ListChangedType != ListChangedType.ItemDeleted)
{
DataGridViewCellStyle red = dataGridView1.DefaultCellStyle.Clone();
red.BackColor=Color.Red;
foreach (DataGridViewRow r in dataGridView1.Rows)
{
if (r.Cells["FollowedUp"].Value.ToString()
.ToUpper().Contains("NO"))
{
r.DefaultCellStyle = red;
}
}
}
}
You need to draw the ComboBox items manually. Try the following (based on this MSDN post):
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
// Note this assumes there is only one ComboBox column in the grid!
var comboBox = e.Control as ComboBox;
if (comboBox == null)
return;
comboBox.DrawMode = DrawMode.OwnerDrawFixed;
comboBox.DrawItem -= DrawGridComboBoxItem;
comboBox.DrawItem += DrawGridComboBoxItem;
}
private void DrawGridComboBoxItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if (e.Index != -1)
{
// Determine which foreground color to use
var itemValue = actionsColumn.Items[e.Index] as string;
bool isNo = String.Equals("no", itemValue, StringComparison.CurrentCultureIgnoreCase);
Color color = isNo ? Color.Red : e.ForeColor;
using (var brush = new SolidBrush(color))
e.Graphics.DrawString(itemValue, e.Font, brush, e.Bounds);
}
e.DrawFocusRectangle();
}
i need to change the backcolor of the selected item in listbox. Can you give me some code example? i've tried adding DrawItem event but it didnt work for me.
Set the listbox's DrawMode to OwnerDrawFixed.
Then assign these events:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
int index = e.Index;
Graphics g = e.Graphics;
foreach (int selectedIndex in this.listBox1.SelectedIndices)
{
if (index == selectedIndex)
{
// Draw the new background colour
e.DrawBackground();
g.FillRectangle(new SolidBrush(Color.Red), e.Bounds);
}
}
// Get the item details
Font font = listBox1.Font;
Color colour = listBox1.ForeColor;
string text = listBox1.Items[index].ToString();
// Print the text
g.DrawString(text, font, new SolidBrush(Color.Black), (float)e.Bounds.X, (float)e.Bounds.Y);
e.DrawFocusRectangle();
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
listBox1.Invalidate();
}