c# combobox DrawItem - refreshing issue - c#

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

Related

How to draw border for panel which is continously resizing c#

Using panel as a selection indicator . The panel changes its size according to cursor position on MouseMove event . However when I draw the border as below , previous borders leave their mark on the panel and it displays too many border within same panel . Even tried refresh() before every draw but that makes it glitchy and slow
private void panel1_Paint(object sender, PaintEventArgs e)
{
this.panel1.Refresh();
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
panel1.Width = e.Location.X - panel1.Left;
panel1.Height = e.Location.Y - panel1.Top;
}
First off, you should never call control paint affecting methods like Invalidate or Refresh inside the control paint handler.
You can solve the original issue by calling Invalidate or Refresh after modifying the size of the panel. Note that it's better to set the Size property with one call rather than Width and Height separately:
private void panel1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.panel1.ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
var size = new Size(Math.Max(e.Location.X - panel1.Left, 0),
Math.Max(e.Location.Y - panel1.Top, 0));
if (panel1.Size != size)
{
panel1.Size = size;
panel1.Invalidate();
}
}
A better option is to set ResizeRedraw property of the selection panel to true. Since it's a protected property, you need to create and use your own Panel subclass. As a bonus, you can also set DoubleBuffered property to true to avoid the flickering, and also move the painting code inside:
class SelectionBox : Panel
{
public SelectionBox()
{
ResizeRedraw = true;
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.DarkBlue, ButtonBorderStyle.Solid);
}
}
Make your panel1 to be SelectionBox, remove the Paint event handler, and then the mouse move handler could be simple
private void drawboard_MouseMove(object sender, MouseEventArgs e)
{
panel1.Size = new Size(Math.Max(e.Location.X - panel1.Left, 0),
Math.Max(e.Location.Y - panel1.Top, 0));
}

In C#, how can I keep all my items in a listbox from changing to the color I set last?

I have researched how to change colors for each line and here is my code using public variable itemColor which is a Brush
...
public Brush itemColor;
private void button2_Click(object sender, EventArgs e)
{
itemColor = Brushes.Purple;
listBox1.Items.Add("Purple");
itemColor = Brushes.Green;
listBox1.Items.Add("Green");
itemColor = Brushes.Red;
listBox1.Items.Add("Red");
}
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
e.Graphics.DrawString(listBox1.Items[e.Index].ToString(), listBox1.Font,
itemColor, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
...
I have set the listbox DrawMode to OwnerDrawFixed and all items turn to red. Can anyone see my probably silly error?
listBox1_DrawItem gets called every time the control needs to be redrawn, such as on item add/remove or selection change. You can see this by adding a second button to your form and doing this:
private void button2_Click(object sender, EventArgs e)
{
itemColor = Brushes.Blue;
}
After you click the second button, the next time the ListBox is redrawn, all the items' text will be blue.
There is most likely a better way to do this, but one way you could handle this is to make a class to represent you items with a Text and Brush field and add fill your ListBox with those. Then on the DrawItem handler, you cast the Items[e.Index] to your class and reference the text and color fields. Something like this:
class Entry
{
public string Text;
public Brush Color;
}
private void button1_Click(object sender, EventArgs e)
{
listBox1.Items.Add(new Entry { Text = "Purple", Color = Brushes.Purple });
listBox1.Items.Add(new Entry { Text = "Green", Color = Brushes.Green });
listBox1.Items.Add(new Entry { Text = "Red", Color = Brushes.Red });
}
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
var currentItem = listBox1.Items[e.Index] as Entry;
e.DrawBackground();
e.Graphics.DrawString(currentItem.Text, listBox1.Font, currentItem.Color,
e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}

How to disable one of items in a combo box [duplicate]

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

Drawn line isn't shown on a combobox

I need your help in following question (using .Net 3.5 and Windows Forms):
I just simply want to draw a line on a middle of a combobox (Windows Forms) that is situated on a form.
The code I use is:
void comboBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(new Pen(Brushes.DarkBlue),
this.comboBox1.Location.X,
this.comboBox1.Location.Y + (this.comboBox1.Size.Height / 2),
this.comboBox1.Location.X + this.comboBox1.Size.Width,
this.comboBox1.Location.Y + (this.comboBox1.Size.Height / 2));
}
To fire a paint event:
private void button1_Click(object sender, EventArgs e)
{
comboBox1.Refresh();
}
When I execute the code and press button the line isn't drawn. In debug the breakpoint at the paint handler isn't being hit. The strange thing is that on MSDN there is a paint event in ComBox's events list, but in VS 2010 IntelliSense doesn't find such event in ComboBox's members
Thanks.
public Form1()
{
InitializeComponent();
comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
comboBox1.DrawItem += new DrawItemEventHandler(comboBox1_DrawItem);
}
void comboBox1_DrawItem(object sender, DrawItemEventArgs e) {
e.DrawBackground();
e.Graphics.DrawLine(Pens.Black, new Point(e.Bounds.Left, e.Bounds.Bottom-1),
new Point(e.Bounds.Right, e.Bounds.Bottom-1));
TextRenderer.DrawText(e.Graphics, comboBox1.Items[e.Index].ToString(),
comboBox1.Font, e.Bounds, comboBox1.ForeColor, TextFormatFlags.Left);
e.DrawFocusRectangle();
}
Paint event will not fire.
What do you want is possible only when DropDownStyle == ComboBoxStyle.DropDownList:
comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox1.DrawItem += (sender, args) =>
{
var ordinate = args.Bounds.Top + args.Bounds.Height / 2;
args.Graphics.DrawLine(new Pen(Color.Red), new Point(args.Bounds.Left, ordinate),
new Point(args.Bounds.Right, ordinate));
};
This way you can draw selected item area yourself.

Change Listbox's selected item Backcolor in C#.net

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

Categories