更改 ComboBox 下拉列表边框的颜色
Change color of the border of a ComboBox DropDown List
我的代码:
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
结果(注意蓝色边缘,我要更改的内容):
要改变ComboBox的DropDown List的Theme边框颜色,需要处理List Control的WM_NCPAINT消息,当Window非客户区需要绘制:通常在显示 DropDown 时。
要获取 ComboBox 的列表控件的句柄,可以使用 GetComboBoxInfo() function: the handle of its List Control and Edit Control are returned in a COMBOBOXINFO 结构。
然后您可以将列表控件句柄分配给 NativeWindow,这样您就可以覆盖它的 WndProc 并捕获 WM_NCPAINT
。
收到消息后,使用 GetWindowDc() function and pass it to the Graphics.FromHdc() 方法获取列表控件的设备上下文 (HDC
) 的句柄,以创建可用于在此表面上绘制的图形对象。
▶ 阅读有关 WM_NCPAINT
消息的文档,您可能会注意到 WPARAM
应该引用更新区域句柄:但通常是 IntPtr.Zero,这就是我们需要 [=15] 的原因=].
在(重要)之后释放设备上下文调用 ReleaseDC() 的句柄。
差不多就这些了。
自定义 ComboBox 控件公开了一个 public 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
我的代码:
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
结果(注意蓝色边缘,我要更改的内容):
要改变ComboBox的DropDown List的Theme边框颜色,需要处理List Control的WM_NCPAINT消息,当Window非客户区需要绘制:通常在显示 DropDown 时。
要获取 ComboBox 的列表控件的句柄,可以使用 GetComboBoxInfo() function: the handle of its List Control and Edit Control are returned in a COMBOBOXINFO 结构。
然后您可以将列表控件句柄分配给 NativeWindow,这样您就可以覆盖它的 WndProc 并捕获 WM_NCPAINT
。
收到消息后,使用 GetWindowDc() function and pass it to the Graphics.FromHdc() 方法获取列表控件的设备上下文 (HDC
) 的句柄,以创建可用于在此表面上绘制的图形对象。
▶ 阅读有关 WM_NCPAINT
消息的文档,您可能会注意到 WPARAM
应该引用更新区域句柄:但通常是 IntPtr.Zero,这就是我们需要 [=15] 的原因=].
在(重要)之后释放设备上下文调用 ReleaseDC() 的句柄。
差不多就这些了。
自定义 ComboBox 控件公开了一个 public 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