ComboBox Text Align Vertically Center
Asked Answered
S

2

3

I created the custom combobox on .net framework 1.1, i can custom draw dropdown items, but i can't set or draw the combobox text on Middle Left , combobox text always render top left , but i need text should be render on middle left.

[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();
            }
        }
    }   
}

my Custom Combobox Appearance

Stockyard answered 13/4, 2018 at 13:19 Comment(2)
Remove try/catch and show what is the error? And note that catching all exceptions are senseless and is a strong sign you are doing wrongMauri
The textbox portion has no option to get the text aligned to the center, custom draw doesn't fix it either. Padding the text with spaces can be a somewhat silly workaround. Meh, users are entirely used to the look+feel of comboboxes.Edelsten
T
5

In an owner draw ComboBox the text of the Edit part of the control will always be shown at top left, regardless of the height of the ItemHeight.

To position the Edit part vertically in middle, you can find the Edit element using GetComboBoxInfo and then using SetWindowPos set a new position for it to stand vertically in middle of the ComboBox.

You need to reposition it when the control size changes. Also you need to fill the background of ComboBox with a Color.

enter image description here

Here is the code that I used:

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

enter image description here

Tilton answered 13/4, 2018 at 15:3 Comment(11)
But will that actually show centered and editable text?Pellerin
@Pellerin Yes, to make it less confusing, I changed the screenshot. In fact I change the position of the Edit control. I'm not painting it.Tilton
Hm, I'm confused. Especially as I wonder how OP could make the text appear top-left; it certainly doesn't do that here. Maybe a .Net .1.1 thing..?Pellerin
@Pellerin Change the DrawMode to OwnerDrawFixed and set ItemHeight to 40 for example, then you will see the edit part will always appear on top left.Tilton
Ah, yes, with sufficient care one can indeed shoot oneself in the foot. Itemheight shouldn't really be set imo since it interferes with the Font size. After setting it I had to go the desgner code to get rid of it again.. (Btw: Was that necessary or is there a trick to reset it to automatic?)Pellerin
@Pellerin Imagine an owner draw combobox like this. In this case, the Item height should be set to a value more than the font height.Tilton
Yes, that is an excellent example. Otoh, when the user can enlarge the font keeping it automatic is much simpler.. And all the owner-drawing was just about the vertical alignment anyway. Hehe, we probably lost him anyway ;-)Pellerin
@Reza , when i set Arial 18f size , then Combobox text won't appear properly. ibb.co/kCR4p7Stockyard
Using a larger ItemHeight solved the problem for me.Tilton
@reza But if increase itemheight then control is very big ,then its not looks good, so that i need text should be occupy the margin space also.Stockyard
I'm not sure if there is another fix for that, If I could find any, I'll let you know :)Tilton
P
1

ok, below code doesn't answer the actual question about the Text portion; Hans got it right, as usual.

I keep the answer because I think it does a few things better than OP code..

enter image description here

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

Instead of calculating a centered position I use the DrawString overload that takes a target rectangle and add a StringFormat to center in both directions. StringFormat was available since .Net 1.1 and indeed is IDisposable, so we should dipose of each we create, best in a using clause..

Note that for drawing controls the use of TextRenderer is encouraged but only came with .Net 2.0.

Also note that I substituted the Brushes for SystemBrushes..

Also: My ComboBox doesn't place the text in its Text portion top-left but middle-left. Maybe the old .Net1.1 control is the culprit?

Pellerin answered 13/4, 2018 at 13:47 Comment(4)
Believe it or not, StringFormat actually implements IDisposable. Go figure.Katakana
Ok, I believe.. thanks for keeping track of my stuff ;-)Pellerin
I don't understand why you say that this does not answer the OP question. Also, if you set the DropDownStyle to DropDownList, the text in the "editor" portion will be centered. So, there are also two possible ways of presenting the content. I think this is exactly what's been requested.Servile
Well, while the DropDownList version is a good observation, (thanks for that!) the OP clearly shows an editable text field. Otoh it also shows non-centered dropdown but the code at least seems to try to center..Pellerin

© 2022 - 2024 — McMap. All rights reserved.