Option Explicit
'
' SAMPLE CODE
'
' Private CustomForm As New cCustomUserform
'
' Private Sub UserForm_Initialize()
'
' ' This useform sets both the TitleBarColor and HoverColor properties,
' ' and disables the Close Button. Press escape to close the userform.
'
' Me.Caption = "Press escape to quit"
' CustomForm.TitleBarColor = RGB(100, 180, 180)
' CustomForm.HoverColor = RGB(0, 120, 120)
' CustomForm.DisplayCloseButton = False
' Set CustomForm.Form = Me
'
' End Sub
'
' Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
'
' Set CustomForm = Nothing
'
' End Sub
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongPtrA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
#Else
Private Declare PtrSafe Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
#End If
Private Declare PtrSafe Function ColorAdjustLuma Lib "SHLWAPI.DLL" (ByVal clrRGB As Long, ByVal n As Long, ByVal fScale As Long) As Long
Private Declare PtrSafe Function DrawMenuBar Lib "user32.dll" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetClassLong Lib "user32.dll" Alias "GetClassLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function OleTranslateColor Lib "oleAut32.dll" (ByVal clr As OLE_COLOR, ByVal palet As LongPtr, Col As Long) As Long
Private Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As Long
Private Declare PtrSafe Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare PtrSafe Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As Long
Private Declare PtrSafe Function SetClassLong Lib "user32.dll" Alias "SetClassLongA" (ByVal hwnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private hwnd As LongPtr
Private WindowStyle As LongPtr
Private Declare PtrSafe Function GetClientRect Lib "user32" (ByVal hwnd As LongPtr, lpRect As RECT) As Long
Private Declare PtrSafe Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As LongPtr
Private Declare PtrSafe Function CombineRgn Lib "gdi32" (ByVal hDestRgn As LongPtr, ByVal hSrcRgn1 As LongPtr, ByVal hSrcRgn2 As LongPtr, ByVal nCombineMode As Long) As Long
Private Declare PtrSafe Function SetWindowRgn Lib "user32" (ByVal hwnd As LongPtr, ByVal hRgn As LongPtr, ByVal bRedraw As Long) As Long
Private Declare PtrSafe Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32" (ByVal hDC As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As LongPtr) As Long
Private Declare PtrSafe Function GetDC Lib "user32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function ReleaseDC Lib "user32" (ByVal hwnd As LongPtr, ByVal hDC As LongPtr) As Long
Private Declare PtrSafe Function LineTo Lib "gdi32" (ByVal hDC As LongPtr, ByVal x As Long, ByVal y As Long) As Long
Private Declare PtrSafe Function MoveToEx Lib "gdi32" (ByVal hDC As LongPtr, ByVal x As Long, ByVal y As Long, ByVal lpPoint As Long) As Long
Private Declare PtrSafe Function TranslateColor Lib "oleAut32.dll" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As LongPtr, Col As Long) As Long
Private Declare PtrSafe Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As LongPtr
Private Declare PtrSafe Function SelectObject Lib "gdi32" (ByVal hDC As LongPtr, ByVal hObject As LongPtr) As LongPtr
Private Declare PtrSafe Function DeleteDC Lib "gdi32" (ByVal hDC As LongPtr) As Long
Private Declare PtrSafe Function DeleteObject Lib "gdi32" (ByVal hObject As LongPtr) As Long
#Else
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA " (ByVal hwnd As Long , ByVal nIndex As Long, ByVal dwNewLong As Long ) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function ColorAdjustLuma Lib "SHLWAPI.DLL" (ByVal clrRGB As Long, ByVal n As Long, ByVal fScale As Long) As Long
Private Declare Function DrawMenuBar Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function GetClassLong Lib "user32.dll" Alias "GetClassLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
Private Declare Function OleTranslateColor Lib "oleAut32.dll" (ByVal clr As OLE_COLOR, ByVal palet As Long, col As Long) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function ReleaseCapture Lib "user32.dll" () As Long
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function SetClassLong Lib "user32.dll" Alias "SetClassLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private hwnd As Long
Private WindowStyle As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function CreateRectRgn Lib "gdi32" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function CombineRgn Lib "gdi32" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
Private Declare Function SetWindowRgn Lib "user32" (ByVal hwnd As Long, ByVal hRgn As Long, ByVal bRedraw As Long) As Long
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) As Long
Private Declare Function GetSysColor Lib "user32" (ByVal nIndex As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hDC As Long, ByVal nIndex As Long) As Long
Private Declare Function IUnknown_GetWindow Lib "shlwapi" Alias "#172" (ByVal pIUnk As IUnknown, ByVal hUf As Long) As Long
Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Private Declare Function LineTo Lib "gdi32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function MoveToEx Lib "gdi32" (ByVal hDC As Long, ByVal x As Long, ByVal y As Long, ByVal lpPoint As Long) As Long
Private Declare Function TranslateColor Lib "oleAut32.dll" Alias "OleTranslateColor" (ByVal clr As OLE_COLOR, ByVal palet As Long, Col As Long) As Long
Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
#End If
Private Const CS_DROPSHADOW As Long = &H20000
Private Const GCL_STYLE As Long = (-26)
Private Const GWL_STYLE As Long = (-16)
Private Const GWL_EXSTYLE As Long = (-20)
Private Const HTCAPTION As Long = 2
Private Const WM_NCLBUTTONDOWN As Long = &HA1
Private Const WS_CAPTION As Long = &HC00000
Private Const WS_EX_DLGMODALFRAME As Long = &H1
Private WithEvents CancelBUtton As MSForms.CommandButton
Private WithEvents cForm As MSForms.UserForm
Private WithEvents NewTitleBar As MSForms.Label
Private WithEvents NewTitleBarCaption As MSForms.Label
Private WithEvents NewCloseButton As MSForms.Label
Event CancelBUtton(ByRef Handled As Boolean)
Private cDisplayCloseButton As Boolean
Private cCaption As String
Private cCaptionColor As Long
Private cCaptionHoverColor As Long
Private cDropShadow As Boolean
Private cFocusOnForm As Boolean
Private cFormSet As Boolean
Private cFocusOnCloseButton As Boolean
Private cHoverColor As Long
Private cHoverColorSetByUser As Long
Private cTitleBarColor As Long
Private cTitleBarColorSetByUser As Long
Private Sub cForm_Layout()
Call CreateShadow
End Sub
' Class Events
Private Sub Class_Initialize()
cTitleBarColorSetByUser = -1
cHoverColorSetByUser = -1
cDisplayCloseButton = True
cCaption = "Default"
End Sub
Private Sub Class_Terminate()
Set cForm = Nothing
End Sub
' Properties
Public Property Get TitleBarColor() As Long
TitleBarColor = cTitleBarColor
End Property
Public Property Let TitleBarColor(uColor As Long)
cTitleBarColorSetByUser = uColor
cCaptionColor = BlackOrWhite(cTitleBarColorSetByUser)
If cFormSet = True Then Apply
End Property
Public Property Get HoverColor() As Long
HoverColor = cHoverColor
End Property
Public Property Let HoverColor(uColor As Long)
cHoverColorSetByUser = uColor
cCaptionHoverColor = BlackOrWhite(cHoverColorSetByUser)
If cFormSet = True Then Apply
End Property
Public Property Get DisplayCloseButton() As Boolean
DisplayCloseButton = cDisplayCloseButton
End Property
Public Property Let DisplayCloseButton(uDisplayCloseButton As Boolean)
cDisplayCloseButton = uDisplayCloseButton
If cFormSet = True Then Apply
End Property
Public Property Set Form(uForm As Object)
Set cForm = uForm
Call AdjustControlsPos
cCaption = IIf(cCaption = "Default", uForm.Caption, cCaption)
Call IUnknown_GetWindow(cForm, VarPtr(hwnd))
Apply
cFormSet = True
End Property
' Prepares userform / applies colors to controls
Public Sub Apply()
HideTitleBorder
Dim cFormBackColor
Call CreateShadow
cFormBackColor = cForm.BackColor
cTitleBarColor = IIf(cTitleBarColorSetByUser >= 0, cTitleBarColorSetByUser, cFormBackColor)
cHoverColor = IIf(cHoverColorSetByUser >= 0, cHoverColorSetByUser, IIf(cTitleBarColorSetByUser >= 0, cTitleBarColorSetByUser, TintAndShade(cFormBackColor, -40)))
cCaptionColor = BlackOrWhite(cTitleBarColor)
cCaptionHoverColor = BlackOrWhite(cHoverColor)
If NewTitleBar Is Nothing Then
CreatePseudoTitleBar
Else
NewTitleBar.BackColor = cTitleBarColor
NewTitleBarCaption.ForeColor = cCaptionColor
NewCloseButton.BackColor = cTitleBarColor
End If
End Sub
' Creates the pseudo titlebar
Private Sub CreatePseudoTitleBar()
Set NewTitleBar = cForm.Controls.Add("Forms.Label.1", "lbTitleBar")
With NewTitleBar
.BackColor = cTitleBarColor
.BackStyle = fmBackStyleOpaque
.Left = 0
.Top = 0
.Height = 20
.Width = cForm.InsideWidth - 26
End With
Set NewTitleBarCaption = cForm.Controls.Add("Forms.Label.1", "lbTitleBarCaption")
With NewTitleBarCaption
.BackStyle = fmBackStyleTransparent
.Caption = cCaption
.Font.Name = "Segoe UI"
.Font.Size = 9
.ForeColor = cCaptionColor
.Left = 6
.Top = 4
.Height = 20
.Width = cForm.InsideWidth - 26
.AutoSize = True
End With
Set NewCloseButton = cForm.Controls.Add("Forms.Label.1", "lbCloseButton")
With NewCloseButton
.BackColor = cTitleBarColor
.BackStyle = fmBackStyleOpaque
.Height = 20
.Width = 24
.Left = cForm.InsideWidth - 26
.Top = 0
.PicturePosition = fmPicturePositionCenter
.Picture = Application.CommandBars.GetImageMso("WindowClose", 16, 16)
End With
Set CancelBUtton = cForm.Controls.Add("Forms.CommandButton.1", "btnDefaultCancel")
With CancelBUtton
.TabStop = False
.Cancel = True
.Top = -40
End With
If cDisplayCloseButton = False Then NewCloseButton.Visible = False
End Sub
' Events for dynamically created controls / pseudo titlebar
Private Sub cForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
If cFocusOnForm = False Then
NewCloseButton.BackColor = cTitleBarColor
NewTitleBar.BackColor = cTitleBarColor
NewTitleBarCaption.ForeColor = cCaptionColor
cFocusOnForm = True
End If
End Sub
' Close routine for the pseudo closebutton
Private Sub NewCloseButton_Click()
Unload cForm
End Sub
Private Sub NewCloseButton_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
NewCloseButton.BackColor = vbRed
cFocusOnCloseButton = True
End Sub
Private Sub NewTitleBarCaption_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
' Debug.Print "Caption"
If cFocusOnForm = True Then
NewTitleBar.BackColor = cHoverColor
NewCloseButton.BackColor = cHoverColor
NewTitleBarCaption.ForeColor = cCaptionHoverColor
cFocusOnForm = False
cFocusOnCloseButton = False
End If
If Button Then
MoveUserForm
End If
End Sub
Private Sub NewTitleBar_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
' Debug.Print "Titlebar"
If cFocusOnForm = True Then
NewTitleBar.BackColor = cHoverColor
NewCloseButton.BackColor = cHoverColor
NewTitleBarCaption.ForeColor = cCaptionHoverColor
cFocusOnForm = False
cFocusOnCloseButton = False
End If
If Button Then
MoveUserForm
End If
End Sub
' Click event for the hidden command button that is dynamically-created
' with cancel property set to allow for easy use of escape key.
Private Sub CancelButton_Click()
Dim Handled As Boolean
RaiseEvent CancelBUtton(Handled)
If Handled = False Then
Unload cForm
End If
End Sub
' Misc / Helper routines
Private Sub HideTitleBorder()
WindowStyle = GetWindowLong(hwnd, GWL_STYLE)
WindowStyle = WindowStyle And (Not WS_CAPTION)
SetWindowLong hwnd, GWL_STYLE, WindowStyle
WindowStyle = GetWindowLong(hwnd, GWL_EXSTYLE)
WindowStyle = WindowStyle And Not WS_EX_DLGMODALFRAME
SetWindowLong hwnd, GWL_EXSTYLE, WindowStyle
DrawMenuBar hwnd
End Sub
Private Sub MoveUserForm()
ReleaseCapture
SendMessage hwnd, WM_NCLBUTTONDOWN, HTCAPTION, ByVal 0&
End Sub
Private Function TintAndShade(ByVal ColRef As Long, Optional ByVal Luminance As Long = 0) As Long
' Orignally sourced code written by Jaafar Tribak from https://www.mrexcel.com/board/threads/applying-tintandshade-to-a-userform-label.1189219/
'(Luminance must be between -100 and +100)
OleTranslateColor ColRef, 0, ColRef
TintAndShade = ColorAdjustLuma(ColRef, Luminance * 10, True)
End Function
Private Function BlackOrWhite(Color As Long) As Long
Dim cRed As Long, cGreen As Long, cBlue As Long
cRed = Color And 255
cGreen = (Color \ 256) And 255
cBlue = (Color \ 65536) And 255
If cRed * 0.3 + cGreen * 0.59 + cBlue * 0.11 < 128 Then BlackOrWhite = vbWhite
End Function
Private Sub CreateShadow()
Const SM_CYCAPTION = 4
Const SM_CYDLGFRAME = 8
Const RGN_OR = 2
Const RGN_DIFF = 4
Const COLOR_3DDKSHADOW = 21
Const PS_SOLID = 1
#If Win64 Then
Dim hwnd As LongLong, hDC As LongLong, hPen As LongLong, hPrevPen As LongLong
Dim hRgn1 As LongLong, hRgn2 As LongLong, hRgn3 As LongLong, hRgn4 As LongLong, hRgn5 As LongLong
#Else
Dim hwnd As Long, hDC As Long, hPen As Long, hPrevPen As Long
Dim hRgn1 As Long, hRgn2 As Long, hRgn3 As Long, hRgn4 As Long, hRgn5 As Long
#End If
Dim tFormClientRect As RECT
Dim lPenColor As Long, lPenWidth As Long
Call IUnknown_GetWindow(cForm, VarPtr(hwnd))
Call GetClientRect(hwnd, tFormClientRect)
hDC = GetDC(hwnd)
lPenWidth = 3
With tFormClientRect
hRgn1 = CreateRectRgn(.Left, .Top, .Right, .Bottom)
hRgn2 = CreateRectRgn(.Left, .Top, .Right, GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME))
Call CombineRgn(hRgn1, hRgn2, hRgn1, RGN_DIFF)
Call DeleteObject(hRgn1)
hRgn3 = CreateRectRgn(.Right - .Left - lPenWidth, .Top, .Right, 8)
Call CombineRgn(hRgn2, hRgn2, hRgn3, RGN_DIFF)
Call DeleteObject(hRgn3)
hRgn4 = CreateRectRgn(.Left, GetSystemMetrics(SM_CYCAPTION), .Right, .Bottom)
Call CombineRgn(hRgn2, hRgn2, hRgn4, RGN_OR)
Call DeleteObject(hRgn4)
hRgn5 = CreateRectRgn(.Left, .Bottom - lPenWidth, 8, .Bottom)
Call CombineRgn(hRgn2, hRgn2, hRgn5, RGN_DIFF)
Call DeleteObject(hRgn5)
Call SetWindowRgn(hwnd, hRgn2, True)
Call DeleteObject(hRgn2)
End With
DoEvents
Call TranslateColor(GetSysColor(COLOR_3DDKSHADOW), 0, lPenColor)
hPen = CreatePen(PS_SOLID, lPenWidth, lPenColor)
hPrevPen = SelectObject(hDC, hPen)
With tFormClientRect
Call MoveToEx(hDC, .Right - 2, .Top + 8, ByVal 0)
Call LineTo(hDC, .Right - 2, .Bottom - 2)
Call LineTo(hDC, .Left + 8, .Bottom - 2)
End With
Call SelectObject(hDC, hPrevPen)
Call DeleteObject(hPen)
Call ReleaseDC(hwnd, hDC)
End Sub
Private Sub AdjustControlsPos()
Const SM_CYCAPTION = 4
Const SM_CYDLGFRAME = 8
Dim oCtrl As Control
For Each oCtrl In cForm.Controls
If oCtrl.Parent Is cForm Then
oCtrl.Top = oCtrl.Top + PXtoPT(GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYDLGFRAME), True)
'oCtrl.Left = oCtrl.Left + PXtoPT(GetSystemMetrics(5), False) + PXtoPT(GetSystemMetrics(32), False)
End If
Next oCtrl
End Sub
Private Function ScreenDPI(ByVal bVert As Boolean) As Long
Const LOGPIXELSX As Long = 88
Const LOGPIXELSY As Long = 90
Static lDPI(1), hDC
If lDPI(0) = 0 Then
hDC = GetDC(0)
lDPI(0) = GetDeviceCaps(hDC, LOGPIXELSX)
lDPI(1) = GetDeviceCaps(hDC, LOGPIXELSY)
hDC = ReleaseDC(0, hDC)
End If
ScreenDPI = lDPI(Abs(bVert))
End Function
Private Function PXtoPT(ByVal Pixels As Long, ByVal bVert As Boolean) As Single
Const POINTSPERINCH As Long = 72
PXtoPT = Pixels / (ScreenDPI(bVert) / POINTSPERINCH)
End Function