带有多列的WinForms组合框(C#)?

17

我现在正在使用以下代码来填充一个下拉框:

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

有没有一种方式可以显示多列?我尝试了在 DisplayMember 中使用"Auftragsnummer, Kunde, Beschreibung",但它没有起作用。

11个回答

12

10

MSDN上有一篇文章介绍如何创建一个多列组合框。

在Windows Forms中为组合框创建具有多个列的下拉列表

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


从上述微软链接的VB下载中获取的源代码,可以轻松地适应于ListBox和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

比听起来容易得多,而且它还给了你根据条件改变颜色、改变分隔符颜色等的机会。是一个不错的解决方案。 - Wade Hatler

7
您可以在数据集中添加一个虚拟列(Description),并将其用作组合框数据绑定中的 DisplayMember
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";

简单易用。

或者您可以创建一个简单的转换器。 - S3ddi9

3

在.NET中(无论是Windows表单还是asp.net的下拉列表),它不是开箱即用的。

查看此代码项目项以了解如何构建自己的下拉列表(尽管还有很多其他选项)。

代码项目


感谢您的建议,但我先在谷歌上搜索了一下,也找到了那些项目。不过我还是决定来问一下,因为我感觉有些项目可能有点过时了,而且我想要一个明确的答案:实现下拉框中的多列并没有标准的方法。 - user134146
它们虽然有些老旧,但仍应该能为您提供如何创建自己的“OwnerDrawn”组合框的答案。 - Colin

2

2

这是我从Visual Basic转换过来的C#完整解决方案。

我已经使用它8年了。请注意,日期格式适用于非美国用户。

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


    }
}

这对我来说运作得很好,但是如何使所选行的显示文本显示一个单元格中的字符串?现在,无论我选择哪一行,它都会在组合框中显示“System.Data.DataRow”。下拉菜单看起来不错。 - Collin Brittain
进一步地,为了 Colin,我已经在 "OnDrawItem" 中添加了一些代码,即 if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { string selectedItem = SelectedValue.ToString(); base.Text = selectedItem.ToString(); }我已经更新了代码... 你需要在主应用程序中将 "DisplayMember" 和 "ValueMember" 设置为 "<column_name>"。 - Bruce Caldwell
进一步甚至更多......我已经在主代码中扩展了ESC键处理,以便在按ESC时清除文本,方法是添加以下内容:                 this.DroppedDown = false;                 this.SelectedIndex = -1;                 this.Text = null; - Bruce Caldwell

0

容易而快速!看看这个...

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

0

你不能拥有一个多列组合框。

你不如使用DataGridView会更好。


它在.NET框架中不存在并不意味着你不能拥有它... 你可以自己编写代码,或使用第三方控件。 - Thomas Levesque
他似乎想通过.NET中的标准控件来实现这一点。 - James
谢谢James。既然我确定在.Net中没有开箱即用的多列组合框,我现在会使用DataGridView。 - user134146
你是对的,詹姆斯。我在寻找 .Net 中的标准控件。 - user134146

0

快速解决方案
据我所知 Datatables 应该是部分类。

  1. 为你的 datatable 创建第二个文件 MyDataTable.custom.cs
  2. 在部分 datatable 类中添加一个名为“DisplayProperty”的字符串属性
  3. 在该属性中返回一个字符串格式化的结果("{0} {1} {2}", Auftragsnummer, Kunde, Beschreibung)
  4. 将你的 Datamember 绑定到 DisplayProperty 上

如果您可以更改数据表的布局,计算列甚至会更好。 - Peter Gfader

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接