PDA

View Full Version : Cannot display text in List Box on User Form while procedures are running



mkofler
02-28-2013, 09:02 PM
Hi
I have a User Form where the user can select various procedures to run. The procedures process various parts of a Word document, headers, footers, styles etc. There are about 60 procedures that the user can choose.

The User Form has a List Box where I want to display various messages while each procedure is running.
Each procedure uses the AddItem method of the List Box object to display a message in the List Box. The List Box has a Run button the user clicks to run the selected procedures. Here's the basic structure:


Sub UserFormInitialise() ' Initialize User Form
ProcedureArray= Array(Procedure1, Procedure2..., Procedurenn)

Load UserForm
UserForm.show VbModeless
End Sub


Sub UserForm_Run_Button_Click() ' Click event for Run button on User Form

Call RunProcedureSelectedByUser() '

End Sub

Sub RunProcedureSelectedByUser() ' Runs the procedures selected by user
For counter = 0 To UBound(ProcedureArray)
Application.Run ProcedureArray(counter)

Next counter
End Sub
Sub RunProcedure1()

do_someprocessing_on_Word_Document
userform.Listbox1.AddItem "A message"
End Sub

Sub RunProcedure2()
do_someprocessing_on_Word_Document
userform.Listbox1.AddItem "Another message"
End Sub

Sub RunProcedure2()...
Sub RunProcedure3()...


However the problem is that the all the messages are added to List Box after all the macros that process the Word document have finished running.
I would like to display each message in the List Box as each macro runs so the user has information about what is running and what is being processed.

It seems that while the macros are processing the Word document, the List Box on the User Form does not get focus, even though I have tried to set the focus on the User Form and List Box.
So is the problem:
The structure - Because the code that processes the Word document is in a separate module from the User Form code.
A limitation of VBA - User form can't have focus until other procedures finish?
I need to set the focus for the User for/List box somewhere specific - change event for the List Box perhaps?Can anybody suggest a way I can display the messages in the List Box as each macro is running?

macropod
03-01-2013, 01:22 AM
Seems a strange way of providing feedback. Why not use a textbox? Besides, merely adding an item to a listbox isn't sufficient to display it.

mkofler
03-01-2013, 04:51 PM
Thanks Paul.
Can you suggest a better way to provide feedback to the user?
Also could you clarify what you mean by "merely adding an item to a listbox isn't enough to display it". What else should I be doing?

macropod
03-01-2013, 05:06 PM
Thanks Paul.
Can you suggest a better way to provide feedback to the user?
I already suggested updating a textbox. Or you could use a progress bar, like the one described here: http://spreadsheetpage.com/index.php/site/tip/displaying_a_progress_indicator/
The code would need minor adaptations to work with Word, the main issue being working out where & when you could update a counter.

Also could you clarify what you mean by "merely adding an item to a listbox isn't enough to display it". What else should I be doing?
You should also do something like:
With ListBox1
.AddItem "A message"
.Selected(.ListCount - 1) = True
'.Selected(.ListCount - 1) = False
End With
using '.Selected(.ListCount - 1) = True' highlights the latest entry. Resetting it to False leaves it visible, but not highlighted.

fumei
03-01-2013, 05:55 PM
"•A limitation of VBA - User form can't have focus until other procedures finish?"

It is a limitation because VBA is not truly multi-threaded.

macropod
03-01-2013, 06:00 PM
"•A limitation of VBA - User form can't have focus until other procedures finish?"

It is a limitation because VBA is not truly multi-threaded.
That's only partly true. A userform can be updated while the code is running. The link I provided demonstrates this.

Another way of providing feedback, without the userform overheads, is to update Word's status bar.

fumei
03-01-2013, 06:15 PM
It is partly true because DoEvents is still not truly multi-threaded. Yes, the bar is changed throughout the progress of the procedure...BUT...it only works because DoEvents pops focus in and OUT (i.e the original procedure is NOT running). Sure it works, but only because the inouts are fast.

Yes the userform is updated, but it has nothing to do with VBA being multi-threaded. It is not. The calling procedure is halted while DoEvents does something else.

gmaxey
03-01-2013, 08:14 PM
I would use a label control and set the .caption property for these notification messages. Or rather than a progress bar you can simply use a second userform. Use of a second userform is demonstrated in in my Word Usage and Frequency Add-In:http://gregmaxey.mvps.org/word_tip_pages/word_usage_and_frequency_report.html

fumei
03-01-2013, 08:46 PM
What does he know...don't listen to him. Two userform...bleeeech. Just kidding. I am just not sure there is an advantage.

gmaxey
03-01-2013, 09:43 PM
Gerry,
The main userform in that add-in is a bit busy and if using a label control or textbox on the same form to show progress notification could get lost chaff. Instead I hid the main form and displayed a compact modeless form to indicate progress.

fumei
03-02-2013, 01:11 PM
I know, it is one of the VERY rare times (IMO) a secondary userform is reasonable. Although........one solution is to to make the first userform less "busy". It seems to me that busy is not - in itself - a fully legitimate reason for a secondary userform.

Paul_Hossler
03-03-2013, 09:00 AM
This is an Excel example I use (nothing I do in Word is long enough to need much in the way of progress bars)

The code should be convertable to Word since it's basically VBA

It just scrolls messages up a listbox, along with a progress bar


Option Explicit
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Const cWhiteSquare As Long = &H25A1
Const cWhiteDiamond As Long = &H25CA
Const cBlackCircle As Long = &H25CF
Dim W As clsScrollMsg
Sub test()
Dim i As Long

Set W = New clsScrollMsg

Call W.ShowForm("Demo: Total of 50 steps", 50)

For i = 1 To 50
Select Case i Mod 3

Case 0, 1
Call AnotherSubroutine1(W)
Case 2
Call AnotherSubroutine2(W, "Still working on " & i)
End Select

W.NumberProcessed = i

Next i

Set W = Nothing

End Sub

Private Sub AnotherSubroutine1(oWindow As clsScrollMsg)
Dim d As Long

d = Int(1000 * Rnd())
oWindow.ScrollingWindowUpdate = ChrW(cBlackCircle) & " " & "Called in subroutine #1 (rnd = " & Format(d, "#,##0") & ")"
Call Sleep(d)
End Sub
Private Sub AnotherSubroutine2(oWindow As clsScrollMsg, s As String)
Dim d As Long

d = Int(1000 * Rnd())
oWindow.ScrollingWindowUpdate = ChrW(cBlackCircle) & " " & "This is in subroutine #2 (" & s & ")"
Call Sleep(d)
End Sub



I'd think that you just need to add oWindow.ScrollingWindowUpdate = " " property in the modules that you want to provide feed back on

Paul