17

I need to create a dropdown menu, or combobox, for a Windows Forms application which contains a small image and then a string of text next to it. Basically, you can think of each 'row' in the dropdown as needing to have an icon and then the name of the icon to the right of the icon. I am having trouble doing this -- in fact, I've been completely unsuccessful. Does anyone know of a way to accomplish this task? Any help will be greatly appreciated. Thanks!

JToland
  • 3,630
  • 12
  • 49
  • 70

5 Answers5

27

I was able to come up with some very simple code to perform this (see snippet below). The code creates a control that is a dropdown control which shows a small colored square and that color's name in the same row (see photo). Thanks for the links provided for this back when it was originally posted! Hopefully this control can help someone else out in the future.

Image:

Drop Down Color Selector


Code:

class ColorSelector : ComboBox
{
    public ColorSelector()
    {
        DrawMode = DrawMode.OwnerDrawFixed;
        DropDownStyle = ComboBoxStyle.DropDownList;
    }
 
    // Draws the items into the ColorSelector object
    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        e.DrawBackground();
        e.DrawFocusRectangle();
 
        DropDownItem item = new DropDownItem(Items[e.Index].ToString());
        // Draw the colored 16 x 16 square
        e.Graphics.DrawImage(item.Image, e.Bounds.Left, e.Bounds.Top);
        // Draw the value (in this case, the color name)
        e.Graphics.DrawString(item.Value, e.Font, new
                SolidBrush(e.ForeColor), e.Bounds.Left + item.Image.Width, e.Bounds.Top + 2);
 
        base.OnDrawItem(e);
    }
}
 
public class DropDownItem
{
    public string Value
    {
        get { return value; }
        set { this.value = value; }
    }
    private string value;
 
    public Image Image
    {
        get { return img; }
        set { img = value; }
    }
    private Image img;
 
    public DropDownItem() : this("")
    {}
 
    public DropDownItem(string val)
    {
        value = val;
        this.img = new Bitmap(16, 16);
        Graphics g = Graphics.FromImage(img);
        Brush b = new SolidBrush(Color.FromName(val));
        g.DrawRectangle(Pens.White, 0, 0, img.Width, img.Height);
        g.FillRectangle(b, 1, 1, img.Width - 1, img.Height - 1);
    }
 
    public override string ToString()
    {
        return value;
    }
}
Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
JToland
  • 3,630
  • 12
  • 49
  • 70
  • 3
    If your combo box doesn't always have a selection, then you need to wrap your drawing code in `if (e.Index >= 0) {...}`. – Don Kirkby Apr 10 '13 at 20:54
  • @Ashkan Mobayen Khiabani Isn't there a `new` missing when you create the `DropDownItem item`? You are calling the constructor, right? – iron9 Sep 23 '20 at 15:13
7

Very helpful.. some optimisations :

public sealed class ColorSelector : ComboBox
{
    public ColorSelector()
    {
        DrawMode = DrawMode.OwnerDrawFixed;
        DropDownStyle = ComboBoxStyle.DropDownList;
    }

    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        e.DrawBackground();

        e.DrawFocusRectangle();

        if (e.Index >= 0 && e.Index < Items.Count)
        {
            DropDownItem item = (DropDownItem)Items[e.Index];

            e.Graphics.DrawImage(item.Image, e.Bounds.Left, e.Bounds.Top);

            e.Graphics.DrawString(item.Value, e.Font, new SolidBrush(e.ForeColor), e.Bounds.Left + item.Image.Width, e.Bounds.Top + 2);
        }

        base.OnDrawItem(e);
    }
}

and ...

public sealed class DropDownItem
{
    public string Value { get; set; }

    public Image Image { get; set; }

    public DropDownItem()
        : this("")
    { }

    public DropDownItem(string val)
    {
        Value = val;
        Image = new Bitmap(16, 16);
        using (Graphics g = Graphics.FromImage(Image))
        {
            using (Brush b = new SolidBrush(Color.FromName(val)))
            {
                g.DrawRectangle(Pens.White, 0, 0, Image.Width, Image.Height);
                g.FillRectangle(b, 1, 1, Image.Width - 1, Image.Height - 1);
            }
        }
    }

    public override string ToString()
    {
        return Value;
    }
}
que dal
  • 139
  • 1
  • 1
2

I resolved the problem, i did this:

ComboBox MarcadorNS = new ComboBox();
MarcadorNS.Height = 30;
MarcadorNS.Width = 150;
MarcadorNS.SelectedValuePath = "Uid";
foreach (var temporalItem in GetPredefinedKinds())
{
    Image ImagenCombo = new Image();
    ImagenCombo.Source =
    new BitmapImage(new Uri(
       "Imagenes/Marcadores/" +
        temporalItem.Name.ToLower() + ".png", UriKind.Absolute));
    ImagenCombo.Height = 28;
    ImagenCombo.Width = 28;
    ImagenCombo.VerticalAlignment = VerticalAlignment.Top;
    ImagenCombo.HorizontalAlignment = HorizontalAlignment.Left;
    Label textoCombo = new Label();
    textoCombo.VerticalAlignment = VerticalAlignment.Top;
    textoCombo.HorizontalAlignment = HorizontalAlignment.Left;
    textoCombo.Content = BaseDatos.NombresDeMarcadores(temporalItem.ToString());
    Grid GridCombo = new Grid();
    GridCombo.Uid = ObtenerMarcador(temporalItem.ToString());
    StackPanel stackCombo = new StackPanel();
    stackCombo.Orientation = Orientation.Horizontal;
    stackCombo.Children.Add(ImagenCombo);
    stackCombo.Children.Add(textoCombo);
    GridCombo.Children.Add(stackCombo);
    MarcadorNS.Items.Add(GridCombo);
}

enter image description here

dontbyteme
  • 1,221
  • 1
  • 11
  • 23
Chrish
  • 29
  • 2
  • 2
    Its better to try and explain what your code is doing than just dumping it in an answer - other people reading the question would find it more useful then. Really the original question isn't great as its not showing any attempt at research. – Tim Rutter Mar 30 '16 at 14:25
1

NOTE: This code is from user que dal's optimization If you wish to have a string that isn't just the name of the color, change DropDownItem to have 2 arguments, the string and the color, then just change how the brush sets the color, as such:

    public DropDownItem(string val, Color color)
    {
        Value = val;
        Image = new Bitmap(16, 16);
        using (Graphics g = Graphics.FromImage(Image))
        {
            using (Brush b = new SolidBrush(color))
            {
                g.DrawRectangle(Pens.White, 0, 0, Image.Width, Image.Height);
                g.FillRectangle(b, 1, 1, Image.Width - 1, Image.Height - 1);
            }
        }
    }

You must then change the dropdown item as such:

    public DropDownItem()
        : this("", Color.Empty)
    {}

Hope this was helpful :)

-13

Not sure about images but this should work for the strings:

comboBox.Items.Add("String");
Zack
  • 17
  • 1
  • 4