【问题标题】:ComboBox Text Align Vertically Center组合框文本垂直居中对齐
【发布时间】:2018-09-23 20:59:36
【问题描述】:

我在 .net 框架 1.1 上创建了自定义组合框,我可以自定义绘制下拉项,但我无法在左中设置或绘制组合框文本,组合框文本始终呈现左上角,但我需要呈现文本在左中。

[ToolboxBitmap(typeof(ComboBox))]
public class MenComboBox :ComboBox
{
    private Image _image = Image.FromFile("Expand.png");
    public MenComboBox()
    {
        this.DrawMode = DrawMode.OwnerDrawFixed;
        this.BackColor = Color.White;
        this.ItemHeight = 18;
        this.Font = new Font("Arial",12f,FontStyle.Regular);
    }       
    protected override void OnDrawItem(DrawItemEventArgs e)
    {

            if (!DesignMode)
            {
                if (e.Index > -1)
                {
                    int textHeight = (int)e.Graphics.MeasureString(this.Items[e.Index].ToString(), e.Font).Height;
                    Point textPos = new Point(e.Bounds.X + 4, e.Bounds.Y + ((this.ItemHeight - textHeight) / 2));
                    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                    {
                        e.Graphics.FillRectangle(Brushes.Blue, e.Bounds);
                        e.Graphics.DrawString(this.Items[e.Index].ToString(),e.Font,Brushes.White,textPos);
                    }
                    else
                    {
                        e.Graphics.FillRectangle(Brushes.White, e.Bounds);
                        e.Graphics.DrawString(this.Items[e.Index].ToString(),e.Font,Brushes.Black,textPos);
                    }
                }
            }

    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == 0x000F)
        {
            using (Graphics g = this.CreateGraphics())
            {
                g.FillRectangle(new SolidBrush(BackColor), this.ClientRectangle);
                g.DrawRectangle(Pens.Blue, new Rectangle(this.ClientRectangle.X, this.ClientRectangle.Y, this.ClientRectangle.Width - 1, this.ClientRectangle.Height - 1));
                Rectangle rect = new Rectangle(this.Width - 15, 3, 12, this.Height - 6);
                g.FillRectangle(new SolidBrush(BackColor), rect);
                g.DrawImage(this._image, this.Width - 16, (this.Height - 8) / 2);
                g.Dispose();
            }
        }
    }   
}

【问题讨论】:

  • 删除 try/catch 并显示错误是什么?请注意,捕获所有异常是毫无意义的,并且是您做错了的强烈迹象
  • 文本框部分没有让文本居中对齐的选项,自定义绘制也无法修复它。用空格填充文本可能是一个有点愚蠢的解决方法。嗯,用户完全习惯了组合框的外观。

标签: c# .net winforms combobox .net-1.1


【解决方案1】:

在所有者绘制ComboBox 中,控件的Edit 部分的文本将始终显示在左上角,无论ItemHeight 的高度如何。

要将Edit 部分垂直放置在中间,您可以使用GetComboBoxInfo 找到Edit 元素,然后使用SetWindowPos 设置一个新位置,使其垂直位于ComboBox 的中间。

当控件大小发生变化时,您需要重新定位它。你还需要用颜色填充ComboBox 的背景。

这是我使用的代码:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyComboBox : ComboBox
{
    public MyComboBox()
    {
        SetStyle(ControlStyles.ResizeRedraw, true);
        DrawMode = DrawMode.OwnerDrawFixed;
        ItemHeight = 40;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        public int Width { get { return Right - Left; } }
        public int Height { get { return Bottom - Top; } }
    }

    private const int SWP_NOSIZE = 0x0001;
    private const int SWP_NOZORDER = 0x0004;
    private const int SWP_SHOWWINDOW = 0x0040;
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, 
        int X, int Y, int cx, int cy, int uFlags);

    [DllImport("user32.dll")]
    public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);

    [StructLayout(LayoutKind.Sequential)]
    public struct COMBOBOXINFO
    {
        public int cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int stateButton;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }
    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        SetupEdit();
        Invalidate();
    }
    private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0xF)
        {
            using (var g = this.CreateGraphics())
            {
                var r = new Rectangle(2, 2,
                    ClientRectangle.Width - buttonWidth - 2,
                    ClientRectangle.Height - 4);
                g.FillRectangle(Brushes.White, r);
            }
        }
        base.WndProc(ref m);
    }
    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        SetupEdit();
    }
    private void SetupEdit()
    {
        var info = new COMBOBOXINFO();
        info.cbSize = Marshal.SizeOf(info);
        GetComboBoxInfo(this.Handle, ref info);
        SetWindowPos(info.hwndEdit, IntPtr.Zero, 3,
            (this.Height - Font.Height) / 2,
            ClientRectangle.Width - buttonWidth - 3,
            ClientRectangle.Height - Font.Height - 4,
            SWP_NOZORDER);
    }
    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        base.OnDrawItem(e);
        e.DrawBackground();
        var txt = "";
        if (e.Index >= 0)
            txt = GetItemText(Items[e.Index]);
        TextRenderer.DrawText(e.Graphics, txt, Font, e.Bounds, 
            ForeColor, TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
    }
}

【讨论】:

  • 但这实际上会显示居中和可编辑的文本吗?
  • @TaW 是的,为了减少混乱,我更改了屏幕截图。事实上,我改变了 Edit 控件的位置。我没有画它。
  • 嗯,我很困惑。特别是当我想知道OP如何使文本出现在左上角时;它当然不会在这里这样做。也许是 .Net .1.1 的东西..?
  • @TaW 将DrawMode 更改为OwnerDrawFixed 并将ItemHeight 设置为40 例如,您将看到编辑部分将始终显示在左上方。
  • @TaW 想象一个所有者绘制组合框like this。在这种情况下,项目高度应该设置为大于字体高度的值。
【解决方案2】:

好的,下面的代码没有回答有关文本部分的实际问题;汉斯做对了,像往常一样。

我保留答案是因为我认为它比 OP 代码做得更好..

    if (!DesignMode)
    {
        if (e.Index > -1)
        {
           using (StringFormat fmt = new StringFormat() 
             { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
           {

            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
            {
                e.Graphics.FillRectangle(SystemBrushes.MenuHighlight, e.Bounds);
                e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), 
                                        e.Font,SystemBrushes.HighlightText, e.Bounds, fmt);
            }
            else
            {
                e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
                e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), 
                                        e.Font, SystemBrushes.MenuText,e.Bounds, fmt);
            }
         }
      }
    }

我没有计算居中位置,而是使用DrawString 重载,它采用目标矩形并将StringFormat 添加到两个方向的居中。 StringFormat 从 .Net 1.1 开始可用,并且确实是 IDisposable,所以我们应该处理我们创建的每一个,最好在 using 子句中......

请注意,对于绘图控件,鼓励使用 TextRenderer,但仅随 .Net 2.0 提供。

另外请注意,我用Brushes 替换了SystemBrushes..

另外:我的 ComboBox 不会将文本放在其文本部分的左上角而是左中角。也许旧的 .Net1.1 控件是罪魁祸首?

【讨论】:

  • 信不信由你,StringFormat 实际上实现了 IDisposable。去图吧。
  • 好的,我相信.. 感谢您跟踪我的资料 ;-)
  • 我不明白你为什么说这不能回答 OP 问题。此外,如果将DropDownStyle 设置为DropDownList,“编辑器”部分中的文本将居中。因此,也有两种可能的方式来呈现内容。我认为这正是我们所要求的。
  • 好吧,虽然 DropDownList 版本是一个很好的观察,(谢谢!)OP 清楚地显示了一个可编辑的文本字段。 Otoh 它还显示了非居中的下拉菜单,但代码至少似乎试图居中..
猜你喜欢
  • 1970-01-01
  • 2013-06-06
  • 2013-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-13
  • 2019-11-17
相关资源
最近更新 更多