I am building an Excel VBA project which makes use of a ListBox to navigate through a tree structure. By double clicking an item it will expand below with additional items. My goal is that by making this selection the change will be made and the ListBox will update, whilst retaining the selection just clicked by the user and keeping it in view.
I have created a separate workbook to isolate the problem I have to make it simpler, and I will be able to replicate any solutions into my original project.
My ListBox is populated using RowSource. Values are stored on a sheet (for genuine reasons I'll omit from this post to keep it to the point), changes are made to the sheet and then RowSource is called again to update the ListBox. By doing this the ListBox will update and then jump down to where the selection made is the last item in the view, but the list item now selected is the one in the position of the previous selection which is incorrect.
Example:
User scrolls down the ListBox using the scrollbar and double clicks item 'Test 100'
ListBox is updated, however the selection is incorrect. 'Test 86' is selected which is in the position of the previous selection 'Test 100', which is placed at the bottom of the view.
Here's a download link for the example workbook
I'm hoping someone will be able to shine some light on an elegant solution to correct this behaviour!
I have tried programmatically making the selection after the RowSource update, however this has no effect. By adding a brief pause and calling DoEvents (commented in the example) I've been able to make this work to some extent, however I have found that it doesn't work all the time and I would prefer not to have to force a pause as this as it makes the ListBox feel less responsive in my original project.
Private selection As Integer
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
selection = ListBox1.ListIndex
Call update
End Sub
Private Sub UserForm_Initialize()
Call update
End Sub
Sub update()
With Sheets("Test")
ListBox1.RowSource = .Range("A2:A" & .Range("A99999").End(xlUp).Row - 1).Address(, , , True)
End With
'Sleep 300
'DoEvents
ListBox1.ListIndex = selection
End Sub
Because it is a timing issue, I think either delays or timers will be required for a solution. This isn't a terribly elegant workaround, but seems to work in my limited tests:
UF module:
Option Explicit
Private selection As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As Long
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
selection = ListBox1.ListIndex
Call update
End Sub
Private Sub UserForm_Initialize()
Call update
End Sub
Sub update()
Dim hwndUF As Long
With Sheets("Test")
ListBox1.RowSource = .Range("A2:A" & .Range("A99999").End(xlUp).Row - 1).Address(, , , True)
End With
If selection <> 0 Then
hwndUF = FindWindow("ThunderDFrame", Me.Caption)
UpdateListIndex hwndUF
End If
End Sub
Public Sub UpdateLBSelection()
ListBox1.ListIndex = selection
End Sub
and then in a normal module:
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 uIDEvent As Long) As Long
Declare Function LockWindowUpdate Lib "user32" (ByVal hwndLock As Long) As Long
Private hWndTimer As Long
Sub UpdateListIndex(hWnd As Long)
Dim lRet As Long
hWndTimer = hWnd
LockWindowUpdate hWndTimer
lRet = SetTimer(hWndTimer, 0, 100, AddressOf TimerProc)
End Sub
Public Function TimerProc(ByVal hWnd As Long, ByVal uMsg As Long, _
ByVal idEvent As Long, ByVal dwTime As Long) As Long
On Error Resume Next
KillTimer hWndTimer, idEvent
UserForm1.UpdateLBSelection
LockWindowUpdate 0&
Userform1.Repaint
End Function
use
Private selection As Variant '<~~ use a Variant to store the ListBox current Value
'...
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
selection = ListBox1.Value '<~~ store the ListBox current Value
Call update '<~~ this will change the ListBox "RowSource"
ListBox1.Value = selection '<~~ get back the stored ListBox value selected before 'update' call
End Sub
I know that this is ancient now, but I had the same problem a couple of months ago and just stumbled on the solution (to my problem) of not selecting the right item in a listbox.
It turned out to be that the zoom level of the sheet was causing an accuracy issue. Listboxes sometimes look slightly fuzzy when at certain zoom levels - maybe thats just me - anyway, the solution was just to zoom in/out a point that didnt cause the problem.
Thanks
R
I also ran into this problem and a simple adding of Userform.Repaint before the setting the ListBox selection did the trick ......
Related
I'm running a PowerPoint Macro-Enabled Slide Show. When a user opens this file the presentation starts immediately. The presentation contains various shapes that, when pressed (use of links), will open a new Powerpoint Slide Show in front of the main Slide Show.
In the background I'm using VBA (code is located in the main Macro enabled slide show) to measure the time a user spends on all of the slides. I want the user to be able to stop this timer with a userform and a button. However, when a new Powerpoint Slide Show is opened, it appears in front of the main slide show. The userform will then disappears behind the new slide show. Using a second screen i have been able to view the userform. But when clicked on the userform it brings the main slide show in front of the other slide show.
So in short: I would like a userform that is in front of all slide show windows.
I tried using vbmodeless but this does not help. I've also tried out various bits of code:
http://www.vbaexpress.com/forum/showthread.php?58189-Make-userform-stay-on-top-of-all-windows-when-macro-is-fired
https://www.mrexcel.com/board/threads/userform-always-on-top.386643/
https://www.mrexcel.com/board/threads/keeping-userform-on-top-when-changing-active-workbook.1165439/
Unfortunately, none of these seem to be working. Some of these are for excel and I've not been able to rewrite these bits of code.
P.S. If this isn't possible, maybe I could hide the main slide show?
Simple version
Create a class module, MyClass, and put this code there:
Public WithEvents App As Application
Private Sub App_SlideShowBegin(ByVal Wn As SlideShowWindow)
UserForm1.Show False
End Sub
Create a module and put this code in it:
Dim MyThing As New MyClass
Sub InitializeApp()
Set MyThing.App = Application
End Sub
Run the InitializeApp method first. Now, when you start your presentation, your UserForm1 will show up. The False flag makes it non-modal, which is what I think you are looking for.
Slightly more advanced version
As above, but change the module to this:
Option Explicit
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOSIZE = &H1
Public Const HWND_TOP = 0
Public Const HWND_BOTTOM = 1
Public Const HWND_TOPMOST = -1
Public Const HWND_NOTOPMOST = -2
Public 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 uFlags As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Dim MyThing As New MyClass
Sub InitializeApp()
Set MyThing.App = Application
End Sub
And add this to your form code:
Option Explicit
Option Explicit
Private Sub UserForm_Initialize()
Dim formHWnd As Long
formHWnd = FindWindow("ThunderDFrame", Me.Caption)
SetWindowPos formHWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
End Sub
What code does: I have a code that moves the mouse around the screen, takes printscreens and pastes it to excel.
Problem: For some reason, my code always (with absolutely no exceptions) turns the NUMLOCK key off after every run.
What I tried so far: I searched around and found the SendKeys (NUMLOCK), which in theory works (although it seems to be very problematic for users).
What I want to do: I want to turn the NUMLOCK on after each macro run,
Obs1: I have no idea what is causing the macro to turn it off in the first place. Fixing whatever is causing this would be ideal, but since I have no idea what the problem is, I first want to get my code functional. I am going to work on that as soon as find a way to turn the NUMLOCK key on.
Question: Can I do this using the SendKeys? Am I using it properly? Is there a better way?
Obs2: Since it is a much bigger code, as soon as this is solved, I am going to post another question with the entire code, and go over on what is causing the problem.
Code I am trying to sue to turn numlock on:
Application.Sendkeys (NUMLOCK)
Also tried:
Application.Sendkeys ("NUMLOCK")
and
Application.Sendkeys {NUMLOCK}
You can set the keystate directly with a couple of Windows API calls. Ported from the MSDN page for keybd_event function:
#If VBA7 Then
Private Declare PtrSafe Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As LongPtr, ByVal dwExtraInfo As LongPtr)
Private Declare PtrSafe Function GetKeyboardState Lib "user32.dll" (ByVal lpKeyState As LongPtr) As Boolean
#Else
Private Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Declare Function GetKeyboardState Lib "user32.dll" (ByVal lpKeyState As Long) As Boolean
#End If
Private Const KEYEVENTF_EXTENDEDKEY As Long = &H1
Private Const KEYEVENTF_KEYUP As Long = &H2
Private Const VK_NUMLOCK As Byte = &H90
Private Const NumLockScanCode As Byte = &H45
Private Sub ToggleNumlock(enabled As Boolean)
Dim keystate(255) As Byte
'Test current keyboard state.
GetKeyboardState (VarPtr(keystate(0)))
If (Not keystate(VK_NUMLOCK) And enabled) Or (keystate(VK_NUMLOCK) And Not enabled) Then
'Send a keydown
keybd_event VK_NUMLOCK, NumLockScanCode, KEYEVENTF_EXTENDEDKEY, 0&
'Send a keyup
keybd_event VK_NUMLOCK, NumLockScanCode, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0&
End If
End Sub
Call it like this:
Sub Example()
'Turn Numlock off.
ToggleNumlock False
'Turn Numlock on.
ToggleNumlock True
End Sub
First of all, Copy and paste the following code in your Excel Sheet’s Module (Ex:-Module-1)...
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Const kCapital = 20
Private Const kNumlock = 144
Public Function CapsLock() As Boolean
CapsLock = KeyState(kCapital)
End Function
Public Function NumLock() As Boolean
NumLock = KeyState(kNumlock)
End Function
Private Function KeyState(lKey As Long) As Boolean
KeyState = CBool(GetKeyState(lKey))
End Function
Then, Copy and Paste the following in your Sheet's Code (Ex:- Sheet1 (Code))...
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Range("XFD1").FormulaR1C1 = "=NumLock()"
If Range("XFD1").Value = "FALSE" Then
SendKeys "{NUMLOCK}"
Else
End If
End Sub
Now Chill!!! For Each SelectionChange you make, Excel Refreshes itself and It makes sure that Numlock is On Always.
Replace "Capslock" instead of Numlock if you need it so as the case may be.
Thanks. Sashi Elit :)
I found this solution so far the best and does not interfere with NUMLOCK.
Put below code in a module and call it from anywhere in your project. The script object overwrites the SendKeys in VBA.
Public Sub Sendkeys(text as variant, Optional wait As Boolean = False)
Dim WshShell As Object
Set WshShell = CreateObject("wscript.shell")
WshShell.Sendkeys cstr(text), wait
Set WshShell = Nothing
End Sub
I found it in below thread:
SendKeys() permission denied error in Visual Basic
I tried all the suggestions until I noticed that it's not (NUMLOCK) but {NUMLOCK}. This worked for me.
Sub Numlock()
SendKeys "{NUMLOCK}"
End Sub
You almost had it!
The correct coding is:
Application.Sendkeys ("{NUMLOCK}")
This question already has answers here:
Why MS Excel crashes and closes during Worksheet_Change Sub procedure?
(3 answers)
Closed 7 years ago.
I'm looking for a way to automatically start a certain Sub when the cell's value is Zero.
E.g. if I enter "0" into Cell A1 the following Sub is supposed to run
Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"
And if I enter 1 (or any other Value above 0) into Cell A1 another Sub should run, e.g.
Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"
The calling of a Sub should happen right after I enter the value in excel, without pressing a button oder anything else.
Is there any way to do this?
Let's start with this code, which I will explain below.
Open the VB Editor Alt+F11. Right click the sheet that you want this behavior to occur on and select View Code.
Copy and paste the following code into the worksheet code.
Private Sub Worksheet_Change(ByVal Target As Range)
'CountLarge is an Excel 2007+ property, if using Excel 2003
'change to just Count
If Target.Cells.CountLarge > 1 Or IsEmpty(Target) Then Exit Sub
If Target.Address = "$A$1" Then
If Target.Value = 0 Then
Me.Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"
ElseIf Target.Value = 1 Then
Me.Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"
End If
End If
End Sub
The Worksheet_Change event is fired every time a user makes a change to the worksheet. If you change a cell value, for example, this event is triggered.
The first line within this subroutine checks to ensure that multiple cells weren't changed and that there was in fact an actual cell change, if either is not true then it will not continue.
Then we check to ensure that the value change happened in cell A1, if it did, we enter that IF statement.
From there, we check the value that was entered into cell A1. If the value was 0, the appropriate formula is added to H32. If the value was 1, the appropriate formula is added to B15. If a value other than 0 or 1 is entered into cell A1, nothing happens.
It is important to note that you must leave the cell for this event to trigger, so while this is a good start, I don't currently know of a way to get this event to fire without at least pressing enter or leaving the cell.
Update
After a bit of research and playing around, I've figured out how you can make this change without pressing enter or any other button, this will occur immediately after either '0' or '1' is pressed, even if you are editing the cell value. I used a keyboard handler from this previous SO question.
The code between the BEGIN KEYBOARD HANDLING and END KEYBOARD HANDLING event was from above.
Copy and paste the following code into the worksheet code of whichever sheet you want to capture these key strokes on:
Option Explicit
'BEGIN KEYBOARD HANDLING
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type MSG
hwnd As Long
Message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
Private Declare Function WaitMessage Lib "user32" () As Long
Private Declare Function PeekMessage Lib "user32" _
Alias "PeekMessageA" _
(ByRef lpMsg As MSG, ByVal hwnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long) As Long
Private Declare Function TranslateMessage Lib "user32" _
(ByRef lpMsg As MSG) 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 FindWindow Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Const WM_KEYDOWN As Long = &H100
Private Const PM_REMOVE As Long = &H1
Private Const WM_CHAR As Long = &H102
Private bExitLoop As Boolean
Sub StartKeyWatch()
Dim msgMessage As MSG
Dim bCancel As Boolean
Dim iKeyCode As Integer
Dim lXLhwnd As Long
'handle the ESC key.
On Error GoTo errHandler:
Application.EnableCancelKey = xlErrorHandler
'initialize this boolean flag.
bExitLoop = False
'get the app hwnd.
lXLhwnd = FindWindow("XLMAIN", Application.Caption)
Do
WaitMessage
'check for a key press and remove it from the msg queue.
If PeekMessage _
(msgMessage, lXLhwnd, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) Then
'strore the virtual key code for later use.
iKeyCode = msgMessage.wParam
'translate the virtual key code into a char msg.
TranslateMessage msgMessage
PeekMessage msgMessage, lXLhwnd, WM_CHAR, _
WM_CHAR, PM_REMOVE
'for some obscure reason, the following
'keys are not trapped inside the event handler
'so we handle them here.
If iKeyCode = vbKeyBack Then SendKeys "{BS}"
If iKeyCode = vbKeyReturn Then SendKeys "{ENTER}"
'assume the cancel argument is False.
bCancel = False
'the VBA RaiseEvent statement does not seem to return ByRef arguments
'so we call a KeyPress routine rather than a propper event handler.
Sheet_KeyPress _
ByVal msgMessage.wParam, ByVal iKeyCode, ByVal Selection, bCancel
'if the key pressed is allowed post it to the application.
If bCancel = False Then
PostMessage _
lXLhwnd, msgMessage.Message, msgMessage.wParam, 0
End If
End If
errHandler:
'allow the processing of other msgs.
DoEvents
Loop Until bExitLoop
End Sub
Sub StopKeyWatch()
'set this boolean flag to exit the above loop.
bExitLoop = True
End Sub
Private Sub Worksheet_Activate()
Me.StartKeyWatch
End Sub
Private Sub Worksheet_Deactivate()
Me.StopKeyWatch
End Sub
'End Keyboard Handling
Private Sub Sheet_KeyPress(ByVal KeyAscii As Integer, ByVal KeyCode As Integer, ByVal Target As Range, Cancel As Boolean)
'CountLarge is an Excel 2007+ property, if using Excel 2003
'change to just Count
If Target.Cells.CountLarge > 1 Or IsEmpty(Target) Then Exit Sub
If Target.Address = "$A$1" Then
If KeyAscii = 48 Then
Me.Range("H32").FormulaR1C1 = "=SUM(R[-4]C:R[-2]C)"
ElseIf KeyAscii = 49 Then
Me.Range("B15").FormulaR1C1 = "=SUM(R[-1]C:R[-1]C)"
End If
End If
End Sub
Additionally, right click on the ThisWorkbook object --> View Code, and add this code in:
Private Sub Workbook_Open()
Sheets("Sheet1").StartKeyWatch
End Sub
Be sure to change Sheet1 to whatever the name of your worksheet is.
The VBA will 'listen' for key presses and if the active cell is A1 and either a 0 or 1 is entered, the appropriate action will be performed even before the user does anything else.
I will add that his comes at a slight performance cost, as the code that executes on Workbook_Open takes a couple seconds to run.
Thanks to user Siddharth Rout for pointing out the potential issue with Count from Excel 2007 and on and directing me to use CountLarge instead.
I can manually shorten or lengthen the Name Box (which is just to the left of the Formula Bar) by dragging the "dot" to the right or left. (This also shortens or lengthens the Formula Bar.)
How can I do the adjustment with VBA??
PHEW!!!!
Things that you throw my way!!! :P
When I realized that there are is no native way to achieve what you want, I resorted to the API way but then I was again disappointed because the "Name Box" only exposed WS_CHILDWINDOW, WS_VISIBLE, CBS_DROPDOWN, CBSAUTOHSCROLL and CBS_HASSTRINGS. The "Dot" doesn't even have a handle.
Out of frustration, I started thinking along the lines of what Mark proposed in his answer. The Registry way. It took me some 20 odd mins to find the Registry key. But Alas, that joy also didn't last long when I realized that changing the registry key didn't have any effect till I restarted Excel.
After this there was only one way left Simulation of the mouse. I would have smashed my laptop on the ground if that didn't work!.
I tried with some hardcoded values in the beginning and was happy with the results. So here is the final version...
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function SetCursorPos Lib "user32" _
(ByVal X As Integer, ByVal Y As Integer) As Long
Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function GetWindowRect Lib "user32" _
(ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Sub mouse_event Lib "user32.dll" (ByVal dwFlags As Long, _
ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)
Private Const MOUSEEVENTF_MOVE = &H1 ' mouse move
Private Const MOUSEEVENTF_LEFTDOWN = &H2 ' left button down
Private Const MOUSEEVENTF_LEFTUP = &H4 ' left button up
Private Const MOUSEEVENTF_ABSOLUTE = &H8000 ' absolute move
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Dim pos As RECT
Sub Sample()
Dim hwndExcel As Long
Dim hwndPanel As Long
Dim hwndCombo As Long
Dim dest_x As Long
Dim dest_y As Long
Dim cur_x As Long
Dim cur_y As Long
Dim Position As POINTAPI
'~~> Get the handle of the Excel Window
hwndExcel = FindWindow("XLMAIN", Application.Caption)
If hwndExcel = 0 Then Exit Sub
'MsgBox "Excel Window Found"
'~~> Get the handle of the Panel where the Name Box is
hwndPanel = FindWindowEx(hwndExcel, ByVal 0&, "EXCEL;", vbNullString)
If hwndPanel = 0 Then Exit Sub
'MsgBox "Excel Panel Found"
hwndCombo = FindWindowEx(hwndPanel, ByVal 0&, "Combobox", vbNullString)
If hwndCombo = 0 Then Exit Sub
'MsgBox "Excel Name Box Found"
'~~> Retrieve the dimensions of the bounding rectangle of the
'~~> specified window. The dimensions are given in screen
'~~> coordinates that are relative to the upper-left corner of the screen.
GetWindowRect hwndCombo, pos
'~~> Get the approx location of the DOT. It is where the Combobox ends
cur_x = pos.Right
cur_y = pos.Top + 10
'~~> New Destination
dest_x = cur_x + 500 '<~~ Change width here
dest_y = cur_y
'~~> Move the cursor to the specified screen coordinates of the DOT.
SetCursorPos cur_x, cur_y
Wait 1 '<~~ Wait 1 second
'~~> Press the left mouse button on the DOT
mouse_event MOUSEEVENTF_LEFTDOWN, cur_x, cur_y, 0, 0
'~> Set the new destination. Take cursor there
SetCursorPos dest_x, dest_y
'~~> Press the left mouse button again to release it
mouse_event MOUSEEVENTF_LEFTUP, dest_x, dest_y, 0, 0
Wait 1
MsgBox "done"
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
End Sub
Instructions
Paste this code in a module and then from the sheet press ALT+F8 and then select Sample and press ALT+R
Tested in Excel 2010
Before
After
As there isn't a NameBox object within VBA Excel.Application I don't think it's possible in native VBA.
You'd have to delve into REGISTRY. The registry key is
Note: Even if you set the value, for it to take effect, you will have to close and open Excel.
I'm interested in creating a slide with powerpoint that will just display a new number in a Shape if theres a new day or time (midnight). I know java programming but haven't done programming in over 6 years now. I've never really used VB.
Dim CurrentTime = TimeValue(now)
Sub If CurrentTime.Now = TimeValue(24:00:00)
Then updateNum;
i = i+1
'Then I would like to display 'i' in a shape on powerpoint.
End Sub
Was thinking about doing a continous loop since the file will always be open and will never close.
Or Should I use a timer to countdown the seconds of the day then increment the number?
Unlike Excel, PowerPoint doesn't have OnTimer which would be helpful here.
Just making a loop will result in 100% processor consumption. You probably don't want that.
Calling Sleep() on each iteration will preserve processor time, but make the application unresponsible. That you probably don't want either.
So you should really set up a timer. If writing a VSTO addin is okay with you, then just use the Timer class, otherwise make one yourself in VBA:
Option Explicit
Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private hTimer As Long
Private PrevDate As Date
Public Sub StartTimer()
If hTimer = 0 Then
hTimer = SetTimer(0, 0, 1000, AddressOf TimerProc)
End If
End Sub
Public Sub StopTimer()
KillTimer 0, hTimer
hTimer = 0
End Sub
Private Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal nIDEvent As Long, ByVal dwTime As Long)
Dim CurDate As Date
CurDate = Date
If CurDate > PrevDate Then
PrevDate = CurDate
'Put your display code here
End If
End Sub
You can include this in a module in your presentation. It will fire on every slide change during a slide show:
Sub OnSlideShowPageChange(ByVal SSW As SlideShowWindow)
MsgBox (SSW.View.Slide.SlideIndex)
End Sub
Obviously, replace the MsgBox statement with code to update your text with the current date/time.
This works in PPT 2010 and should work as far back as Office 97, but isn't documented/supported, so MS might remove it whenever the whim strikes them. I don't know whether it works in PPT on the Mac.