C# Can I display images in a list box? - c#

C# In a nut shell can I display images in a list box? I have a list of users and I want to display a green tick next to some of the names, is this possible?
Thanks

The following code displays how to do custom drawing in a listbox.
using System.Windows.Forms;
using System.Drawing;
namespace Toolset.Controls
{
public class CustomDrawListBox : ListBox
{
public CustomDrawListBox()
{
this.DrawMode = DrawMode.OwnerDrawVariable; // We're using custom drawing.
this.ItemHeight = 40; // Set the item height to 40.
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
// Make sure we're not trying to draw something that isn't there.
if (e.Index >= this.Items.Count || e.Index <= -1)
return;
// Get the item object.
object item = this.Items[e.Index];
if (item == null)
return;
// Draw the background color depending on
// if the item is selected or not.
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
// The item is selected.
// We want a blue background color.
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), e.Bounds);
}
else
{
// The item is NOT selected.
// We want a white background color.
e.Graphics.FillRectangle(new SolidBrush(Color.White), e.Bounds);
}
// Draw the item.
string text = item.ToString();
SizeF stringSize = e.Graphics.MeasureString(text, this.Font);
e.Graphics.DrawString(text, this.Font, new SolidBrush(Color.White),
new PointF(5, e.Bounds.Y + (e.Bounds.Height - stringSize.Height) / 2));
}
}
}

System.Windows.Forms.ListView will do the trick very easily. You might have to work a little harder than a ListBox if you want the list in 'details' view though.

In WPF it's quite simple, but if you're using winforms, you can't do it with the System.Windows.Forms.ListBox control. You can do it with the ListView control, or third party controls.

Related

Winforms - Listbox item hover and select color

I'm remaking a program, everything was going good but now i ran into a problem. The program i'm rewriting is wpf and i'm working in winforms, the program has a listbox that i cant recreate that easy :(
Here is a picture of the ListBox with a selected item: Selected Item
As you can see selected items turn blue and the border of it another blue, when you stop focusing on the form it will turn white: White item
And items where you hover on will turn blue to but a bit lighter:
Hover Item
Thanks for your help!
btw im using .NET Framework 4.8
You can create your own listbox deriving from the original one. Once the new list is compiled for the first time, it will appear in the toolbox, so that you can drag and drop it onto your forms. Or you can change the type of existing listboxes to ListBoxEx in the Form.designer.cs manually.
public class ListBoxEx : ListBox
{
public ListBoxEx()
{
DrawMode = DrawMode.OwnerDrawFixed;
DoubleBuffered = true; // Eliminates flicker (optional).
}
private int _hotTrackedIndex = -1;
private int HotTrackedIndex
{
get => _hotTrackedIndex;
set {
if (value != _hotTrackedIndex) {
if (_hotTrackedIndex >= 0 && _hotTrackedIndex < Items.Count) {
Invalidate(GetItemRectangle(_hotTrackedIndex));
}
_hotTrackedIndex = value;
if (_hotTrackedIndex >= 0) {
Invalidate(GetItemRectangle(_hotTrackedIndex));
}
}
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
var borderRect = e.Bounds;
borderRect.Width--;
borderRect.Height--;
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
if (Focused) {
e.Graphics.FillRectangle(Brushes.Teal, e.Bounds);
e.Graphics.DrawRectangle(Pens.LightSkyBlue, borderRect);
} else {
e.Graphics.FillRectangle(Brushes.DimGray, e.Bounds);
e.Graphics.DrawRectangle(Pens.White, borderRect);
}
} else if (e.Index == HotTrackedIndex) {
e.Graphics.FillRectangle(Brushes.DarkSlateGray, e.Bounds);
e.Graphics.DrawRectangle(Pens.DarkCyan, borderRect);
} else {
e.DrawBackground();
}
if (Items[e.Index] != null) {
e.Graphics.DrawString(Items[e.Index].ToString(), e.Font, Brushes.White, 6, e.Bounds.Top, StringFormat.GenericTypographic);
}
}
protected override void OnMouseLeave(EventArgs e)
{
HotTrackedIndex = -1;
base.OnMouseLeave(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
HotTrackedIndex = IndexFromPoint(e.Location);
base.OnMouseMove(e);
}
protected override void OnGotFocus(EventArgs e)
{
if (SelectedIndex >= 0) {
RefreshItem(SelectedIndex);
}
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
if (SelectedIndex >= 0) {
RefreshItem(SelectedIndex);
}
base.OnLostFocus(e);
}
}
We change the appearance of the listbox by overriding OnDrawItem. in the constructor, we set DrawMode = DrawMode.OwnerDrawFixed; to enable owner drawing.
We must consider selected items and hot tracked items, i.e., items the mouse moves over. If the item to be drawn is the selected one, we further differentiate between the cases where the listbox has the focus or not.
FillRectangle draws the background. DrawRectangle draws the border. Note that the border rectangle must be smaller by one pixel than the e.Bounds rectangle, otherwise the right and bottom borders will not be drawn.
If the current item is not selected, we test to see whether it is hot tracked. If it is, we draw in different colors. Otherwise we draw the default background with e.DrawBackground();.
Then we draw the text over the background with DrawString.
For all this to work, we also must invalidate regions of the listbox where the colors are changing. We detect changes in hot tracking in OnMouseMove and OnMouseLeave. There we set the HotTrackedIndex. This is a property which triggers drawing when necessary.
In OnGotFocus and OnLostFocus we refresh the selected item to change its color depending on the focused state.
My colors do not match your images, but you can easily tweak them. If you need to create Brushes and Pens in non-standard colors, then either create them as static and read-only or then don't forget to dispose them.
private static readonly Brush HotTrackBrush = new SolidBrush(new Color(123, 45, 67));
private static readonly Pen HotTrackPen = new Pen(new Color(234, 56, 78));
An improved version of this listbox could expose the different selection and hot tracking colors as properties, so that you could easily change them in the properties window. (Properties automatically appear there.)

How to add Back and Fore Color on ContextMenu

I am Showing a ContextMenu whenever the user right clicks on a specific location in a DataGridView.
I want the items of that ContextMenu to have a back color and fore color depending on their content.
How can I do this since ContextMenu has no back color or Fore color property?
I tried looking up ContextMenuStrip but this has to be connected to a ToolStripButton which I do not have and do not want.
In order to change the back color of a MenuItem you need to specify a draw item handler and set owner-draw to true for each item. Also for the color to actually take some space you need to implement a MeasureMenuItem handler.
So for example
color.MenuItems.Add(new MenuItem("#123456", menuHandler));
color.MenuItems.Add(new MenuItem("Green", menuHandler));
color.MenuItems.Add(new MenuItem("Red", menuHandler));
foreach (MenuItem item in color.MenuItems)
{
item.OwnerDraw = true;
item.DrawItem += item_DrawItem;
item.MeasureItem += MeasureMenuItem;
}
The above codes hooks up the items and their handlers.
void item_DrawItem(object sender, DrawItemEventArgs e)
{
MenuItem cmb = sender as MenuItem;
string color = SystemColors.Window.ToString();
if (e.Index > -1)
{
color = cmb.Text;
}
if (checkHtmlColor(color))
{
e.DrawBackground();
e.Graphics.FillRectangle(new SolidBrush(ColorTranslator.FromHtml(color)), e.Bounds);
e.Graphics.DrawString(color, new Font("Lucida Sans", 10), new SolidBrush(ColorTranslator.FromHtml(color)), e.Bounds);
}
}
The above code takes the MenuItem contents, converts it to a color, creates a rectangle for that color and draws it.
void MeasureMenuItem(object sender, MeasureItemEventArgs e)
{
MenuItem m = (MenuItem)sender;
Font font = new Font(Font.FontFamily, Font.Size, Font.Style);
SizeF sze = e.Graphics.MeasureString(m.Text, font);
e.ItemHeight = (int)sze.Height;
e.ItemWidth = (int)sze.Width;
}
And lastly the above few lines simply measure the area the MenuItem should take before drawing (basically measures the space of it's string content) so the draw_item handler knows how much space to take up
I allow myself to dig up the post because I've had the same problem (add background color to MenuItem in ContextMenu) and found this post.
But the answer seemed very complicated. So I continued to search and find a simplet solution : Use ContextMenuStrip and ToolStripMenuItem
Here is an example for users who has the same problem :
ContextMenuStrip cMenu=new ContextMenuStrip();
ToolStripMenuItem mi;
// Item 1, null in constructor to say : no picture on the label
mi=new ToolStripMenuItem("item 1",null , (s,a)=> actionOnClicItem1());
mi.BackColor = Color.Red;
cMenu.Items.add(mi);
// Separator
cMenu.Items.Add(new ToolStripSeparator());
// Item 2
mi=new ToolStripMenuItem("item 2",null , (s,a)=> actionOnClicItem2());
mi.BackColor = Color.Blue;
cMenu.Items.add(mi);
// show the context menu near by the mouse pointer
cMenu.Show(myDataGridView,new Point(e.X,e.Y));
myToolStripMenuItem.GetCurrentParent().BackColor = Color.Red

change height of combobox in winform application

I'm developing an application for kind of touch screen device. In order be user friendly, I need to change size of combobox.
I've checked many thing including DrawItemEventHandler and MeasureItemEventHandler, but it didn't work as I want.
Basically I would like to change height of combobox without touching font size. When I change font size of combobox, it looks like left side of the image.
How can I set my combobox which will look like right side of the image?
By the way, don't know if it's effect solution, I am not using array string. I'm binding data like.
combobox.DisplayMember = "Name";
combobox.ValueMember = "ID";
combobox.DataSource = new BindingSource { DataSource = datalist };
Thanks in advance.
With TaW solution, I managed to set items as I want. The only thing I couldn't set text in middle when combobox items not droped down. How can I set this text position to the centre?
You can set the ItemHeight property and then draw the items yourself in the DrawItem event.
Not terribly hard, search for 'ownerdraw' & 'combobox'. There is one example on Code Project
Here is a minimal version, pulled from the above link:
private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index < 0) return;
Font f = comboBox1.Font;
int yOffset = 10;
if ((e.State & DrawItemState.Focus) == 0)
{
e.Graphics.FillRectangle(Brushes.White, e.Bounds);
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), f, Brushes.Black,
new Point(e.Bounds.X, e.Bounds.Y + yOffset));
}
else
{
e.Graphics.FillRectangle(Brushes.Blue, e.Bounds);
e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), f, Brushes.White,
new Point(e.Bounds.X, e.Bounds.Y + yOffset));
}
}
You also have to set the DropDownStyle to DropDownList to get the highlighting to work and you need to set the DrawMode to OwnerDrawFixed. (Or to OwnerDrawVariable, if you want to have different heights for some itmes..)

C# Color of listbox item continued

I'm trying to find a way to highlight the background of list box items with different colors, and came across this four year old closed thread that almost answers the question here:
Background color of a ListBox item (winforms)
I'm not following how to use his method. So if I created an object named listBox1 and a string variable named strSomeString, what would be the exact code for adding strSomeString to listBox1 with a red background?
Using Shadow Wizard's code here:
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;
Color color = (selected) ? Color.FromKnownColor(KnownColor.Highlight) : (((index % 2) == 0) ? Color.White : Color.Gray);
g.FillRectangle(new SolidBrush(color), e.Bounds);
// Print text
g.DrawString(text, e.Font, (selected) ? reportsForegroundBrushSelected : reportsForegroundBrush,
lbReports.GetItemRectangle(index).Location);
}
e.DrawFocusRectangle();
}
-Thanks
Consider using an owner draw control for this.
It's more work because you need to render all the list contents. But you'll have total flexibility.
You just need to hook the Draw_item event of the listbox to lbReports_DrawItem handler.
You can do that by set it at the properties of the listbox.
Another thing is to Set the DrawMode to OwnerDrawFixed

Default implementation for ListView OwnerDraw

I have a ListView where I wish to tweak the drawing of items (for example highlighting certain strings in list view itmes), however I don't want to radically alter the way that items are displayed.
I have set the OwnerDraw to true and can get my head around how to draw my highlighting effect, however whenever I try to defer to the default implementation to draw the rest of the list view item things go wrong and I'm left with a whole load of graphical problems indicating that actually I've completely gone wrong.
Is there somewhere that I can see what the "Default" handlers for the DrawItem and DrawSubItem events do so that I can better my understanding and more easily tweak my code?
For reference here is a snippet showing what I'm currently doing:
public MyListView()
{
this.OwnerDraw = true;
this.DoubleBuffered = true;
this.DrawColumnHeader += new DrawListViewColumnHeaderEventHandler(MyListView_DrawColumnHeader);
this.DrawItem += new DrawListViewItemEventHandler(MyListView_DrawItem);
this.DrawSubItem += new DrawListViewSubItemEventHandler(MyListView_DrawSubItem);
}
private void MyListView_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
// Not interested in changing the way columns are drawn - this works fine
e.DrawDefault = true;
}
private void MyListView_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
}
private void MyListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0)
{
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
}
e.DrawText();
}
I haven't got the time now to write up a complete answer so instead I'll put down some quick notes and come back to it later.
As LarsTech said, owner drawing a ListView control is a pain - the .Net ListView class is a wrapper around the underlying Win32 List View Control and the ability to "Owner draw" is provided by the NM_CUSTOMDRAW notification code. As such there is no "default .Net implementation" - the default is to use the underlying Win32 control.
To make life even more difficult there are a number of extra considerations to make:
As LarsTech pointed out, the first subitem in fact represents the parent item itself, and so if you handle rendering in both DrawItem and DrawSubItem you may well be drawing the contents of the first cell twice.
There is a bug in the underlying list view control (documented on the note on this page) that means that a DrawItem event will occur without corresponding DrawSubItem events, meaning that if you draw a background in the DrawItem event and then draw the text in the DrawSubItem event your item text will disappear when you mouse over.
Some of the rendering also appears to not be double-buffered by default
I also noticed that the ItemState property is not always correct, for example just after resizing a column. Consequently I've found its best not to rely on it.
You also need to make sure that your text doesn't split over multiple lines, else you will see the top few pixels of the lower line being rendered at the bottom of the cell.
Also special consideration needs to be given when rendering the first cell to take account of extra padding that the native list view uses.
Because the DrawItem event occurs first, anything you draw in the DrawItem handler (e.g. the selection effect) may well be overlayed by things you do in the DrawSubItem handler (e.g. having certain cells with a different background color).
All in all handling owner drawing is a fairly involved affair - I found it best to handle all drawing inside the DrawSubItem event, its also best to perform your own double-buffering by using the BufferedGraphics class.
I also found looking at the source code for ObjectListView very handy.
Finally, all of this is just to handle the details mode of the list view (the only mode I am using), if you want the other modes to work too then I believe that there are extra things to take account of.
When I get a chance I'll try and post my working example code.
I don't know if this will completely help you, but I'll add a few notes:
One thing to keep in mind is that DrawSubItem will draw the first item, too, and that's probably where you are getting the double-rendered look from.
Some things to try (not factored for speed):
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e) {
e.DrawBackground();
if ((e.State & ListViewItemStates.Selected) == ListViewItemStates.Selected) {
Rectangle r = new Rectangle(e.Bounds.Left + 4, e.Bounds.Top, TextRenderer.MeasureText(e.Item.Text, e.Item.Font).Width, e.Bounds.Height);
e.Graphics.FillRectangle(SystemBrushes.Highlight, r);
e.Item.ForeColor = SystemColors.HighlightText;
} else {
e.Item.ForeColor = SystemColors.WindowText;
}
e.DrawText();
e.DrawFocusRectangle();
}
For your DrawSubItem routine, make sure you aren't drawing in the first column and I added the DrawBackground() routine. I added some clipping to the highlight rectangle so it wouldn't paint outside the column parameters.
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) {
if (e.ColumnIndex > 0) {
e.DrawBackground();
string searchTerm = "Term";
int index = e.SubItem.Text.IndexOf(searchTerm);
if (index >= 0) {
string sBefore = e.SubItem.Text.Substring(0, index);
Size bounds = new Size(e.Bounds.Width, e.Bounds.Height);
Size s1 = TextRenderer.MeasureText(e.Graphics, sBefore, this.Font, bounds);
Size s2 = TextRenderer.MeasureText(e.Graphics, searchTerm, this.Font, bounds);
Rectangle rect = new Rectangle(e.Bounds.X + s1.Width, e.Bounds.Y, s2.Width, e.Bounds.Height);
e.Graphics.SetClip(e.Bounds);
e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), rect);
e.Graphics.ResetClip();
}
e.DrawText();
}
}
In general, owner drawing a ListView control is welcoming in a world of hurt. You aren't drawing in Visual Styles anymore, you would have to do that yourself, too. Ugh.
Item selected back color changed. In by default blue in windows. This code will help for u in any colors:
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
if (e.Item.Selected)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds);
}
e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds);
}
private void Form1_Load(object sender, EventArgs e)
{
for (int ix = 0; ix < listView1.Items.Count; ++ix)
{
var item = listView1.Items[ix];
item.BackColor = (ix % 2 == 0) ? Color.Gray : Color.LightGray;
}
}
private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
}
}
ComponentOwl recently released a freeware component called Better ListView Express.
It looks and behaves exactly like the ListView, but has much more powerful owner drawing capabilities - you can draw accurately over all elements and even turn off some drawing (e.g. selection to make you on).
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawBackground();
if (e.Item.Selected)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(250, 194, 87)), e.Bounds);
}
e.Graphics.DrawString(e.Item.Text, new Font("Arial", 10), new SolidBrush(Color.Black), e.Bounds);
}

Categories

Resources