user134146
user134146

Reputation:

WinForms combobox with multiple columns (C#)?

I am using currently the following code to populate a combobox:

combobox.DataSource = datatable;
combobox.DisplayMember = "Auftragsnummer";
combobox.ValueMember = "ID";

Is there a way to display multiple columns. I tried "Auftragsnummer, Kunde, Beschreibung" for DisplayMember but it did not work.

Upvotes: 17

Views: 106511

Answers (11)

Bruce Caldwell
Bruce Caldwell

Reputation: 21

Here's the full solution in C# which I've converted from Visual Basic.

I've been using that for the past 8 years. Note the date format is for non-American users.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Windows.Forms;

namespace MultiColumnCombcs
{
    public partial class MultiColumnCombocs: ComboBox
    {
    // Hide some properties
    [Browsable(false)]
    public new bool IntegralHeight { get; set; }

    [Browsable(false)]
    public new DrawMode DrawMode { get; set; }

    [Browsable(false)]
    public new int DropDownHeight { get; set; }

    [Browsable(false)]
    public new ComboBoxStyle DropDownStyle { get; set; }

    [Browsable(false)]
    public new bool DoubleBuffered { get; set; }

    public Boolean paintHandled = false;
    public const int WM_PAINT = 0xF;
    public int intScreenMagnification = 125; // Screen Magnification = 125%
    // Dropdown
    private string _columnWidths;
    private string[] columnWidthsArray;

    // 'Combo Box
    private Color _buttonColor = Color.Gainsboro;
    private Color _borderColor = Color.Gainsboro;
    private readonly Color bgSelectedColor = Color.PaleGreen; 
    private readonly Color textselectedcolor = Color.Red;
    private readonly Color bgColor = Color.White;
    private readonly Color lineColor = Color.White;
   
    private Brush backgroundBrush = new SolidBrush(SystemColors.ControlText);
    private Brush arrowBrush = new SolidBrush(Color.Black);
    private Brush ButtonBrush = new SolidBrush(Color.Gainsboro);

    //Properties
    [Browsable(true)]
    public Color ButtonColor
    {
        get
        {
            return _buttonColor;
        }
        set
        {
            _buttonColor = value;
            ButtonBrush = new SolidBrush(this.ButtonColor);
            this.Invalidate();
        }
    }

    [Browsable(true)]
    public Color BorderColor
    {
        get
        {
            return _borderColor;
        }
        set
        {
            _borderColor = value;
            this.Invalidate();
        }
    }
    //Column Widths to be set in Properties Window as string containing width of each column in Pixels, 
    // delimited by ';' eg 15;45;40;100,50;40 for six columns

    [Browsable(true)]
    public string ColumnWidths 
    {
        get
        {
        if (string.IsNullOrEmpty(_columnWidths))
            {
                _columnWidths = "15";  //default value
            }

            return _columnWidths;
        }
        set
        {
            _columnWidths = value;
            // split Column Widths string into Array of substrings delimited by ';' character
            columnWidthsArray = _columnWidths.Split(System.Convert.ToChar(";")); 
            int w = 0;
            foreach (string str in columnWidthsArray)
                w += System.Convert.ToInt32(System.Convert.ToInt32(str) * intScreenMagnification / 100);// ******
            DropDownWidth = (w + 20);
        }
    }

    // Constructor stuff
    public MultiColumnCombocs() : base()
    {
        base.IntegralHeight = false;
        base.DrawMode = DrawMode.OwnerDrawFixed;
        base.DropDownStyle = ComboBoxStyle.DropDown;
        MaxDropDownItems = 12;
        // Minimise flicker in painted control
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void WndProc(ref Message m)  // Listen for operating system messages
    {   
        base.WndProc(ref m);  /*Inheriting controls should call the base class's WndProc(Message) method
                                to process any messages that they do not handle.*/
        switch (m.Msg)
        {
            case WM_PAINT:
                // Draw Combobox and dropdown arrow
                Graphics g = this.CreateGraphics();
                // Background - Only the borders will show up because the edit box will be overlayed
                try
                {
                    backgroundBrush = new SolidBrush(Color.White);
                    g.FillRectangle(backgroundBrush, 0, 0, Size.Width, Size.Height);
                    // Border
                    Rectangle rectangle = new Rectangle();
                    Pen pen = new Pen(BorderColor, 2);
                    rectangle.Size = new Size(Width - 2, Height);
                    g.DrawRectangle(pen, rectangle);
                    
                    // Background of the dropdown button
                    ButtonBrush = new SolidBrush(ButtonColor);
                    Rectangle rect = new Rectangle(Width - 15, 0, 15, Height);
                    g.FillRectangle(ButtonBrush, rect);

                    // Create the path for the arrow
                    g.SmoothingMode = SmoothingMode.AntiAlias;

                    GraphicsPath pth = new GraphicsPath();
                    PointF TopLeft = new PointF(Width - 12, System.Convert.ToSingle((Height - 5) / 2));
                    PointF TopRight = new PointF(Width - 5, System.Convert.ToSingle((Height - 5) / 2));
                    PointF Bottom = new PointF(Width - 8, System.Convert.ToSingle((Height + 4) / 2));
                    pth.AddLine(TopLeft, TopRight);
                    pth.AddLine(TopRight, Bottom);

                    // Determine the arrow and button's color.
                    arrowBrush = new SolidBrush(Color.Black);

                    if (this.DroppedDown)
                    {
                        arrowBrush = new SolidBrush(Color.Red);
                        ButtonBrush = new SolidBrush(Color.PaleGreen);
                    }
                    // Draw the arrow
                    g.FillRectangle(ButtonBrush, rect);
                    g.FillPath(arrowBrush, pth);

                    pen.Dispose();
                    pth.Dispose();

                }
                finally
                {
                    // Cleanup
                    g.Dispose();
                    arrowBrush.Dispose();
                    backgroundBrush.Dispose();
                }
                break;
               
           default:
              {
                break;
              }
        }
    }

    protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
    {
        // Draw Dropdown with Multicolumns
        Cursor = Cursors.Arrow;
        DataRowView row = (DataRowView)base.Items[e.Index];

        int newpos = e.Bounds.X;
        int endpos = e.Bounds.X;
        int intColumnIndex = 0;

        // Draw the current item text based on the current Font and the custom brush settings
        foreach (string str in columnWidthsArray)
        {
            // paint each column, "intColumnIndex" is local integer
            string strColWidth = columnWidthsArray[intColumnIndex];
            int ColLength = System.Convert.ToInt32(strColWidth);
            // Adjust ColLength
            ColLength = System.Convert.ToInt32(ColLength * intScreenMagnification / 100); // ******
            endpos += ColLength;

            string strColumnText = row[intColumnIndex].ToString();

            if (IsDate(strColumnText))  //Format Date as 'dd-MM-yy' (not avail as 'ToString("Format")'
            {
                strColumnText = strColumnText.Replace("/", "-");
                string strSaveColumn = strColumnText;
                strColumnText = strSaveColumn.Substring(0, 6) + strSaveColumn.Substring(8, 2);
                ColLength = 40;
            }

            // Paint Text
            if (ColLength > 0)
            {
                RectangleF r = new RectangleF(newpos + 1, e.Bounds.Y, endpos - 1, e.Bounds.Height);
                // Colours of normal row and text

                //   Colours of normal row and text
                SolidBrush textBrush = new SolidBrush(Color.Black);
                SolidBrush backbrush = new SolidBrush(Color.White);
                StringFormat strFormat = new StringFormat();
                try 
                {
                    // Colours of selected row and text
                    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                    {
                        textBrush.Color = textselectedcolor; // Red
                        backbrush.Color = bgSelectedColor; // Pale Green
                    }
                    e.Graphics.FillRectangle(backbrush, r);

                    strFormat.Trimming = StringTrimming.Character;
                    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    e.Graphics.DrawString(strColumnText, e.Font, textBrush, r, strFormat);
                    e.Graphics.SmoothingMode = SmoothingMode.None;
                }
                finally
                { 
                    backbrush.Dispose();
                    strFormat.Dispose();
                    textBrush.Dispose();
                }

                //Separate columns with white border
                if (intColumnIndex > 0 && intColumnIndex <= (columnWidthsArray.Length))
                {
                    e.Graphics.DrawLine(new Pen(Color.White), endpos, e.Bounds.Y, endpos, ItemHeight * MaxDropDownItems);
                }
                newpos = endpos;
                intColumnIndex++;
            } // end if
        }// end for

        // load ValueMember value into combobox when using mouse on dropped down list
        if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
        {
          string selectedItem = SelectedValue.ToString();  
            base.Text = selectedItem.ToString();
        }
    } //end sub

      private bool IsDate(string strColumnText)
      {
        DateTime dateValue;

        if (DateTime.TryParse(strColumnText, out dateValue))
        {
            return true;
        }
        else
        {
            return false;
        }
     } // end sub

    protected override void OnMouseWheel(MouseEventArgs e)
    {
        // Overrides Sub of same name in parent class
        HandledMouseEventArgs MWheel = (HandledMouseEventArgs)e;
        // HandledMouseEventArgs prevents event being sent to parent container
        if (!this.DroppedDown)
        {
            MWheel.Handled = true;
        }
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);
        if (!this.DroppedDown & e.KeyCode == Keys.Down)
            this.DroppedDown = true;
        else if (e.KeyCode == Keys.Escape)
        {
            this.DroppedDown = false;
            this.SelectedIndex = -1;
            this.Text = null; 
        }
     }


    }
}

Upvotes: 2

Bruce Caldwell
Bruce Caldwell

Reputation: 21

The full C# solution I provided for a Multicolumn Combo Box caused a problem when debugging in the app that used it. The problem was with the line "DateTime dt = DateTime.Parse(strColumnText);" in the method "IsDate" here is a new version of the method that uses DateTimeTryparse" instead: I have replaced the faulty version in the original. (N B For all its faults, it works!) cheers, Bruce Caldwell

     private bool IsDate(string strColumnText)
    {
        DateTime dateValue;

        if (DateTime.TryParse(strColumnText, out dateValue))
        {
            return true;
        }
        else
        {
            return false;
        }
    } // end sub

Upvotes: 0

David Greenfeld
David Greenfeld

Reputation: 111

easy and quick! look at this...

combobox.Datasource = 
entities.tableName.Select(a => a.Coulmn1 + " " + a.Coulmn2).ToList();

Upvotes: 0

Irshad
Irshad

Reputation: 3131

There's an article on MSDN describing how a Multicolumn ComboBox can be created.

How to create a multiple-column drop-down list for a combo box in Windows Forms

http://support.microsoft.com/kb/982498


Source code from the download for VB from the above Microsoft Link, that can be easily adapted to work with a ListBox as well as a ComboBox:

'************************************* Module Header **************************************'
' Module Name:  MainForm.vb
' Project:      VBWinFormMultipleColumnComboBox
' Copyright (c) Microsoft Corporation.
' 
' 
' This sample demonstrates how to display multiple columns of data in the dropdown of a ComboBox.
' 
' This source is subject to the Microsoft Public License.
' See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
' All other rights reserved.
' 
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
' EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
' WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
'******************************************************************************************'

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Text
Imports System.Windows.Forms
Imports System.Drawing.Drawing2D

Public Class MainForm

    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim dtTest As DataTable = New DataTable()
        dtTest.Columns.Add("ID", GetType(Integer))
        dtTest.Columns.Add("Name", GetType(String))

        dtTest.Rows.Add(1, "John")
        dtTest.Rows.Add(2, "Amy")
        dtTest.Rows.Add(3, "Tony")
        dtTest.Rows.Add(4, "Bruce")
        dtTest.Rows.Add(5, "Allen")

        ' Bind the ComboBox to the DataTable
        Me.comboBox1.DataSource = dtTest
        Me.comboBox1.DisplayMember = "Name"
        Me.comboBox1.ValueMember = "ID"

        ' Enable the owner draw on the ComboBox.
        Me.comboBox1.DrawMode = DrawMode.OwnerDrawFixed
        ' Handle the DrawItem event to draw the items.
    End Sub

    Private Sub comboBox1_DrawItem(ByVal sender As System.Object, _
                                   ByVal e As System.Windows.Forms.DrawItemEventArgs) _
                                   Handles comboBox1.DrawItem
        ' Draw the default background
        e.DrawBackground()

        ' The ComboBox is bound to a DataTable,
        ' so the items are DataRowView objects.
        Dim drv As DataRowView = CType(comboBox1.Items(e.Index), DataRowView)

        ' Retrieve the value of each column.
        Dim id As Integer = drv("ID").ToString()
        Dim name As String = drv("Name").ToString()

        ' Get the bounds for the first column
        Dim r1 As Rectangle = e.Bounds
        r1.Width = r1.Width / 2

        ' Draw the text on the first column
        Using sb As SolidBrush = New SolidBrush(e.ForeColor)
            e.Graphics.DrawString(id, e.Font, sb, r1)
        End Using

        ' Draw a line to isolate the columns 
        Using p As Pen = New Pen(Color.Black)
            e.Graphics.DrawLine(p, r1.Right, 0, r1.Right, r1.Bottom)
        End Using

        ' Get the bounds for the second column
        Dim r2 As Rectangle = e.Bounds
        r2.X = e.Bounds.Width / 2
        r2.Width = r2.Width / 2

        ' Draw the text on the second column
        Using sb As SolidBrush = New SolidBrush(e.ForeColor)
            e.Graphics.DrawString(name, e.Font, sb, r2)
        End Using
    End Sub
End Class

Upvotes: 10

Leonards
Leonards

Reputation: 1

The MultiColumn ComboBox Control combines the Text Box control to edit and the Grid View in the drop down list to display data.

Upvotes: 0

bizabo
bizabo

Reputation: 71

You can add to your dataset a dummy column (Description) and use that as DisplayMember in the combo box databinding.

SELECT Users.*, Surname+' '+Name+' - '+UserRole AS Description FROM Users
ComboBox.DataBindings.Add(new Binding("SelectedValue", bs, "ID"));
ComboBox.DataSource = ds.Tables["Users"];
ComboBox.DisplayMember = "Description";
ComboBox.ValueMember = "ID";

Simple and works.

Upvotes: 7

Colin
Colin

Reputation: 10638

It's not available out-of-the-box in .NET (be it Windows forms or asp.net's dropdownlist) CHeck out this code project item for reference on how to build your own. (there are loads more though).

Code Project

Upvotes: 3

Peter Gfader
Peter Gfader

Reputation: 7751

Quick solution
Datatables should be partical classes as far as I remember

  1. Create a 2nd file for your datatable MyDataTable.custom.cs
  2. Add a string property in the partial datatable class called "DisplayProperty"
  3. In that property return a string.format("{0} {1} {2}", Auftragsnummer, Kunde, Beschreibung);
  4. Bind your Datamember to your DisplayProperty

Upvotes: 0

Rashmi Pandit
Rashmi Pandit

Reputation: 23848

You can't have multiple columns. Though you can have concatenation of multiple fields as Display Member

Check out: How do I bind a Combo so the displaymember is concat of 2 fields of source datatable?

Upvotes: 12

Pop Catalin
Pop Catalin

Reputation: 62970

There's an article on Code Project describing how a Multicolumn ComboBox can be created.

Multicolumn Combobox - Code Project

Upvotes: 2

James
James

Reputation: 82136

You can't have a multiple column combo-box.

Would you not be better off using a DataGridView instead

Upvotes: 0

Related Questions