How to add minimize button for a userform? - vba

I need to create a userform for my excel vba program., But how can I add "minimize" button for this userform? Thanks.

You can try this code :
Private Sub ToggleButton1_Click()
If ToggleButton1.Value = True Then
Me.Height = Me.Height * 0.25
Else
Me.Height = dHeight
End If
End Sub
Private Sub UserForm_Initialize()
dHeight = Me.Height
End Sub
[EDIT]
Found this link where they explain how to do it with an API to minimize.
Here is the given code :
Option Explicit
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
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 DrawMenuBar Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Const GWL_STYLE As Long = (-16) 'Sets a new window style
Private Const WS_SYSMENU As Long = &H80000 'Windows style
Private Const WS_MINIMIZEBOX As Long = &H20000
Private Const WS_MAXIMIZEBOX As Long = &H10000
Private Const SW_SHOWMAXIMIZED = 3
Private Sub UserForm_Activate()
Dim lFormHandle As Long, lStyle As Long
'===========================================
'= Originally from Dax =
'= Modified with comments by Ivan F Moala =
'= 22/07/01 =
'===========================================
'Lets find the UserForm Handle the function below retrieves the handle
'to the top-level window whose class name ("ThunderDFrame" for Excel)
'and window name (me.caption or UserformName caption) match the specified strings.
lFormHandle = FindWindow("ThunderDFrame", Me.Caption)
'The GetWindowLong function retrieves information about the specified window.
'The function also retrieves the 32-bit (long) value at the specified offset
'into the extra window memory of a window.
lStyle = GetWindowLong(lFormHandle, GWL_STYLE)
'lStyle is the New window style so lets set it up with the following
lStyle = lStyle Or WS_SYSMENU 'SystemMenu
lStyle = lStyle Or WS_MINIMIZEBOX 'With MinimizeBox
lStyle = lStyle Or WS_MAXIMIZEBOX 'and MaximizeBox
'Now lets set up our New window the SetWindowLong function changes
'the attributes of the specified window , given as lFormHandle,
'GWL_STYLE = New windows style, and our Newly defined style = lStyle
SetWindowLong lFormHandle, GWL_STYLE, (lStyle)
'Remove >'< if you want to show form Maximised
'ShowWindow lFormHandle, SW_SHOWMAXIMIZED 'Shows Form Maximized
'The DrawMenuBar function redraws the menu bar of the specified window.
'We need this as we have changed the menu bar after Windows has created it.
'All we need is the Handle.
DrawMenuBar lFormHandle
End Sub
Regards,
Max

'Place this code in a Module
Private Declare Function FindWindowA Lib "USER32" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLongA Lib "USER32" _
(ByVal hWnd As Long, _
ByVal nIndex As Long) As Long
Private Declare Function SetWindowLongA Lib "USER32" _
(ByVal hWnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Option Explicit
Sub FormatUserForm(UserFormCaption As String)
Dim hWnd As Long
Dim exLong As Long
hWnd = FindWindowA(vbNullString, UserFormCaption)
exLong = GetWindowLongA(hWnd, -16)
If (exLong And &H20000) = 0 Then
SetWindowLongA hWnd, -16, exLong Or &H20000
Else
End If
End Sub
Sub ShowForm()
UserForm1.Show
End Sub
'Place this code in a UserForm with one Command Button named CommandButton1.
Option Explicit
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub UserForm_Initialize()
Call FormatUserForm(Me.Caption)
End Sub

Related

How to adjust form object transparency

I'm looking to determine how I can adjust the transparency of a shape on a Microsoft Access form. So far I've figured out how to use hWnd to adjust the opacity of the entire form, but I'd like to adjust the opacity of individual form elements/shapes.
This is what I have thus far. Like I mentioned, it adjusts the entire forms transparency instead of an individual object.
Code on the Form associated with a button:
Public Sub FullOpacity_Click()
Dim Transp As Long
Transp = RGB(0, 0, 0) 'This is the color you want your background to be
Me.Detail.BackColor = Transp
Me.Painting = False
SetFormOpacity Me, 0.5, Transp
Me.Painting = True
End Sub
Code in a module
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function SetLayeredWindowAttributes Lib "user32" _
(ByVal hwnd As Long, _
ByVal crKey As Long, _
ByVal bAlpha As Byte, _
ByVal dwFlags As Long) As Long
Private Const LWA_ALPHA As Long = &H2
Private Const GWL_EXSTYLE As Long = -20
Private Const WS_EX_LAYERED As Long = &H80000
Public Sub SetFormOpacity(frm As Form, sngOpacity As Single, TColor As Long)
Dim lngStyle As Long
' get the current window style, then set transparency
lngStyle = GetWindowLong(frm.hwnd, GWL_EXSTYLE)
SetWindowLong frm.hwnd, GWL_EXSTYLE, lngStyle Or WS_EX_LAYERED
SetLayeredWindowAttributes frm.hwnd, TColor, (sngOpacity * 255), LWA_ALPHA
End Sub
The object I'm trying to target is called "rectangle" and it's on the form called "Main".
Thanks so much for any help

vba show userform upon opening, hide worksheet, but keep taskbar icon

I have a userform that opens upon the opening of the workbook. Excel is also hidden so that the userform is all that is shown to the user.
Private Sub Workbook_Open()
Application.Visible = False
UserForm1.Show vbModeless
End Sub
However, this also hides the icon for Excel on the taskbar, so when a user clicks away from the userform they cannot get back into it unless using alt+tab or closes/minimises other windows that are in front of the userform. I do not want the users to do this and some may even try to open the form again (Presuming It is closed), causing re-open prompts and errors that I do not want either.
Essentially, I need an icon on the taskbar for the userform.
Once the userform is closed I have it so that Excel closes
Unload UserForm1
Application.Quit
Examples I have found on the internet for this problem don't quite achieve what I am trying to do.
Changing the form to minimise and open as modal works to keep the icon in the taskbar and not let the user edit the worksheet
Application.WindowState = xlMinimized
UserForm1.Show (1)
But this has 2 problems..... 1st - the userform doesn't become the focus, 2nd - the user can click on the taskbar icon and the sheet is now visible behind the userform, which is not what I what them to be able to do.
I spent an appreciable amount of time on this task in the development of Excel-Visio application and faced with the same problem (Excel form above Visio/ Excel and VBA editor are hidden - but user can lost focus easily and only way back - Alt-Tabbing). Same problem as is!
My algorithm to solve this problem was something like this (All code in Userform class):
Private Sub UserForm_Initialize()
'some init's above
ToggleExcel 'Toggle excel, all windows are hidden now!
ActivateVisio 'Visio fired and on top
SetStandAloneForm 'Let's customize form
End Sub
So on start up we have our desired Visio and Form. On Terminate event I ToggleExcel again and minimize Visio.
ToggleExcel:
Private Function ToggleExcel()
Static IsVBEWasVisible As Boolean
With Application
If .Visible = True Then
IsVBEWasVisible = .VBE.MainWindow.Visible
If IsVBEWasVisible Then _
.VBE.MainWindow.Visible = False
.WindowState = xlMinimized
.Visible = False
Else
If IsVBEWasVisible Then _
.VBE.MainWindow.Visible = True
.WindowState = xlMaximized
.Visible = True
End If
End With
End Function
SetStandAloneForm:
To SetStandAloneForm I declared this block of API-function:
#If VBA7 Then
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare PtrSafe Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare PtrSafe Function SetFocus Lib "user32" (ByVal hWnd As Long) As Long
#Else
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
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 SetFocus Lib "user32" (ByVal hWnd As Long) As Long
#End If
Actual SetStandAloneForm:
Private Function SetStandAloneForm()
Const GWL_STYLE As Long = -16
Const GWL_EXSTYLE As Long = -20
Const WS_CAPTION As Long = &HC00000
Const WS_MINIMIZEBOX As Long = &H20000
Const WS_MAXIMIZEBOX As Long = &H10000
Const WS_POPUP As Long = &H80000000
Const WS_VISIBLE As Long = &H10000000
Const WS_EX_DLGMODALFRAME As Long = &H1
Const WS_EX_APPWINDOW As Long = &H40000
Const SW_SHOW As Long = 5
Dim Hwnd As Long
Dim CurrentStyle As Long
Dim NewStyle As Long
If Val(Application.Version) < 9 Then
Hwnd = FindWindow("ThunderXFrame", Me.Caption) 'XL97
Else
Hwnd = FindWindow("ThunderDFrame", Me.Caption) '>XL97
End If
'Let's give to userform minimise and maximise buttons
CurrentStyle = GetWindowLong(Hwnd, GWL_STYLE)
NewStyle = CurrentStyle Or WS_MINIMIZEBOX Or WS_MAXIMIZEBOX
NewStyle = NewStyle And Not WS_VISIBLE And Not WS_POPUP
Call SetWindowLong(Hwnd, GWL_STYLE, NewStyle)
'Let's give to userform a taskbar icon
CurrentStyle = GetWindowLong(Hwnd, GWL_EXSTYLE)
NewStyle = CurrentStyle Or WS_EX_APPWINDOW
Call SetWindowLong(Hwnd, GWL_EXSTYLE, NewStyle)
Call ShowWindow(Hwnd, SW_SHOW)
End Function
The answer posted by Gareth on this question:
Excel Useform: How to hide application but have icon in the taskbar
Worked to give me a taskbar icon and was a simple copy and paste.
Thanks all for the help.
Rather than hide the application minimise the workbook:
ThisWorkbook.Windows(1).WindowState = xlMinimized

How to make an outlook reminder popup and stay on top of other windows

How do you make an outlook reminder popup and stay on top of other windows?
After looking online for a long while; I wasn't able to find a satisfactory answer to this question.
Using Windows 7 and Microsoft Outlook 2007+; when a reminder flashes up, it no longer gives a modal box to grab your attention. At work where additional plugins can be problematic to install (admin rights) and when using a quiet system, meeting requests are often overlooked.
Is there an easier way to implement this without using third party plugins/apps?
Sep 2021: Updated question title to indicate modal popup
For the latest macro please see update 4 (Office 365 inclusion)
After searching for a while I found a partial answer on a website that seemed to give me the majority of the solution;
https://superuser.com/questions/251963/how-to-make-outlook-calendar-reminders-stay-on-top-in-windows-7
However as noted in the comments, the first reminder failed to popup; while further reminders then did. based on the code I assumed this was because the window wasn't detected until it had instantiated once
To get around this, I looked to employ a timer to periodically test if the window was present and if it was, then bring it to the front.
Taking the code from the following website; Outlook VBA - Run a code every half an hour
Then melding the two solutions together gave a working solution to this problem.
From the trust centre, I enabled the use of macros then opening the visual basic editor from Outlook (alt+F11) I added the following code to the 'ThisOutlookSession' module
CODE REMOVED
UPDATE 1 (Feb 12, 2015)
After using this for a while I found a real annoyance with the fact that triggering the timer removes the focus from the current window. It's a massive hassle as you're writing an e-mail.
As such I upgraded the code so that the timer only runs every 60 seconds then upon finding the first active reminder, the timer is stopped and the secondary event function is then used forthwith to activate the window focus change.
UPDATE 2 (Sep 4, 2015)
Having transitioned to Outlook 2013 - this code stopped working for me. I have now updated it with a further function (FindReminderWindow) that looks for a range of popup reminder captions. This now works for me in 2013 and should work for versions below 2013.
The FindReminderWindow function takes a value which is the number of iterations to step through to find the window. If you routinely have a larger number of reminders than 10 popup then you could increase this number in the EventMacro sub...
CODE REMOVED
UPDATE 3 (Aug 8, 2016)
Having rethought my approach and based on observation - I redesigned the code to try and have a minimal impact on working while Outlook was open; I would find the timer still took focus away from e-mails I was writing and possibly other issues with windows losing focus might have been related.
Instead - I assumed the reminders window once instantiated was merely hidden and not destroyed when reminders were shown; as such I now keep a global handle to the window so I should only need to look once at the window titles and subsequently check if the reminders window is visible before making it modal.
Also - the timer is now only employed when the reminders window is triggered, then turned off once the function has run; hopefully stopping any intrusive macro's running during the working day.
See which one works for you I guess...
Updated code below:
Add the following code to the 'ThisOutlookSession' module
Private WithEvents MyReminders As Outlook.Reminders
Private Sub Application_Startup()
On Error Resume Next
Set MyReminders = Outlook.Application.Reminders
End Sub
Private Sub MyReminders_ReminderFire(ByVal ReminderObject As Reminder)
On Error Resume Next
Call ActivateTimer(1)
End Sub
Then the updated module code...
Option Explicit
Private Declare Function SetTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerfunc As Long) As Long
Private Declare Function KillTimer Lib "User32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Long
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName _
As String, ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "User32" (ByVal hWnd As Long, ByVal nCmdSHow As Long) As Long
Private Declare Function SetWindowPos Lib "User32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Public TimerID As Long 'Need a timer ID to turn off the timer. If the timer ID <> 0 then the timer is running
Public hRemWnd As Long 'Store the handle of the reminder window
Public Sub ActivateTimer(ByVal Seconds As Long) 'The SetTimer call accepts milliseconds
On Error Resume Next
If TimerID <> 0 Then Call DeactivateTimer 'Check to see if timer is running before call to SetTimer
If TimerID = 0 Then TimerID = SetTimer(0, 0, Seconds * 1000, AddressOf TriggerEvent)
End Sub
Public Sub DeactivateTimer()
On Error Resume Next
Dim Success As Long: Success = KillTimer(0, TimerID)
If Success <> 0 Then TimerID = 0
End Sub
Public Sub TriggerEvent(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idevent As Long, ByVal Systime As Long)
Call EventFunction
End Sub
Public Function EventFunction()
On Error Resume Next
If TimerID <> 0 Then Call DeactivateTimer
If hRemWnd = 0 Then hRemWnd = FindReminderWindow(100)
If IsWindowVisible(hRemWnd) Then
ShowWindow hRemWnd, 1 ' Activate Window
SetWindowPos hRemWnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS ' Set Modal
End If
End Function
Public Function FindReminderWindow(iUB As Integer) As Long
On Error Resume Next
Dim i As Integer: i = 1
FindReminderWindow = FindWindow(vbNullString, "1 Reminder")
Do While i < iUB And FindReminderWindow = 0
FindReminderWindow = FindWindow(vbNullString, i & " Reminder(s)")
i = i + 1
Loop
If FindReminderWindow <> 0 Then ShowWindow FindReminderWindow, 1
End Function
UPDATE 4 (Sep 9, 2021)
Transition to Office 365: This comes with an option in the settings now to show reminders on top of windows (picture below), so why would you want to run a macro to place it on top now? The reason is that you can set it as a modal reminder box (using SWP_DRAWFRAME) so if you swap between programs, it will stay visible which doesn't happen with the vanilla option
Code should be compatible with all Outlook versions and allow transition between them easily (however I can no longer error check the non-VBA7 code)
In ThisOutlookSession
Private WithEvents MyReminders As Outlook.Reminders
Private Sub Application_Startup()
On Error Resume Next
With Outlook.Application
Set MyReminders = .Reminders
End With
End Sub
Private Sub MyReminders_ReminderFire(ByVal ReminderObject As Reminder)
On Error Resume Next
Call ReminderStartTimer
End Sub
In a module
Option Explicit
' https://jkp-ads.com/articles/apideclarations.asp; useful resource for Declare functions
Private Const SWP_NOSIZE = &H1, SWP_NOMOVE = &H2, SWP_NOACTIVATE = &H10, SWP_DRAWFRAME = &H20, HWND_TOPMOST = -1, GW_HWNDNEXT = 2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE Or SWP_DRAWFRAME
#If VBA7 Then
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As LongPtr) As Long
Private Declare PtrSafe Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr
Private Declare PtrSafe Function IsWindowVisible Lib "user32" (ByVal hWnd As LongPtr) As Boolean
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal nCmdShow As Long) As Boolean
Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#Else
Private Declare Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "User32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetWindow Lib "User32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Long
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "User32" (ByVal hWnd As Long, ByVal nCmdSHow As Long) As Long
Private Declare Function SetWindowPos Lib "User32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
#End If
#If VBA7 Then
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr, _
ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hWnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
#End If
#If VBA7 Then
'TimerIDs to turn off timers. If a TimerID <> 0 then the timer is running
Public ReminderTimerID As LongPtr
Public Function ReminderStartTimer()
On Error Resume Next
Call ActivateTimer(1, AddressOf ReminderEvent, ReminderTimerID)
End Function
Public Sub ReminderEvent(ByVal hWnd As LongPtr, ByVal uMsg As LongPtr, ByVal idevent As LongPtr, ByVal Systime As LongPtr)
On Error Resume Next
Call EventFunction
End Sub
Private Function EventFunction()
On Error Resume Next
If ReminderTimerID <> 0 Then Call DeactivateTimer(ReminderTimerID)
Dim hRemWnd As LongPtr: FindWindowFromPartialCaption hRemWnd, "Reminder"
If IsWindowVisible(hRemWnd) Then
'ShowWindow hRemWnd, 1 ' Activate Window
SetWindowPos hRemWnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS ' Set Modal
End If
Debug.Print TimeInMS() & "; " & hRemWnd
End Function
Private Function FindWindowFromPartialCaption(ByRef hWnd As LongPtr, ByVal PartialCaption As String)
Dim hWndP As LongPtr: hWndP = FindWindow(vbNullString, vbNullString) 'Parent Window
Do While hWndP <> 0
If InStr(GetNameFromHwnd(hWndP), PartialCaption) > 0 Then hWnd = hWndP
If hWnd = hWndP Then Exit Do
hWndP = GetWindow(hWndP, GW_HWNDNEXT)
Loop
End Function
Private Function GetNameFromHwnd(ByRef hWnd As LongPtr) As String
Dim Title As String * 255
GetWindowText hWnd, Title, 255
GetNameFromHwnd = Left(Title, GetWindowTextLength(hWnd))
End Function
Private Function ActivateTimer(ByVal Seconds As Long, FunctionAddress As LongLong, ByRef TimerID As LongPtr) 'The SetTimer call accepts milliseconds
On Error Resume Next
If TimerID = 0 Then TimerID = SetTimer(0, 0, Seconds * 1000, FunctionAddress) 'Check to see if timer is running before call to SetTimer
End Function
Private Function DeactivateTimer(ByRef TimerID As LongLong)
On Error Resume Next
If KillTimer(0&, TimerID) <> 0 Then TimerID = 0
End Function
#Else
'TimerIDs to turn off timers. If a TimerID <> 0 then the timer is running
Public ReminderTimerID As Long
Public Function ReminderStartTimer()
On Error Resume Next
Call ActivateTimer(1, AddressOf ReminderEvent, ReminderTimerID)
End Function
Public Sub ReminderEvent(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idevent As Long, ByVal Systime As Long)
Call EventFunction
End Sub
Private Function ActivateTimer(ByVal Seconds As Long, FunctionAddress As Long, ByRef TimerID As Long) 'The SetTimer call accepts milliseconds
On Error Resume Next
If TimerID = 0 Then TimerID = SetTimer(0, 0, Seconds * 1000, FunctionAddress) 'Check to see if timer is running before call to SetTimer
End Function
Private Function DeactivateTimer(ByRef TimerID As Long)
On Error Resume Next
If KillTimer(0, TimerID) <> 0 Then TimerID = 0
End Function
Private Function EventFunction()
On Error Resume Next
If ReminderTimerID <> 0 Then Call DeactivateTimer(ReminderTimerID)
Dim hRemWnd As Long: FindWindowFromPartialCaption hRemWnd, "Reminder"
If IsWindowVisible(hRemWnd) Then
'ShowWindow hRemWnd, 1 ' Activate Window
SetWindowPos hRemWnd, HWND_TOPMOST, 0, 0, 0, 0, FLAGS ' Set Modal
End If
Debug.Print TimeInMS() & "; " & hRemWnd
End Function
Private Function FindWindowFromPartialCaption(ByRef hWnd As Long, ByVal PartialCaption As String)
Dim hWndP As Long: hWndP = FindWindow(vbNullString, vbNullString) 'Parent Window
Do While hWndP <> 0
If InStr(GetNameFromHwnd(hWndP), PartialCaption) > 0 Then hWnd = hWndP
If hWnd = hWndP Then Exit Do
hWndP = GetWindow(hWndP, GW_HWNDNEXT)
Loop
End Function
Private Function GetNameFromHwnd(ByRef hWnd As Long) As String
Dim Title As String * 255
GetWindowText hWnd, Title, 255
GetNameFromHwnd = Left(Title, GetWindowTextLength(hWnd))
End Function
#End If
Private Function TimeInMS() As String
Dim TimeNow As Double: TimeNow = Timer
TimeInMS = Format(Date, "dd/mm/yyyy ") & Format(DateAdd("s", TimeNow, 0), "hh:mm:ss.") & Right(Format(TimeNow, "#0.00"), 2)
End Function
Using AutoHotKey you can set the window to be Always On Top without stealing focus of the current window. (Tested with WIn10 / Outlook 2013)
TrayTip Script, Looking for Reminder window to put on top, , 16
SetTitleMatchMode 2 ; windows contains
loop {
WinWait, Reminder(s),
WinSet, AlwaysOnTop, on, Reminder(s)
WinRestore, Reminder(s)
TrayTip Outlook Reminder, You have an outlook reminder open, , 16
WinWaitClose, Reminder(s), ,30
}
I've found a free program called PinMe! that will do exactly what I want. When your Outlook Reminder appears, right click on PinMe! in the system tray and select the Reminder window. This will place a lock icon next to the window. Go ahead Dismiss or Snooze your Reminder. The next time the reminder pops, it should appear in the front of every other window. This will work regardless of Outlook in the foreground or minimized.
After being inspired by Eric Labashosky's answer, I took his concept a step further and created the NotifyWhenMicrosoftOutlookReminderWindowIsOpen app, which you can download for free. It is a small executable that can ensure the Outlook Reminders window appears on top of other windows, as well as has some other optional ways of alerting the user that the window has opened.
I have Office 2013 and Windows 8.1 Pro. Many macros I found weren't handling the variable nature of the title Outlook places on the Reminder dialog. When you have 1 reminder, the title is "1 Reminder(s)" etc. I created a simple windows forms application in VB.NET, which I load on startup and keep minimized to the system tray. There is a 60 Timer added to the form which triggers the active code. When there is more than 0 reminders, the dialog box will be set to topmost and moved to 0,0.
Here is the code:
Imports System.Runtime.InteropServices
Imports System.Text
Module Module1
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function FindWindowEx(ByVal parentHandle As IntPtr, ByVal childAfter As IntPtr, ByVal lclassName As String, ByVal windowTitle As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True)> _
Public Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Public Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function
End Module
Public Class Form1
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Dim titleString As String = ""
Dim nullHandle As New IntPtr
Dim windowHandle As New IntPtr
Dim titleLength As Long
Try
Do
Dim sb As New StringBuilder
sb.Capacity = 512
Dim prevHandle As IntPtr = windowHandle
windowHandle = FindWindowEx(nullHandle, prevHandle, "#32770", vbNullString)
If windowHandle <> 0 And windowHandle <> nullHandle Then
titleLength = GetWindowText(windowHandle, sb, 256)
If titleLength > 0 Then
titleString = sb.ToString
Dim stringPos As Integer = InStr(titleString, "Reminde", CompareMethod.Text)
If stringPos Then
Dim reminderCount As Integer = Val(Mid(titleString, 1, 2))
If reminderCount > 0 Then
Dim baseWindow As IntPtr = -1 '-1 is the topmost position
SetWindowPos(windowHandle, baseWindow, 0, 0, 100, 100, &H41)
End If
Exit Sub
End If
End If
Else
Exit Sub
End If
Loop
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
End Sub
Private Sub ToolStripMenuItem1_Click(sender As Object, e As EventArgs) Handles ToolStripMenuItem1.Click
Me.Close()
End Sub
Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Me.Hide()
End Sub
End Class
Outlook 2016 now provides an option to "Show reminders on top of other windows". Use File > Options > Advanced, and then use the checkbox in the Reminders section. See this support.office.com page for screenshot. This option was added in Version 1804 of Outlook 2016, released to the "monthly channel" on April 25, 2018.
This Outlook 2016 option puts the reminder on top of all apps only initially. I like to keep the reminder on top until I explicitly dismiss, even if I click some other window. To keep the reminder on top I highly recommend using the app in #deadlydog's answer. #Tragamor's accepted answer on this question also works to keep on top, and I used it for years, but much more complicated compared to the app by #deadlydog.
This should work in different Outlook versions even if I tested it only on Outlook 2013.
Since I cannot test it in a localized English version, you may need to customize the code lines related to searching the reminders window even if, in my answer, I changed the related code lines in order to find the window in the English localized version.
Let me know if the macro works in your English Outlook version.
The user is free to minimize or close the reminders window in which cases, when a new or existing reminder fires, the reminders window will be topmost and not activated.
The reminders window title will be always updated reflecting the real number of visible reminders even without activating it.
In all cases the reminders window will never steal focus unless, obviously, the foreground window is the reminders window, that is unless the user has deliberately selected the reminders window.
This macro, other than making the reminders window topmost, will also select the most recent reminder in the reminder window itself, you can customize this behavior, please read the code in order to be able to do that.
The macro also flashes the reminders window when showing the window for the first time and whenever a new or existing reminder fires again.
You can customize how many times the window flashes or any other parameters related to it, it should be clear how to do that.
Paste the next code lines into the class module 'ThisOutlookSession':
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, _
ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Declare Function FlashWindowEx Lib "user32" (FWInfo As FLASHWINFO) As Boolean
Private Const FLASHW_STOP = 0
Private Const FLASHW_CAPTION = 1
Private Const FLASHW_TRAY = 2
Private Const FLASHW_ALL = FLASHW_CAPTION Or FLASHW_TRAY
Private Const FLASHW_TIMER = 4
Private Const FLASHW_TIMERNOFG = 12
Private Type FLASHWINFO
cbSize As Long
hwnd As Long
dwFlags As Long
uCount As Long
dwTimeout As Long
End Type
Private Const HWND_TOPMOST = -1
Private Const HWND_NOTOPMOST = -2
Private Const HWND_TOP = 0
Private Const HWND_BOTTOM = 1
Private Const SWP_NOSIZE = 1
Private Const SWP_NOMOVE = 2
Private Const SWP_NOACTIVATE = 16
Private Const SWP_DRAWFRAME = 32
Private Const SWP_NOOWNERZORDER = 512
Private Const SWP_NOZORDER = 4
Private Const SWP_SHOWWINDOW = 64
Private Existing_reminders_window As Boolean
Private WithEvents Rmds As Reminders
Public Reminders_window As Long
Private Sub Application_Reminder(ByVal Item As Object)
If Existing_reminders_window = False Then
Set Rmds = Application.Reminders
'In order to create the reminders window
ActiveExplorer.CommandBars.ExecuteMso ("ShowRemindersWindow")
Reminders_window = FindWindow("#32770", "0 Reminder(s)")
If Reminders_window = 0 Then
Reminders_window = FindWindow("#32770", "0 Reminder")
If Reminders_window = 0 Then
Reminders_window = FindWindow("#32770", "0 Reminder ")
End If
End If
'To prevent stealing focus in case Outlook was in the foreground
ShowWindow Reminders_window, 0
SetWindowPos Reminders_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOACTIVATE
Existing_reminders_window = True
End If
End Sub
Private Sub Rmds_BeforeReminderShow(Cancel As Boolean)
Dim FWInfo As FLASHWINFO
If Existing_reminders_window = True Then
Cancel = True
With FWInfo
.cbSize = 20
.hwnd = Reminders_window
.dwFlags = FLASHW_CAPTION
.uCount = 4
.dwTimeout = 0
End With
'In case the reminders window was not the highest topmost. This will not work on Windows 10 if the task manager window is topmost, the task manager and some other system windows have special z position
SetWindowPos Reminders_window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOACTIVATE
ShowWindow Reminders_window, 4
Select_specific_reminder
FlashWindowEx FWInfo
End If
End Sub
Paste the next code lines into a new or existing standard module:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Const WM_CHAR = &H102
Private Const VK_HOME = &H24
Private Const VK_END = &H23
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Public Sub Select_specific_reminder()
Dim Retval As Long
Retval = EnumChildWindows(ThisOutlookSession.Reminders_window, AddressOf EnumChildProc, 0)
End Sub
Private Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim Nome_classe As String
Nome_classe = Space$(256)
GetClassName hwnd, Nome_classe, 256
If InStr(Nome_classe, "SysListView32") Then
'You can customize the next code line in order to select a specific reminder
SendMessage hwnd, WM_KEYDOWN, VK_HOME, ByVal 0&
End If
EnumChildProc = 1
End Function
The latest Outlook has this feature inbuilt and the same is answered in https://superuser.com/a/1327856/913992
Just Alt F11 and copy paste this code..Works for me
Option Explicit
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "User32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Private Declare Function GetWindow Lib "User32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function IsWindowVisible Lib "User32" (ByVal hWnd As Long) As Boolean
Private Const GW_HWNDNEXT = 2
Private Declare PtrSafe Function FindWindowA Lib "User32" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare PtrSafe Function SetWindowPos Lib "User32" ( _
ByVal hWnd As Long, ByVal hWndInsertAfter As Long, _
ByVal X As Long, ByVal Y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const FLAGS As Long = SWP_NOMOVE Or SWP_NOSIZE
Private Const HWND_TOPMOST = -1
Private Sub Application_Reminder(ByVal Item As Object)
Dim ReminderWindowHWnd As Variant
On Error Resume Next
Dim lhWndP As Long
If GetHandleFromPartialCaption(lhWndP, "Reminder") = True Then
SetWindowPos lhWndP, HWND_TOPMOST, 0, 0, 0, 0, FLAGS
End If
End Sub
Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
Dim lhWndP As Long
Dim sStr As String
GetHandleFromPartialCaption = False
lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
Do While lhWndP <> 0
sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
GetWindowText lhWndP, sStr, Len(sStr)
sStr = Left$(sStr, Len(sStr) - 1)
If InStr(1, sStr, sCaption) > 0 Then
GetHandleFromPartialCaption = True
lWnd = lhWndP
Exit Do
End If
lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
Loop
End Function

Can you change the color of the titlebar of a userform in VBA using Windows API?

Is it possible to change the color of the title bar for a VBA userform using Windows API. Please note that I am only interested in changing the color of the title bar for a particular userform and not a system-wide them change. Thanks!
Just for fun;
UserForm:
Private gHWND As Long
Private Sub Label1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 1 Then HandleDragMove gHWND
End Sub
Private Sub UserForm_Initialize()
gHWND = Setup(Me)
End Sub
Private Sub UserForm_Click()
Unload Me
End Sub
*.BAS
Option Explicit
Private Const WM_NCLBUTTONDOWN = &HA1&
Private Const HTCAPTION = 2&
Private Const GWL_STYLE = (-16)
Private Const WS_BORDER = &H800000
Private Const WS_DLGFRAME = &H400000
Private Const WS_CAPTION = WS_BORDER Or WS_DLGFRAME
Private Declare Sub ReleaseCapture Lib "User32" ()
Private Declare Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal HWND As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowLong Lib "User32" Alias "GetWindowLongA" (ByVal HWND As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal HWND As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Function Setup(objForm As Object) As Long
Setup = FindWindow("ThunderDFrame", objForm.Caption)
SetWindowLong Setup, GWL_STYLE, GetWindowLong(Setup, GWL_STYLE) And Not WS_CAPTION
End Function
Public Sub HandleDragMove(HWND As Long)
Call ReleaseCapture
Call SendMessage(HWND, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
End Sub
(Would need mod for 64bit Office)

How to write a control to reduce the cell value in a VBA code module

I have a code module where I create a text box with pressing a specific key and reduce the current selected cell value by the amount inserted in the text box. I came to the point where I created the text box. Now I need to access the events of the text box outside the Worksheet modules. I found out that I could create a class module with the WihtEvents property. Unfortunate this does not seem to work. Here the code which is executed to make the control:
Dim objControl As BankingEventSink
Private Sub ReduceCell()
If IsNumeric(ActiveCell.Text) Then
Dim value As Double
value = CDbl(ActiveCell.Text)
ActiveSheet.Shapes.AddOLEObject(ClassType:="Forms.TextBox.1").Name = "ReduceCellTextBox"
With ActiveSheet.OLEObjects("ReduceCellTextBox")
.Top = ActiveCell.Top + ActiveCell.Height
.Left = ActiveCell.Left
End With
ActiveSheet.OLEObjects("ReduceCellTextBox").Activate
Set objControl = New BankingEventSink
objControl.Init (ActiveSheet.OLEObjects("ReduceCellTextBox").Object)
Else
RethrowKeys ("{BS}{-}")
End If
End Sub
The code of the class module:
Dim WithEvents objOLEControl As MSForms.TextBox
Public Sub Init(oleControl As MSForms.TextBox)
Set objOLEControl = oleControl
End Sub
Private Sub ReduceCellTextBox_Change()
MsgBox "Changed"
End Sub
Private Sub ReduceCellTextBox_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
MsgBox "Key down: " & KeyCode
End Sub
What ever I write in the text box no event is triggered. Where is the mistake?
To remove the title bar from a VBA userform, you need to use API's FindWindow, SetWindowLong, GetWindowLong and SetWindowPos. HERE is my one stop place for APIs
Create your userform and place a textbox in it. For example
Next paste this code in the userform.
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As Long
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 SetWindowPos Lib "user32" ( _
ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
ByVal x As Long, ByVal y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const GWL_STYLE = (-16)
Private Const WS_CAPTION = &HC00000
Private Const WS_BORDER = &H800000
Private Enum ESetWindowPosStyles
SWP_SHOWWINDOW = &H40
SWP_HIDEWINDOW = &H80
SWP_FRAMECHANGED = &H20
SWP_NOACTIVATE = &H10
SWP_NOCOPYBITS = &H100
SWP_NOMOVE = &H2
SWP_NOOWNERZORDER = &H200
SWP_NOREDRAW = &H8
SWP_NOREPOSITION = SWP_NOOWNERZORDER
SWP_NOSIZE = &H1
SWP_NOZORDER = &H4
SWP_DRAWFRAME = SWP_FRAMECHANGED
HWND_NOTOPMOST = -2
End Enum
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Dim FrmWndh As Long, lStyle As Long
Dim tR As RECT
Private Sub UserForm_Activate()
FrmWndh = FindWindow(vbNullString, Me.Caption)
lStyle = GetWindowLong(FrmWndh, GWL_STYLE)
lStyle = lStyle And Not WS_CAPTION
SetWindowLong FrmWndh, GWL_STYLE, lStyle
SetWindowPos FrmWndh, 0, tR.Left, tR.Top, _
tR.Right - tR.Left, tR.Bottom - tR.Top, _
SWP_NOREPOSITION Or SWP_NOZORDER Or SWP_FRAMECHANGED Or WS_BORDER
Me.Repaint
End Sub
Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
If KeyCode = 27 Then Unload Me
End Sub
When you now run the userform, it will look like this. Since we have removed the userform's title bar, I have added a code so that when you press ESC from the textbox, the userform will unload. You can change that to whatever (reasonable) you like.