You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为ComboBox添加按钮并调整内部TextBox编辑控件尺寸

自定义ComboBox添加按钮并避免编辑框覆盖问题

我正在制作一个自定义ComboBox,想在里面加个按钮让用户添加数据源项目。知道要通过WndProc获取内部TextBox,但不知道怎么修改它的长度,防止选中时覆盖按钮。

目前遇到的问题:

  • 鼠标悬停或进入编辑状态时,内部TextBox编辑控件会完全覆盖右侧添加按钮
  • 当前状态:编辑状态下,ComboBox的编辑框延伸至控件最右侧,按钮被完全遮挡
  • 期望效果:编辑框仅占据ComboBox左侧区域,右侧按钮始终可见,不会被覆盖

已尝试的代码

自定义ComboBox构造代码

public ComboBoxButton() : base()
{
    
    _button1.Size = buttonSize;
    this.DrawMode = DrawMode.OwnerDrawVariable;
    _button1.BackColor = SystemColors.Control;
    _button1.Text = "?";
    _button1.Location = buttonLocation;
    this.Controls.Add(_button1);
    SetStyle(ControlStyles.OptimizedDoubleBuffer, true);            
}

获取编辑控件的API声明

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

尝试修改TextBox尺寸的NativeWindow代码

private class TextWindow : NativeWindow
{
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    private struct COMBOBOXINFO
    {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public int buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }

    [DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);

    public TextWindow(ComboBox cb)
    {
        COMBOBOXINFO info = new COMBOBOXINFO();
        info.cbSize = Marshal.SizeOf(info);
        SendMessageCb(cb.Handle, 0x164, IntPtr.Zero, out info);
        this.AssignHandle(info.hwndEdit);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == (0x0302))
        {
            MessageBox.Show("No pasting allowed!");
            return;
        }
        base.WndProc(ref m);
    }
}

private TextWindow textWindow;

protected override void OnHandleCreated(EventArgs e)
{
    textWindow = new TextWindow(this);
    base.OnHandleCreated(e);
    listBoxHandle = GetComboBoxListInternal(this.Handle, out COMBOBOXINFO info);
}

protected override void OnHandleDestroyed(EventArgs e)
{
    textWindow.ReleaseHandle();            
    base.OnHandleDestroyed(e);
}

解决方案

核心思路是动态调整内部TextBox的边界,为右侧按钮留出固定空间,具体实现步骤如下:

1. 完善API声明与结构体

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
    public int cbSize;
    public RECT rcItem;
    public RECT rcButton;
    public int buttonState;
    public IntPtr hwndCombo;
    public IntPtr hwndEdit;
    public IntPtr hwndList;
}

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

[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);

private const uint SWP_NOZORDER = 0x0004;
private const uint SWP_NOACTIVATE = 0x0010;

2. 添加编辑框尺寸调整方法

private void AdjustEditBoxSize()
{
    COMBOBOXINFO cbi = new COMBOBOXINFO();
    cbi.cbSize = Marshal.SizeOf(cbi);
    if (GetComboBoxInfo(this.Handle, ref cbi) && cbi.hwndEdit != IntPtr.Zero)
    {
        // 获取ComboBox客户端区域
        RECT comboRect;
        GetClientRect(this.Handle, out comboRect);
        
        // 计算编辑框宽度:总宽度减去按钮宽度和边距
        int editWidth = comboRect.Right - comboRect.Left - _button1.Width - 4;
        int editHeight = comboRect.Bottom - comboRect.Top;
        
        // 固定编辑框位置与大小,避免覆盖按钮
        SetWindowPos(cbi.hwndEdit, IntPtr.Zero, 2, 2, editWidth, editHeight, SWP_NOZORDER | SWP_NOACTIVATE);
    }
}

3. 在关键事件中调用调整方法

// 控件大小改变时调整
protected override void OnResize(EventArgs e)
{
    base.OnResize(e);
    _button1.Location = new Point(this.ClientSize.Width - _button1.Width - 2, 2);
    AdjustEditBoxSize();
}

// 获取焦点时调整(防止编辑状态下尺寸异常)
protected override void OnGotFocus(EventArgs e)
{
    base.OnGotFocus(e);
    AdjustEditBoxSize();
}

// 控件创建完成后初始化
protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    _button1.Location = new Point(this.ClientSize.Width - _button1.Width - 2, 2);
    AdjustEditBoxSize();
}

4. 适配OwnerDraw模式的绘制逻辑

因为设置了DrawMode.OwnerDrawVariable,需要重写绘制方法避免文本覆盖按钮:

protected override void OnDrawItem(DrawItemEventArgs e)
{
    base.OnDrawItem(e);
    if (e.Index < 0) return;
    
    e.DrawBackground();
    // 绘制文本时留出按钮空间
    Rectangle textRect = e.Bounds;
    textRect.Width -= _button1.Width + 4;
    e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, new SolidBrush(e.ForeColor), textRect);
    e.DrawFocusRectangle();
}

protected override void OnMeasureItem(MeasureItemEventArgs e)
{
    base.OnMeasureItem(e);
    e.ItemHeight = this.Font.Height + 2;
}

内容的提问来源于stack exchange,提问作者Hack.Sign

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 09:13:19