首页 > 解决方案 > 更改组合框下拉列表的边框颜色

问题描述

我的代码:

Private Sub ComboBox2_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ComboBox2.DrawItem
    If e.Index < 0 Then
        Return
    End If
    e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
    Dim CB As ComboBox = TryCast(sender, ComboBox)
    If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
        e.Graphics.FillRectangle(New SolidBrush(Color.DarkRed), e.Bounds)
    Else
        e.Graphics.FillRectangle(New SolidBrush(CB.BackColor), e.Bounds)
    End If
    e.Graphics.DrawString(CB.Items(e.Index).ToString(), e.Font, New SolidBrush(CB.ForeColor), New Point(e.Bounds.X, e.Bounds.Y))
End Sub

结果(注意蓝色边缘,我要更改的内容):

印刷丝网

标签: vb.netwinformsgraphicscombobox

解决方案


要改变ComboBox的DropDown List的Theme边框颜色,需要处理List Control的WM_NCPAINT消息,该消息在需要绘制非客户区时发送到Window的句柄:通常,当显示下拉。

要获取 ComboBox 的 List Control 的句柄,可以使用GetComboBoxInfo()函数:其 List Control 和 Edit Control 的句柄在 COMBOBOXINFO 结构中返回

然后,您可以将 List Control 句柄分配给NativeWindow,这样您就可以覆盖它的 WndProc 和 trap WM_NCPAINT
收到消息后,使用GetWindowDc()HDC函数获取列表控件的设备上下文 ()的句柄并将其传递给Graphics.FromHdc()方法,以创建可用于绘制的 Graphics 对象这个表面。

▶ 阅读有关WM_NCPAINT消息的文档,您可能会注意到WPARAM应该引用更新区域句柄:但它通常是 IntPtr.Zero,这就是我们需要的原因GetWindowDc()

在(重要)之后调用ReleaseDC()释放设备上下文的句柄。

差不多就是这样。
自定义组合框控件公开了一个公共ListBorderColor属性,用于在设计时和运行时设置列表控件边框的颜色。

Imports System.ComponentModel
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

<DesignerCategory("code")>
Public Class ComboBoxExt
    Inherits ComboBox

    Private listControl As ListNativeWindow = Nothing
    Private m_ListBorderColor As Color = Color.Transparent

    Public Sub New()
    End Sub

    <DefaultValue(GetType(Color), "Transparent")>
    Public Property ListBorderColor As Color
        Get
            Return m_ListBorderColor
        End Get
        Set
            m_ListBorderColor = Value
            If listControl IsNot Nothing Then
                listControl.BorderColor = m_ListBorderColor
            End If
        End Set
    End Property

    Protected Overrides Sub OnHandleCreated(e As EventArgs)
        MyBase.OnHandleCreated(e)
        listControl = New ListNativeWindow(GetComboBoxListInternal(Me.Handle))
        listControl.BorderColor = ListBorderColor
    End Sub

    Protected Overrides Sub OnHandleDestroyed(e As EventArgs)
        listControl.ReleaseHandle()
        MyBase.OnHandleDestroyed(e)
    End Sub

    Public Class ListNativeWindow
        Inherits NativeWindow

        Public Sub New()
            Me.New(IntPtr.Zero)
        End Sub

        Public Sub New(hWnd As IntPtr)
            If hWnd <> IntPtr.Zero Then AssignHandle(hWnd)
        End Sub

        Public Property BorderColor As Color = Color.Transparent

        Protected Overrides Sub WndProc(ByRef m As Message)
            MyBase.WndProc(m)
            Select Case m.Msg
                Case WM_NCPAINT
                    Dim hDC As IntPtr = GetWindowDC(Me.Handle)
                    Try
                        Using g = Graphics.FromHdc(hDC),
                            pen = New Pen(BorderColor)
                            Dim rect = g.VisibleClipBounds
                            g.DrawRectangle(pen, 0, 0, rect.Width - 1, rect.Height - 1)
                        End Using
                    Finally
                        ReleaseDC(Me.Handle, hDC)
                    End Try
                m.Result = IntPtr.Zero
            End Select
        End Sub
    End Class


    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
    Friend Shared Function GetComboBoxInfo(hWnd As IntPtr, ByRef pcbi As COMBOBOXINFO) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Friend Shared Function GetWindowDC(hWnd As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Friend Shared Function ReleaseDC(hWnd As IntPtr, hDc As IntPtr) As Boolean
    End Function

    Friend Const WM_NCPAINT As Integer = &H85

    <StructLayout(LayoutKind.Sequential)>
    Friend Structure COMBOBOXINFO
        Public cbSize As Integer
        Public rcItem As Rectangle
        Public rcButton As Rectangle
        Public buttonState As Integer
        Public hwndCombo As IntPtr
        Public hwndEdit As IntPtr
        Public hwndList As IntPtr
        Public Sub Init()
            cbSize = Marshal.SizeOf(Of COMBOBOXINFO)()
        End Sub
    End Structure

    Friend Function GetComboBoxListInternal(cboHandle As IntPtr) As IntPtr
        Dim cbInfo = New COMBOBOXINFO()
        cbInfo.Init()
        GetComboBoxInfo(cboHandle, cbInfo)
        Return cbInfo.hwndList
    End Function
End Class

推荐阅读