I know that similar questions have already been asked here before, but they all lead to the same codeproject article that doesn't work. Does anybody know of a working ListBox with icons?
Will a ListView work for you? That is what I use. Much easier and you can make it look just like a ListBox. Also, plenty of documentation on MSDN to get started with.
How to: Display Icons for the Windows Forms ListView Control
The Windows Forms ListView control can display icons from three image
lists. The List, Details, and SmallIcon views display images from the
image list specified in the SmallImageList property. The LargeIcon
view displays images from the image list specified in the
LargeImageList property. A list view can also display an additional
set of icons, set in the StateImageList property, next to the large or
small icons. For more information about image lists, see ImageList
Component (Windows Forms) and How to: Add or Remove Images with the
Windows Forms ImageList Component.
Inserted from How to: Display Icons for the Windows Forms ListView Control
If you don't want to change ListBox to a ListView you can write a handler for DrawItemEvent. for example:
private void InitializeComponent()
{
...
this.listBox.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.listBox_DrawItem);
...
}
private void listBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1)
return;
// Draw the background of the ListBox control for each item.
e.DrawBackground();
var rect = new Rectangle(e.Bounds.X+10, e.Bounds.Y+8, 12, 14);
//assuming the icon is already added to project resources
e.Graphics.DrawIconUnstretched(YourProject.Properties.Resources.YouIcon, rect);
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(),
e.Font, Brushes.Black, new Rectangle(e.Bounds.X + 25, e.Bounds.Y + 10, e.Bounds.Width, e.Bounds.Height), StringFormat.GenericDefault);
// If the ListBox has focus, draw a focus rectangle around the selected item.
e.DrawFocusRectangle();
}
you can play around with the rectangle to set the location of the icon right
If you're stuck working in WinForms, then you'll have to owner-draw your items.
See the example for the DrawItem event.
A little different approach - don't use a list box.
Instead of using that control that bounds me to its limited set of properties and methods I am making a listbox of my own.
It's not as hard as it sounds:
int yPos = 0;
Panel myListBox = new Panel();
foreach (Object object in YourObjectList)
{
Panel line = new Panel();
line.Location = new Point(0, Ypos);
line.Size = new Size(myListBox.Width, 20);
line.MouseClick += new MouseEventHandler(line_MouseClick);
myListBox.Controls.Add(line);
// Add and arrange the controls you want in the line
yPos += line.Height;
}
Example for myListBox event handlers - selecting a line:
private void line_MouseClick(object sender, MouseEventArgs)
{
foreach (Control control in myListBox.Controls)
if (control is Panel)
if (control == sender)
control.BackColor = Color.DarkBlue;
else
control.BackColor = Color.Transparent;
}
The code samples above were not tested but the described method was used and found very convenient and simple.
Related
I have created a ListBox to which I add elements during code compilation. and I want to record its color when adding one element (so that each added element has a different color)
listBox1.Items.Add(string.Format("Місце {0} | В роботі з {1} | ({2} хв)", temp[7].Substring(6, 4), temp[8].Substring(11, 5), rezult)); `
I tried everywhere possible to embed this change
BackColor = Color.Yellow;ForeColor = Color.Yellow;
I am working with listbox because I have seen so many answers about ListView.
Set the listbox DrawMode to either OwnerDrawFixed or OwnerDrawVariable and set this as the DrawItem event handler:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e){
if(e.Index == 1) e.DrawBackground(); //use e.Index to see if we want to highlight the item
else e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), e.Bounds); //This does the same thing as e.DrawBackground but with a custom color
e.DrawFocusRectangle();
if(e.Index < 0) return;
TextRenderer.DrawText(e.Graphics, (string)listBox1.Items[e.Index], listBox1.Font, e.Bounds, listBox1.ForeColor, TextFormatFlags.Left);
}
Well, best idea I have is to not use list box, but flowLayoutPanel and add usercontrols where you will have labels.
flowLayoutPanel works as list of controls which you can scroll, so we will just create a usercontrol, where we will put label and change the usercontrol background
Don't forget to turn on the AutoScroll feature to flowLayoutPanel, otherwise the scroll bar wont work and wont even show up.
If you want to be able to be clickable just add to the label click event.
public void CreateItem(Color OurColor, string TextToShow)
{
Label OurText = new Label()
{
Text = "TextToShow",
Font = new Font("Segoe UI", 8f),
Location = new Point(0, 0),
AutoSize = true,
};
UserControl OurUserControl = new UserControl();
OurUserControl.Size = new Size((int)((double)flowLayoutPanel1.Width * 0.9) , OurText.Font.Height);
OurUserControl.BackColor = OurColor;
OurUserControl.Controls.Add(OurText);
flowLayoutPanel1.Controls.Add(OurUserControl);
}
I am trying to create a Bitmap from a RichTextBox and set it as the background image for a panel, but unfortunately the text is not shown.
Bitmap l_bitmap = new Bitmap(m_control.Width, m_control.Height);
m_control.DrawToBitmap(l_bitmap, new Rectangle(0, 0, l_bitmap.Width, l_bitmap.Height));
m_panel.BackgroundImage = l_bitmap;
m_panel.Refresh();
m_control is my RichTextBox. When I debug, I can see that the control contains the text I wrote, but the bitmap just shows an empty RichTextBox.
I use the same code for other types of controls (Button, CheckBox, TextBox...). The text is shown with no problems.
Well you are trying to create a bitmap from the control. The text you put in there isn't the control, so it won't bother to chow it as bitmap. Try to create a picture from screen (like a screenshot).
Example:
Graphics gr = Graphics.FromImage(l_bitmap);
gr.CopyFromScreen(m_control.PointToScreen(Point.Empty), point.Empty, m_control.Size);
This will make a bitmap from your given points. This will additional show you the text.
EDIT
Maybe you can use this instead. In addition to your idea, I simply put a label onto my panel. (L for Label and P for Panel)
As you can see, the label is empty because I cleared the Text property. Now, when you click one of the buttons below the panel, it will update the label.Text propertie and there will be the text you gave the control.
Here is some example:
As you can see, the label shows the Name of the control. Completly custom as you can see on my source code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public RichTextBox tmpRtf = new RichTextBox();
//Poor button name incoming...
private void button1_Click(object sender, EventArgs e)
{
if (tmpRtf == null)
tmpRtf = new RichTextBox();
//You can add any text here and it will be shown on the label.
this.tmpRtf.Text = "Richtextbox";
this.UpdatePanel(this.tmpRtf);
}
//Custom method to update the panel for any control. Can pobably be done way better than this, but hey.
private void UpdatePanel(object pControl)
{
//Checks if control is a rtf
if(pControl is RichTextBox)
{
//This is your code! Ay.
Bitmap l_bitmap = new Bitmap(this.panel1.Width / 2, this.panel1.Height / 2);
(pControl as RichTextBox).DrawToBitmap(l_bitmap, new Rectangle(0, 0, l_bitmap.Width, l_bitmap.Height));
this.tmpRtf.BackColor = Color.LightGray;
this.panel1.BackgroundImage = l_bitmap;
this.panel1.BackgroundImageLayout = ImageLayout.Center;
this.labelControlName.Text = this.tmpRtf.Text;
this.panel1.Refresh();
}
}
}
Its not possible to show text on a control thats not visualized. But you can build a workaround! Or, instead of taking a picture you can simply create the control on top of it, that will also show the Text and maybe the user can test it (e.g. click on buttons, look at the control behaviour).
Hopefully this is something to get you inspired that there are always more ways to accomplish.
I've got a WinForms comboBox with the datasouce set to a list of a custom class. I'm displaying these items as colors (based on a property in the class) and would like to only display the color (i.e. no text). I'm displaying the items as colors in the dropdown through the DrawItem event, but this doesn't work for the comboBox itself (the part other than the dropdown). I've tried changing the ForeGround Color to Transparent, but that didn't work either. What I'd really like is a comboBox.DisplayMember = "None"; or something similar.
What is the best way to accomplish this?
Edit: So after a bit of fiddling around, I've found one solution: adding a "None" property to the class like this:
public string None
{
get
{
return "";
}
}
then I can just do the comboBox.DisplayMember = "None"; like I mentioned before. But I think the question still stands: is there a better way?
You can make a ComboBox control as a color picker to display and select colors by using DrawItem event and also there is a property called DrawMode for the ComboBox control which determines whether the Operating System or the code will handle the drawing of the items in the list. This property must be set to OwnerDrawFixed using the Properties window in order for the DrawItem event implementation to be called.
private void ColorComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Rectangle rect = e.Bounds;
if (e.Index >= 0)
{
Color c = Color.FromName(n);
Brush b = new SolidBrush(c);
g.DrawString(n, f, Brushes.Black, rect.X, rect.Top);
g.FillRectangle(b, rect.X, rect.Y + 5, rect.Width -10, rect.Height - 10);
}
}
You can read more about CodeProject: Color Picker Combo Box
As my "pseudo-solution" seems to be the best solution here, I'll just copy it down here:
Since the items in my comboBox are of a custom class, I added another property to that class:
public string None
{
get
{
return "";
}
}
and I set the comboBox.DisplayMember = "None";. This achieves the result I was looking for
Here and there I've seen nicely styled dialog controls, similar to this one which appears in Beyond Compare v4:
My own implementation of this gets vaguely close, and consists of a listbox on the left and usercontrols which change when the listbox selected item changes. However no amount of putting lipstick on that pig will get it looking like the above. I can picture how I might undertake this with custom painting of things and so forth, but my real intent is to generate the left hand entries at runtime not design time (there will be one for each column in a datafile).
I was wondering if anyone had any ideas on how to reasonably easily implement such a thing, either with a component (commercial is fine) or some other ingenious method.
Thanks!
Here is an example of mimicking your design. I have one large panel1 to house both sides and in it one panel2 to house the left side. Inside panel2 there are the search controls and the listview.
The search controls are a Label1 containing a TextBox and another Label2. The Label.Images are aligned and the Textbox has no Border. The Labels are AutoSize=false and Label1 has a 3D-border.
Panel1 has a singleLine border, panel2 and the ListView have no borders. The ListView has View=Details and one column, HeaderStyle=None. It also has OwnerDraw=true.
I have added a Paint event for the ListView but have to call it in code.
Please note that I haven't taken the time to create nice images. Also note: Their height will determine the Items' Height (!) so leave a little transparent border above and below; their good looks will be key to the overall looks!
They are contained in an Imagelist with appropriate Size and BitDepth. You may need to adapt the DrawIamge numbers..
The stuff on the right side is pretty much standard; for the horizontal bar I use a Panel with height=1 and Border=Single. If you have more than one group of RadioButtons make sure to put each group in a separate Panel, transparent and no Borders, of course..
public Form1()
{
InitializeComponent();
listView1.Width = panel2.ClientSize.Width;
listView1.Columns[0].Width = listView1.ClientSize.Width;
listView1.Paint += listView1_Paint;
listView1.BackColor = panel2.BackColor;
leftBrush = new SolidBrush(panel2.BackColor);
rightBrush = new SolidBrush(panel1.BackColor);
}
Pen borderPen = new Pen(SystemColors.ActiveBorder);
SolidBrush leftBrush, rightBrush;
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
if (e.ItemIndex == 0) listView1_Paint(
null, new PaintEventArgs(e.Graphics, listView1.ClientRectangle));
if (!e.Item.Selected)
{
e.Graphics.FillRectangle(leftBrush, e.Bounds);
e.Graphics.DrawLine(borderPen,
listView1.Width-1, e.Bounds.Y, listView1.Width-1, e.Bounds.Bottom);
}
else
{
e.Graphics.FillRectangle(rightBrush , e.Bounds);
e.Graphics.DrawLine(borderPen, 0, e.Bounds.Top, e.Bounds.Width, e.Bounds.Top);
e.Graphics.DrawLine(borderPen,
0, e.Bounds.Bottom-1, e.Bounds.Width, e.Bounds.Bottom-1);
}
e.Graphics.DrawString( e.Item.Text, listView1.Font,
Brushes.Black, 35, e.Bounds.Y + 5 );
e.Graphics.DrawImage(imageList1.Images[e.Item.ImageIndex], 2, e.Bounds.Y );
}
void listView1_Paint(object sender, PaintEventArgs e)
{
int hh = listView1.Items.Count * imageList1.ImageSize.Height;
e.Graphics.DrawLine(borderPen,
listView1.Width - 1, hh, listView1.Width - 1, listView1.Height);
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(borderPen, panel2.Width-1, 0, panel2.Width-1, listView1.Top);
}
}
Here is a screenshot of my Q&D version:
If this is a dumb question, forgive me. I have a small amount of experience with C#, but not to this degree yet.
I have a series of images that I want to put into a grid with space around each image, also text beneath them and I want them to be clickable, so when they're clicked they hilite, and double click runs an event. The best example I have for this is the user interface of the program ACDSee. I've googled this for hours, and haven't come up with anything applicable. Is this difficult or simple? Can anyone give me an example, or point me in the right direction?
Cheers.
It doesn't seem to be very difficult. I would suggest the following steps:
Add a new "User Control" to your project for image thumbnails. It can contain a docked PictureBox and a Label or LinkLabel at its bottom.
For the space around each thumbnail simply play with the Padding property of the user control.
For the so called grid that is going to hold the thumbnails, use a FlowLayoutPanel, and simply add instances of the above mentioned user-control to this panel.
For visual representation of being selected, change the background color of the user-control instance to blue (for example), and back to control-face when deselected. It is recommended to implement an IsSelected property for the user-control as well.
To emulate thumbnail selection, handle the Click event of the user-control and assign the events for all thumbnail instances to a single event-handler method. Store a global reference to the already selected thumbnail, name it e.g., SelectedThumbnail initialized with null. In the event-handler body compare the sender with the global SelectedThumbnail, and update it if required. If the user-control associated with the sender is not selected (i.e., its background is not blue, or IsSelected is false) make it selected, or change its background. Otherwise change the background to its default color (e.g., control-face).
The Click event handler body looks something like this:
MyThumbnailControl ctrl = sender as MyThumbnailControl;
if(ctrl == null) return;
if(ctrl == SelectedThumbnail) return; // selected again
if(ctrl != SelectedThumbnail)
{
ctrl.IsSelected = true;
ctrl.BackColor = Color.Blue;
// it's better to set the back-color in the IsSelected property setter, not here
SelectedThumbnail.IsSelected = false;
SelectedThumbnail.BackColor = Color.Control;
SelectedThumbnail = ctrl; // important part
}
It's also recommended that all thumbnail instances that are going to be added to the so-called grid, be referenced in a separate array too. Therefore changing selection with arrow-keys would be possible with simple index calculations.
Further Notes: I assumed that the user-control that is to be created is named MyThumbnailControl, just a random name to refer to that control. When you create a new user-control, the wizard generates a class for you with your desired name (e.g., MyThumbnailControl), you can define a property inside it named IsSelected and implement its getter and setter. See this for a tutorial. After defining the user-control you can instantiate instances from its corresponding class. Also by global reference, I meant a variable at the form (or any parent control) level. To keep it simple we can add a reference of the selected thumbnail in the form that is going to hold the grid and thumbnails: MyThumbnailControl selectedThumb = null; or something like this in the body of the form.
Here is something, I just fixed you.
Create a C# project name CreateImageList and in the Form1 add the following 5 controls with their default name i.e. Panel1, PictureBox1, Label1, Button1, Button2:
How it works:
When the page load it create an imageList objects and load all .jpg images from a folder you provide
ImageList Images are set into the PictureBox control and when user clicks "Button1" the picturebox shows next image in ImageList and when user clicks "Button2" the PictureBox shows previous image from ImageList.
The Label1 shows the currentImage counter from the ImageList Arrage. If you want to write something specific, you can create an array of text and sync with your image counter.
When user click on PictureBox the a border is create to show Picture highlighted
When user Double Click on PictureBox a MessageBox appears shows DoubleClick event.
Now, you can use the following code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace CreateImageList
{
public partial class Form1 : Form
{
private int currentImage = 0;
protected Graphics myGraphics;
ImageList iPicList = new ImageList();
public Form1()
{
InitializeComponent();
DirectoryInfo dirImages = new DirectoryInfo("C:\\2012");
iPicList.ImageSize = new Size(255, 255);
iPicList.TransparentColor = Color.White;
myGraphics = Graphics.FromHwnd(panel1.Handle);
foreach (FileInfo file in dirImages.GetFiles())
{
if (file.Extension == ".jpg")
{
Image myImage = Image.FromFile(file.FullName);
iPicList.Images.Add(myImage);
}
}
if (iPicList.Images.Empty != true)
{
panel1.Refresh();
currentImage = 0;
// Draw the image in the panel.
iPicList.Draw(myGraphics, 1, 1, currentImage);
// Show the image in the PictureBox.
pictureBox1.Image = iPicList.Images[currentImage];
label1.Text = "Image #" + currentImage;
}
}
private void showImage(int imgIndex)
{
// Draw the image in the panel.
iPicList.Draw(myGraphics, 1, 1, currentImage);
// Show the image in the PictureBox.
pictureBox1.Image = iPicList.Images[currentImage];
label1.Text = "image #" + currentImage;
panel1.Refresh();
}
private void button1_Click(object sender, EventArgs e)
{
if (iPicList.Images.Count - 1 > currentImage)
{
currentImage++;
}
else
{
currentImage = 0;
}
showImage(currentImage);
}
private void button2_Click(object sender, EventArgs e)
{
if (iPicList.Images.Count - 1 >= currentImage)
{
if (currentImage == 0)
currentImage = iPicList.Images.Count-1;
else
currentImage--;
}
else
{
currentImage = iPicList.Images.Count;
}
showImage(currentImage);
}
private void pictureBox1_DoubleClick(object sender, EventArgs e)
{
MessageBox.Show("Picture Box Double clicked");
}
private void pictureBox1_Click(object sender, EventArgs e)
{
panel1.Refresh();
myGraphics.DrawRectangle(Pens.Black, 0, 0, iPicList.Images[currentImage].Width + 1, iPicList.Images[currentImage].Height + 1);
pictureBox1.Image = iPicList.Images[currentImage];
}
}
}
The changes you need are:
Change the Following folder to a place where you have lots of jpg:
DirectoryInfo dirImages = new DirectoryInfo("C:\\2012");
Also if you are dealing with other kind of images, make change here:
if (file.Extension == ".jpg") // Change it to your image type.
If you don't want to use the the buttons to go up and down, you have several other options to host PictureBox control in scrollable Panel or list or something else.