代码之家  ›  专栏  ›  技术社区  ›  Riddari

允许每个项目在WinForms组合框(或列表框)中使用多行

  •  2
  • Riddari  · 技术社区  · 15 年前

    这能相对容易地做到吗?

    2 回复  |  直到 11 年前
        1
  •  1
  •   PandaWood    11 年前

    我能在15分钟内完成以下任务,所以是的。主要的想法是处理drawitem事件。

    下面是我对这个问题的看法(您可以看到另一个例子,在项目中绘制图标 here )

    public partial class Form1 : Form  
    {
        public Form1()
        {
            InitializeComponent();
    
            this.comboBox1.DrawMode = DrawMode.OwnerDrawVariable;            
            this.comboBox1.DrawItem += new DrawItemEventHandler(comboBox1_DrawItem);
            this.comboBox1.Items.Add("Some text that needs to be take up two lines...");
            this.comboBox1.ItemHeight = 30;
        }
    
        IEnumerable<string> WrapString(string str, Graphics g, Font font, 
                                       int allowedWidth)
        {            
            string[] arr = str.Split(' ');            
            StringBuilder current = new StringBuilder();
            foreach (string token in arr)
            {                
                // TODO: You'll have to fix this, might break in marginal cases
                int width = 
                  (int)g.MeasureString(current.ToString() + " " + token, font).Width;
                if (width > allowedWidth)
                {
                    yield return current.ToString();
                    current.Clear();
                }
    
                current.Append(token + " ");
    
            }
            yield return current.ToString();
        }
    
        void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
        {
            Brush backgroundBrush, forgroundBrush;
    
            if (e.State == (DrawItemState.Selected | 
                        DrawItemState.NoAccelerator | DrawItemState.NoFocusRect) ||
                e.State == DrawItemState.Selected) 
            {
                forgroundBrush = Brushes.Black;
                backgroundBrush = Brushes.White;
            }
            else
            {
                forgroundBrush = Brushes.White;
                backgroundBrush = Brushes.Black;
            }
    
            // some way to wrap the string (on a space)           
            string str = (string)comboBox1.Items[e.Index];
    
    
            Rectangle rc = 
               new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
            e.Graphics.FillRectangle(forgroundBrush, rc);
    
            int stringHeight = 
                   (int)e.Graphics.MeasureString(str, comboBox1.Font).Height;
            int lineNo = 0;
            foreach (string line in 
                          WrapString(str, e.Graphics, comboBox1.Font, e.Bounds.Width))
            {
                e.Graphics.DrawString(line, comboBox1.Font, backgroundBrush, 
                                      new PointF(0, lineNo * stringHeight + 5));
                lineNo++;
            }            
        }             
    }
    

    用法:创建一个常规表单并在其上放置一个组合框。

    (请注意,这当然只是一个幼稚的概念证明——显然还有改进的空间。而且它只是假设只有两行而不是一行。但这表明这是可能的。)

        2
  •  1
  •   DDA Naimish B. Makwana    12 年前

    我发现这门课是蒂姆·麦基做的,在我的项目中效果很好。( Tim's Blog Entry ):

    C版本:

    using System;
    using System.Drawing;
    using System.Linq;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Collections.Generic;
    
    namespace HortLaptopApp
    {
        class ComboBoxWrap : ComboBox
        {            
            [DllImport("user32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
    
            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
    
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int Left;        // x position of upper-left corner
                public int Top;         // y position of upper-left corner
                public int Right;       // x position of lower-right corner
                public int Bottom;      // y position of lower-right corner
            }
    
            public const int SWP_NOZORDER = 0x0004;
            public const int SWP_NOACTIVATE = 0x0010;
            public const int SWP_FRAMECHANGED = 0x0020;
            public const int SWP_NOOWNERZORDER = 0x0200;
    
            public const int WM_CTLCOLORLISTBOX = 0x0134;
    
            private int _hwndDropDown = 0;
    
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_CTLCOLORLISTBOX)
                {
                    if (_hwndDropDown == 0)
                    {
                        _hwndDropDown = m.LParam.ToInt32();
    
                        RECT r;
                        GetWindowRect((IntPtr)_hwndDropDown, out r);
    
                        //int newHeight = 0;
                       // for(int i=0; i<Items.Count && i < MaxDropDownItems; i++)
                        //    newHeight += this.GetItemHeight(i);
    
                        int total = 0;
                        for (int i = 0; i < this.Items.Count; i++)
                            total += this.GetItemHeight(i);
                        this.DropDownHeight = total + SystemInformation.BorderSize.Height * (this.Items.Count + 2);
    
    
                        SetWindowPos((IntPtr)_hwndDropDown, IntPtr.Zero,
                            r.Left,
                                     r.Top,
                                     DropDownWidth,
                                     DropDownHeight,
                                     SWP_FRAMECHANGED |
                                         SWP_NOACTIVATE |
                                         SWP_NOZORDER |
                                         SWP_NOOWNERZORDER);
                    }
                }
    
                base.WndProc(ref m);
            }
    
            protected override void OnDropDownClosed(EventArgs e)
            {
                _hwndDropDown = 0;
                base.OnDropDownClosed(e);
            }
    
            public ComboBoxWrap() : base()
            {
                // add event handlers
                this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
                this.DrawItem += new DrawItemEventHandler(ComboBoxWrap_DrawItem);
                this.MeasureItem += new MeasureItemEventHandler(ComboBoxWrap_MeasureItem);
            }
    
            void ComboBoxWrap_MeasureItem(object sender, MeasureItemEventArgs e)
            {
                // set the height of the item, using MeasureString with the font and control width
                ComboBoxWrap ddl = (ComboBoxWrap)sender;
                string text = ddl.Items[e.Index].ToString();
                SizeF size = e.Graphics.MeasureString(text, this.Font, ddl.DropDownWidth); 
                e.ItemHeight = (int)Math.Ceiling(size.Height) + 1;  // plus one for the border
                e.ItemWidth = ddl.DropDownWidth;
                System.Diagnostics.Trace.WriteLine(String.Format("Height {0}, Text {1}", e.ItemHeight, text));
            }
    
            void ComboBoxWrap_DrawItem(object sender, DrawItemEventArgs e)
            {
                if (e.Index < 0)
                    return;
    
                // draw a lighter blue selected BG colour, the dark blue default has poor contrast with black text on a dark blue background
                if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                    e.Graphics.FillRectangle(Brushes.PowderBlue, e.Bounds);
                else
                    e.Graphics.FillRectangle(Brushes.White, e.Bounds);
    
                // get the text of the item
                ComboBoxWrap ddl = (ComboBoxWrap)sender;
                string text = ddl.Items[e.Index].ToString();
    
                // don't dispose the brush afterwards
                Brush b = Brushes.Black;
                e.Graphics.DrawString(text, this.Font, b, e.Bounds, StringFormat.GenericDefault);
    
                // draw a light grey border line to separate the items
                Pen p = new Pen(Brushes.Gainsboro, 1);
                e.Graphics.DrawLine(p, new Point(e.Bounds.Left, e.Bounds.Bottom-1), new Point(e.Bounds.Right, e.Bounds.Bottom-1));
                p.Dispose();
    
                e.DrawFocusRectangle();
            }
        }
    }
    

    VB版本:

    Imports System.Drawing
    Imports System.Linq
    Imports System.Windows.Forms
    Imports System.Runtime.InteropServices
    Imports System.Collections.Generic
    
    Namespace HortLaptopApp
        Class ComboBoxWrap
            Inherits ComboBox           
            <DllImport("user32.dll")> _
            Public Shared Function GetWindowRect(hwnd As IntPtr, ByRef lpRect As RECT) As <MarshalAs(UnmanagedType.Bool)> Boolean
            End Function
    
            <DllImport("user32.dll", SetLastError := True)> _
            Private Shared Function SetWindowPos(hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, _
                uFlags As UInteger) As <MarshalAs(UnmanagedType.Bool)> Boolean
            End Function
    
            <StructLayout(LayoutKind.Sequential)> _
            Public Structure RECT
                Public Left As Integer
                ' x position of upper-left corner
                Public Top As Integer
                ' y position of upper-left corner
                Public Right As Integer
                ' x position of lower-right corner
                Public Bottom As Integer
                ' y position of lower-right corner
            End Structure
    
            Public Const SWP_NOZORDER As Integer = &H4
            Public Const SWP_NOACTIVATE As Integer = &H10
            Public Const SWP_FRAMECHANGED As Integer = &H20
            Public Const SWP_NOOWNERZORDER As Integer = &H200
    
            Public Const WM_CTLCOLORLISTBOX As Integer = &H134
    
            Private _hwndDropDown As Integer = 0
    
            Protected Overrides Sub WndProc(ByRef m As Message)
                If m.Msg = WM_CTLCOLORLISTBOX Then
                    If _hwndDropDown = 0 Then
                        _hwndDropDown = m.LParam.ToInt32()
    
                        Dim r As RECT
                        GetWindowRect(DirectCast(_hwndDropDown, IntPtr), r)
    
                        'int newHeight = 0;
                        ' for(int i=0; i<Items.Count && i < MaxDropDownItems; i++)
                        '    newHeight += this.GetItemHeight(i);
    
                        Dim total As Integer = 0
                        For i As Integer = 0 To Me.Items.Count - 1
                            total += Me.GetItemHeight(i)
                        Next
                        Me.DropDownHeight = total + SystemInformation.BorderSize.Height * (Me.Items.Count + 2)
    
    
                        SetWindowPos(DirectCast(_hwndDropDown, IntPtr), IntPtr.Zero, r.Left, r.Top, DropDownWidth, DropDownHeight, _
                            SWP_FRAMECHANGED Or SWP_NOACTIVATE Or SWP_NOZORDER Or SWP_NOOWNERZORDER)
                    End If
                End If
    
                MyBase.WndProc(m)
            End Sub
    
            Protected Overrides Sub OnDropDownClosed(e As EventArgs)
                _hwndDropDown = 0
                MyBase.OnDropDownClosed(e)
            End Sub
    
            Public Sub New()
                MyBase.New()
                ' add event handlers
                Me.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable
                Me.DrawItem += New DrawItemEventHandler(AddressOf ComboBoxWrap_DrawItem)
                Me.MeasureItem += New MeasureItemEventHandler(AddressOf ComboBoxWrap_MeasureItem)
            End Sub
    
            Private Sub ComboBoxWrap_MeasureItem(sender As Object, e As MeasureItemEventArgs)
                ' set the height of the item, using MeasureString with the font and control width
                Dim ddl As ComboBoxWrap = DirectCast(sender, ComboBoxWrap)
                Dim text As String = ddl.Items(e.Index).ToString()
                Dim size As SizeF = e.Graphics.MeasureString(text, Me.Font, ddl.DropDownWidth)
                e.ItemHeight = CInt(Math.Ceiling(size.Height)) + 1
                ' plus one for the border
                e.ItemWidth = ddl.DropDownWidth
                System.Diagnostics.Trace.WriteLine([String].Format("Height {0}, Text {1}", e.ItemHeight, text))
            End Sub
    
            Private Sub ComboBoxWrap_DrawItem(sender As Object, e As DrawItemEventArgs)
                If e.Index < 0 Then
                    Return
                End If
    
                ' draw a lighter blue selected BG colour, the dark blue default has poor contrast with black text on a dark blue background
                If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                    e.Graphics.FillRectangle(Brushes.PowderBlue, e.Bounds)
                Else
                    e.Graphics.FillRectangle(Brushes.White, e.Bounds)
                End If
    
                ' get the text of the item
                Dim ddl As ComboBoxWrap = DirectCast(sender, ComboBoxWrap)
                Dim text As String = ddl.Items(e.Index).ToString()
    
                ' don't dispose the brush afterwards
                Dim b As Brush = Brushes.Black
                e.Graphics.DrawString(text, Me.Font, b, e.Bounds, StringFormat.GenericDefault)
    
                ' draw a light grey border line to separate the items
                Dim p As New Pen(Brushes.Gainsboro, 1)
                e.Graphics.DrawLine(p, New Point(e.Bounds.Left, e.Bounds.Bottom - 1), New Point(e.Bounds.Right, e.Bounds.Bottom - 1))
                p.Dispose()
    
                e.DrawFocusRectangle()
            End Sub
        End Class
    End Namespace
    
    推荐文章