为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
相关产品推荐
相关产品推荐

