如何在.NET WinForms C#中移除ComboBox下拉列表蓝色焦点边框?
移除WinForms自定义ComboBox下拉列表的蓝色焦点边框
问题说明
自定义CxFlatComboBoxEnhanced控件时,下拉列表始终存在蓝色焦点边框。尝试通过处理0x0134(WM_CTLCOLORLISTBOX)消息移除边框,但仅在列表项获得焦点时生效;打开列表、鼠标移出窗体时边框重现,且代码会覆盖其他列表项。
问题根源
DrawItem事件中调用e.DrawBackground()会触发系统绘制默认的焦点边框,无法完全自定义控制。- 原
0x0134消息处理逻辑错误:直接填充整个下拉区域会覆盖其他列表项,且没有正确拦截系统绘制焦点边框的流程。 - GDI资源(如
SolidBrush)未正确释放,存在泄漏风险;且错误释放了系统提供的e.Graphics对象。
解决方案
1. 修改DrawItem事件处理
完全重绘列表项,不依赖系统默认绘制,同时跳过焦点边框的绘制:
private void CxFlatComboBoxEnhanced_DrawItem(object sender, DrawItemEventArgs e) { if (e.Index < 0) return; e.Graphics.SmoothingMode = SmoothingMode.HighQuality; e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; // 不调用e.DrawBackground(),避免系统绘制默认焦点边框 Color backColor = ((e.State & DrawItemState.Selected) == DrawItemState.Selected) ? DropHoverColor : DropColor; using (SolidBrush backBrush = new SolidBrush(backColor)) { e.Graphics.FillRectangle(backBrush, e.Bounds); } string itemText = base.GetItemText(base.Items[e.Index]); using (SolidBrush textBrush = new SolidBrush(ForeColor)) { e.Graphics.DrawString(itemText, Font, textBrush, new RectangleF(e.Bounds.X + 8, e.Bounds.Y, e.Bounds.Width - 8, e.Bounds.Height - 5), StringAlignment.BottomLeft); } // 不要释放e.Graphics,这是系统提供的对象 }
2. 修正WndProc中的0x0134消息处理
WM_CTLCOLORLISTBOX消息需要返回一个画刷句柄,让系统使用该画刷绘制下拉列表背景,从而避免蓝色边框:
// 新增成员变量存储背景画刷,避免重复创建 private SolidBrush _listBoxBackBrush; [DllImport("gdi32.dll")] static extern int SetBkMode(IntPtr hdc, int mode); public CxFlatComboBoxEnhanced() { DrawItem += CxFlatComboBoxEnhanced_DrawItem; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true); DoubleBuffered = true; FlatStyle = FlatStyle.Flat; DrawMode = DrawMode.OwnerDrawFixed; ItemHeight = 30; // 初始化自定义属性默认值 DropArrowColor = Color.Black; DropColor = Color.White; DropHoverColor = Color.Gray; _listBoxBackBrush = new SolidBrush(DropColor); } protected override void WndProc(ref Message m) { // 处理WM_CTLCOLORLISTBOX消息,返回自定义背景画刷 if (m.Msg == 0x0134) { // 设置背景模式为透明,避免系统绘制默认边框 SetBkMode(m.WParam, 1); // TRANSPARENT // 返回自定义背景画刷的句柄 m.Result = _listBoxBackBrush.Handle; return; // 跳过base.WndProc,避免系统默认处理 } base.WndProc(ref m); if (m.Msg == 0x000F || m.Msg == 0x133) { IntPtr hDC = GetWindowDC(m.HWnd); if (hDC.ToInt32() == 0) return; using (Graphics graphics = Graphics.FromHdc(hDC)) { graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.Clear(Parent.BackColor); GraphicsPath backPath = DrawHelper.CreateRoundRect(1, 1, Width - 2, Height - 2, 2); using (SolidBrush backBrush = new SolidBrush(BackColor)) { graphics.FillPath(backBrush, backPath); } using (Pen backPen = new Pen(BackColor, 2)) { graphics.DrawPath(backPen, backPath); } using (SolidBrush textBrush = new SolidBrush(ForeColor)) { graphics.DrawString(Text, Font, textBrush, new Point(8, 8)); } using (Pen arrowPen = new Pen(DropArrowColor, 1)) { if (DroppedDown) { graphics.DrawLine(arrowPen, Width - 22, Height / 2 + 2, Width - 17, 15); graphics.DrawLine(arrowPen, Width - 12, Height / 2 + 2, Width - 17, 15); } else { graphics.DrawLine(arrowPen, Width - 22, Height / 2 - 3, Width - 17, 20); graphics.DrawLine(arrowPen, Width - 12, Height / 2 - 3, Width - 17, 20); } } } ReleaseDC(m.HWnd, hDC); } } // 重写Dispose方法,释放自定义画刷 protected override void Dispose(bool disposing) { if (disposing) { _listBoxBackBrush?.Dispose(); } base.Dispose(disposing); }
关键说明
- 移除
e.DrawBackground()是避免系统绘制默认焦点边框的核心,所有绘制逻辑完全由自己控制。 WM_CTLCOLORLISTBOX消息返回自定义画刷,让系统使用该画刷绘制下拉背景,彻底替代系统默认样式。- 使用
using语句自动释放GDI资源,避免内存泄漏;不要释放系统提供的e.Graphics对象。 - 重写
Dispose方法释放自定义创建的_listBoxBackBrush,确保资源完全回收。
内容的提问来源于stack exchange,提问作者M Z
相关产品推荐
相关产品推荐

