【问题标题】:WinForms combobox with multiple columns (C#)?具有多列的 WinForms 组合框(C#)?
【发布时间】:2010-11-08 15:36:59
【问题描述】:

我目前正在使用以下代码来填充组合框:

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

有没有办法显示多列。我为 DisplayMember 尝试了“Auftragsnummer, Kunde, Beschreibung”,但没有成功。

【问题讨论】:

    标签: c# winforms combobox


    【解决方案1】:

    这是我从 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”。不过下拉菜单看起来不错。
    • 更进一步.. 对于 Colin,我在“OnDrawItem”中添加了一些代码,即 if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) { string selectedItem = SelectedValue.ToString (); base.Text = selectedItem.ToString();我已经更新了代码....您需要在主应用程序中将“DisplayMember”和“ValueMember”设置为“
    • Further even more....... 我在主代码中扩展了 ESC 键处理,因此通过添加以下内容在按 ESC 时清除文本: this.DroppedDown = false; this.SelectedIndex = -1; this.Text = null;
    【解决方案2】:

    我为多列组合框提供的完整 C# 解决方案在使用它的应用程序中进行调试时导致了问题。 问题在于“DateTime dt = DateTime.Parse(strColumnText);”这一行在方法中 “日期” 这是使用 DateTimeTryparse" 的方法的新版本: 我已经替换了原始版本中的错误版本。 (注:尽管它的所有缺点,它都有效!) 干杯, 布鲁斯·考德威尔

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

    【讨论】:

      【解决方案3】:

      简单快捷! 看看这个……

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

      【讨论】:

        【解决方案4】:

        MSDN 上有一篇文章描述了如何创建多列组合框。

        如何为组合框创建多列下拉列表 Windows 窗体

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


        来自上述 Microsoft 链接的 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
        

        【讨论】:

        • 比听起来容易得多,而且它还让您有机会根据条件更改颜色,更改分隔线的颜色等。很好的解决方案。
        【解决方案5】:

        MultiColumn ComboBox Control结合了Text Box控件来编辑和下拉列表中的Grid View来显示数据。

        【讨论】:

          【解决方案6】:

          您可以向数据集添加一个虚拟列 (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";
          

          简单而有效。

          【讨论】:

          • 或者你可以创建一个简单的转换器
          【解决方案7】:

          它在 .NET 中不可用(无论是 Windows 窗体还是 asp.net 的下拉列表) 查看此代码项目项目以获取有关如何构建自己的参考。 (不过还有更多)。

          Code Project

          【讨论】:

          • 感谢您的建议,但我先用谷歌搜索,我也遇到了这些项目。无论如何,我还是决定问,因为在我看来,有些项目有点老了,我想以某种方式明确回答没有在组合框中实现多列的标准方法。
          • 它们已经很老了,但它们仍应为您提供有关如何创建自己的“OwnerDrawn”组合框的答案。
          【解决方案8】:

          快速解决方案
          据我所知,数据表应该是部分类

          1. 为您的数据表创建第二个文件 MyDataTable.custom.cs
          2. 在名为的部分数据表类中添加一个字符串属性 “显示属性”
          3. 在该属性中返回一个 string.format("{0} {1} {2}", Auftragsnummer, 昆德语, Beschreibung);
          4. 将您的 Datamember 绑定到您的 DisplayProperty

          【讨论】:

          • 如果您可以更改数据表布局,计算列会更好
          【解决方案9】:

          您不能有多个列。虽然您可以将多个字段串联为显示成员

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

          【讨论】:

            【解决方案10】:

            有一篇关于代码项目的文章描述了如何创建多列组合框。

            Multicolumn Combobox - Code Project

            【讨论】:

              【解决方案11】:

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

              使用DataGridView 会不会更好

              【讨论】:

              • 它在.NET框架中不存在的事实并不意味着你不能拥有它......你可以自己编码,或者使用第三方控件
              • 认为他希望通过 .NET 中的标准控件来做到这一点
              • 谢谢詹姆斯。现在我将使用 DataGridView 来代替,因为我确信 .Net 中没有现成的多列组合框。
              • 你说得对,詹姆斯。我在 .Net 中寻找标准控件。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-06-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多