ข้อความ 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();
            }
        }
    }   
}

ลักษณะที่ปรากฏของ Combobox แบบกำหนดเองของฉัน


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 ใช่ เพื่อให้สับสนน้อยลง ฉันจึงเปลี่ยนภาพหน้าจอ อันที่จริง ฉันเปลี่ยนตำแหน่งของตัวควบคุมการแก้ไข ฉันไม่ได้วาดภาพมัน - 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 จริงๆ เนื่องจากจะรบกวนขนาดตัวอักษร หลังจากตั้งค่าแล้ว ฉันต้องไปที่โค้ด desgner เพื่อกำจัดมันอีกครั้ง .. (แต่: จำเป็นไหมหรือมีเคล็ดลับในการรีเซ็ตเป็นอัตโนมัติหรือไม่) - person TaW; 13.04.2018
comment
@TaW ลองนึกภาพเจ้าของวาดคอมโบบ็อกซ์ เช่นนี้ ในกรณีนี้ ความสูงของรายการควรตั้งค่าให้มากกว่าความสูงของแบบอักษร - person Reza Aghaei; 13.04.2018
comment
ใช่แล้ว นั่นเป็นตัวอย่างที่ดีเยี่ยม โอ๊ะ เมื่อผู้ใช้สามารถขยายแบบอักษรเพื่อให้เป็นอัตโนมัติจะง่ายกว่ามาก.. และการวาดโดยเจ้าของทั้งหมดก็แค่การจัดแนวแนวตั้งเท่านั้นเอง อิอิ ยังไงซะเราก็คงเสียเขาไปแล้ว ;-) - person TaW; 13.04.2018
comment
@Reza เมื่อฉันตั้งค่า Arial 18f size ข้อความ Combobox จะไม่ปรากฏอย่างถูกต้อง 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 clause..

โปรดทราบว่าสำหรับการควบคุมการวาด แนะนำให้ใช้ TextRenderer แต่จะมาพร้อมกับ .Net 2.0 เท่านั้น

โปรดทราบว่าฉันแทนที่ Brushes ด้วย SystemBrushes..

นอกจากนี้: ComboBox ของฉันไม่ได้วางข้อความในส่วนข้อความด้านซ้ายบน แต่เป็นด้านซ้ายกลาง บางทีการควบคุม .Net1.1 แบบเก่าอาจเป็นตัวการใช่ไหม

person TaW    schedule 13.04.2018
comment
เชื่อหรือไม่ว่า StringFormat ใช้งาน IDisposable ได้จริง ไปคิดดู. - person LarsTech; 13.04.2018
comment
โอเค ฉันเชื่อว่า.. ขอบคุณที่คอยติดตามสิ่งของของฉัน ;-) - person TaW; 13.04.2018
comment
ฉันไม่เข้าใจว่าทำไมคุณถึงบอกว่าสิ่งนี้ไม่ตอบคำถาม OP นอกจากนี้ หากคุณตั้งค่า DropDownStyle เป็น DropDownList ข้อความในส่วนของตัวแก้ไขจะอยู่ตรงกลาง ดังนั้นจึงมีวิธีนำเสนอเนื้อหาได้สองวิธี ฉันคิดว่านี่คือสิ่งที่ได้รับการร้องขออย่างแน่นอน - person Jimi; 13.04.2018
comment
แม้ว่าเวอร์ชัน DropDownList จะเป็นข้อสังเกตที่ดี (ขอบคุณสำหรับสิ่งนั้น!) OP จะแสดงช่องข้อความที่แก้ไขได้อย่างชัดเจน Otoh มันยังแสดงดรอปดาวน์ที่ไม่อยู่กึ่งกลาง แต่อย่างน้อยโค้ดก็ดูเหมือนจะพยายามจัดกึ่งกลาง .. - person TaW; 13.04.2018