Выравнивание текста ComboBox по вертикали по центру

Я создал настраиваемое поле со списком в .net framework 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();
            }
        }
    }   
}

мой собственный внешний вид списка со списком


person Vignesh Nethaji    schedule 13.04.2018    source источник
comment
Удалить try/catch и показать, в чем ошибка? И обратите внимание, что отлавливание всех исключений бессмысленно и является явным признаком того, что вы делаете неправильно.   -  person Evgeny Gorbovoy    schedule 13.04.2018
comment
Часть текстового поля не имеет возможности выровнять текст по центру, пользовательское рисование также не исправляет это. Заполнение текста пробелами может быть довольно глупым решением. Мех, пользователи полностью привыкли к внешнему виду и ощущениям от выпадающих списков.   -  person Hans Passant    schedule 13.04.2018


Ответы (2)


В отрисовке владельца ComboBox текст части Edit элемента управления всегда будет отображаться вверху слева, независимо от высоты ItemHeight.

Чтобы расположить часть Edit вертикально посередине, вы можете найти элемент Edit с помощью GetComboBoxInfo, а затем с помощью 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);
    }
}

введите описание изображения здесь

person Reza Aghaei    schedule 13.04.2018
comment
Но будет ли это отображать центрированный и редактируемый текст? - person TaW; 13.04.2018
comment
@TaW Да, чтобы не запутаться, я изменил скриншот. На самом деле я меняю положение элемента управления Edit. Я не рисую это. - person Reza Aghaei; 13.04.2018
comment
Хм, я в замешательстве. Тем более, что мне интересно, как OP может заставить текст отображаться в левом верхнем углу; это, конечно, не делает это здесь. Может быть, дело в .Net .1.1? - person TaW; 13.04.2018
comment
@TaW Измените DrawMode на OwnerDrawFixed и установите ItemHeight на 40, например, тогда вы увидите, что часть редактирования всегда будет отображаться вверху слева. - person Reza Aghaei; 13.04.2018
comment
Ах, да, при должном усердии действительно можно выстрелить себе в ногу. Itemheight на самом деле не должен быть установлен imo, так как он мешает размеру шрифта. После его установки мне пришлось использовать код дизайнера, чтобы снова избавиться от него. (Кстати: это было необходимо или есть какой-то трюк, чтобы сбросить его на автоматический?) - person TaW; 13.04.2018
comment
@TaW Представьте, что владелец рисует поле со списком вот так. В этом случае высота элемента должна быть больше высоты шрифта. - person Reza Aghaei; 13.04.2018
comment
Да, это отличный пример. Отох, когда пользователь может увеличить шрифт, сохраняя его автоматическим, это намного проще. И в любом случае весь рисунок владельца сводился к вертикальному выравниванию. Хе-хе, наверное, мы его все равно потеряли ;-) - person TaW; 13.04.2018
comment
@Reza, когда я устанавливаю размер Arial 18f, текст в поле со списком не будет отображаться должным образом. ibb.co/kCR4p7 - person Vignesh Nethaji; 14.04.2018
comment
Использование большего ItemHeight решило проблему для меня. - person Reza Aghaei; 14.04.2018
comment
@reza Но если увеличить itemheight, тогда элемент управления станет очень большим, тогда он выглядит не очень хорошо, поэтому мне нужен текст, который также должен занимать пространство на полях. - person Vignesh Nethaji; 14.04.2018
comment
Я не уверен, есть ли другое решение для этого, если я найду его, я дам вам знать :) - person Reza Aghaei; 14.04.2018

хорошо, приведенный ниже код не отвечает на фактический вопрос о текстовой части; Ганс, как обычно, все сделал правильно.

Я сохраняю ответ, потому что думаю, что он делает несколько вещей лучше, чем код 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..

Кроме того: My ComboBox не помещает текст в свою текстовую часть вверху слева, а в середине слева. Может быть, виноват старый элемент управления .Net1.1?

person TaW    schedule 13.04.2018
comment
Хотите верьте, хотите нет, но StringFormat на самом деле реализует IDisposable. Иди разберись. - person LarsTech; 13.04.2018
comment
Хорошо, я верю .. спасибо, что следите за моими вещами ;-) - person TaW; 13.04.2018
comment
Я не понимаю, почему вы говорите, что это не отвечает на вопрос ОП. Кроме того, если вы установите DropDownStyle на DropDownList, текст в части редактора будет центрирован. Таким образом, есть также два возможных способа представления контента. Я думаю, это именно то, что просили. - person Jimi; 13.04.2018
comment
Что ж, в то время как версия DropDownList является хорошим наблюдением (спасибо за это!) OP ясно показывает редактируемое текстовое поле. Otoh также показывает нецентрированное выпадающее меню, но код, по крайней мере, кажется, пытается центрировать. - person TaW; 13.04.2018