Excel VBA - get double-clicked word in userform multiline textbox - vba

The Task: my goal is to extract the highlighted word from a multi-line TextBox in a UserForm after a double-click.
Used Properties: Whereas it's absolutely no problem to highlight a given string position via the TextBox properties .SelStart and .SelLength, it isn't as easy the other way round: a users DblClick highlights a whole word string, but Excel doesn't reset the .SelStart value at the starting position of the highlighted text as one could assume, the .SelStart value remains there where the user double-clicks.
My Question: is there any possibility to catch the highlighted text starting position directly as set by the application?
My work around: I will demonstrate a very simple work around to reconstruct the high-lighted word just by checking the following and preceding e.g. 20 letters right and left to the actual clicking position (of course, one could use regex as well and refine the example code):
Private Sub TextBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
Dim sTxt As String, sSel As String ' edited due to comment below
Dim selPos As Long, i As Long, i2 As Long ' "
TextBox1.SetFocus
' this is the User's DblClick Position,
' but not the starting Position of the highlighted Word
' after DblClick
selPos = TextBox1.SelStart
sTxt = Replace(Replace(TextBox1.Text, vbCrLf, vbLf), "\", ".")
If TextBox1.SelLength > 0 Then
sSel = TextBox1.SelText
Else
sSel = Mid(sTxt, selPos + 1, 5)
' check the preceding 20 letters
i = selPos
For i = selPos To (selPos - 20) Step -1
If i < 0 Then Exit For
Select Case Left(Mid(sTxt, i + 1), 1)
Case " ", vbLf, ":", ".", "?", """", "'", "(", ")"
sSel = Mid(sTxt, i + 2, selPos - i)
Exit For
End Select
Next i
' check the following 20 letters
i2 = selPos
For i2 = selPos To (selPos + 20)
If i2 > Len(sTxt) Then Exit For
Select Case Left(Mid(sTxt, i2 + 1), 1)
Case " ", vbLf, ":", ".", "?", """", "'", ")", "("
sSel = Replace(Mid(sTxt, i + 2, i2 - i - IIf(i = i2, 0, 1)), vbLf, "")
Exit For
End Select
Next i2
End If
' Show the highlighted word
Me.Label1.Text = sSel
End Sub
Additional explanations to found solution in UserForm code module (thx #Rory)
In order to actually get the double-clicked highlighted string from a multi-line textbox, you'll need three steps to solve the timing problem:
As the textbox position properties SelStart and SelLength aren't set yet in the DblClick event,
it's necessary to assign True to a boolean variable/marker (bCheck).
Use the MouseUp event to get the final position properties after checking for bCheck.
In order to count correctly, it'll be necessary to remove e.g. vbLf within the pair of carriage returns Chr(13) (=vbCr) and line feeds Chr(10) (=vbLf) on MS systems.
Caveat: Note that AFAIK Mac systems use only line feeds Chr(10) as ending sign, so IMO you can omit replacing in this case.
Final Code
Option Explicit
Private bCheck As Boolean
' [1] assign True to boolean variable
Private Sub TextBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
bCheck = True ' set marker to True
End Sub
Private Sub TextBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If bCheck Then
bCheck = False ' reset marker to False
' [2][3] extract the highlighted doubleclicked word from multi-line textbox
MsgBox Trim(Mid(Replace(Me.TextBox1.Text, vbLf, ""), Me.TextBox1.SelStart + 1, Me.TextBox1.SelLength))
End If
End Sub

I think it's a timing issue. It seems to work if you use a flag variable and the MouseUp event in conjunction with the DblClick event:
Private bCheck As Boolean
Private Sub TextBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
bCheck = True
End Sub
Private Sub TextBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If bCheck Then
bCheck = False
MsgBox Me.TextBox1.SelStart & "; " & Me.TextBox1.SelLength
End If
End Sub

Related

Get focus on unbound textbox when form returns no records

I'm a little stumped.
I've got an MS Access front end application for an SQL Server back end. I have an orders form with a list box that, when selected and a "Notes" button is clicked will open another form of notes. This is a continuous form and has a data source (linked table - a view) from the back end database.
When the notes button is clicked in the main orders form, it passes a filter and an OpenArgs string to the Notes form in this code:
Private Sub cmdItemNotes_Click()
Dim i As Integer
Dim ordLine As Boolean
Dim line As Integer
Dim args As String
If Me.lstOrders.ItemsSelected.count = 1 Then
ordLine = False
With Me.lstOrders
For i = 0 To .ListCount - 1
If .selected(i) Then
If .Column(16, i) = "Orders" Then
ordLine = True
line = .Column(0, i)
End If
End If
Next i
End With
If ordLine Then
args = "txtLineID|" & line & "|txtCurrentUser|" & DLookup("[User]", "tblUsers", "[Current] = -1") & "|txtSortNum|" & _
Nz(DMax("[SortNum]", "dbo_vwInvoiceItemNotesAll", "[LineID] = " & line), 0) + 1 & "|"
DoCmd.OpenForm "frmInvoiceItemNotes", , , "LineID = " & line, , , args
Else
'Potting order notes
End If
Else: MsgBox "Please select one item for notes."
End If
Here is my On Load code for the Notes form:
Private Sub Form_Load()
Dim numPipes As Integer
Dim ArgStr As String
Dim ctl As control
Dim ctlNam As String
Dim val As String
Dim i As Integer
ArgStr = Me.OpenArgs
numPipes = Len(ArgStr) - Len(Replace(ArgStr, "|", ""))
For i = 1 To (numPipes / 2)
ctlNam = Left(ArgStr, InStr(ArgStr, "|") - 1)
Set ctl = Me.Controls(ctlNam)
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(ctlNam) + 1))
val = Left(ArgStr, InStr(ArgStr, "|") - 1)
ctl.Value = val
ArgStr = Right(ArgStr, Len(ArgStr) - (Len(val) + 1))
Next i
End Sub
This code executes fine. The form gets filtered to only see the records (notes) for the line selected back in the orders form.
Because this is editing a table in the back end, I use stored procedures in a pass through query to update the table, not bound controls. The bound controls in the continuous form are for displaying current records only. So... I have an unbound textbox (txtNewNote) in the footer of the form to type a new note, edit an existing note, or post a reply to an existing note.
As stated above, the form filters on load. Everything works great when records show. But when it filters to no records, the txtNewNote textbox behaves quite differently. For instance, I have a combo box to mention other users. Here is the code after update for the combo box:
Private Sub cmbMention_AfterUpdate()
Dim ment As String
If Me.txtNewNote = Mid(Me.txtNewNote.DefaultValue, 2, Len(Me.txtNewNote.DefaultValue) - 2) Then
Me.txtNewNote.Value = ""
End If
If Not IsNull(Me.cmbMention) Then
ment = " #" & Me.cmbMention & " "
If Not InStr(Me.txtNewNote, ment) > 0 Then
Me.txtNewNote = Me.txtNewNote & ment
End If
End If
With Me.txtNewNote
.SetFocus
.SelStart = Len(Nz(Me.txtNewNote, ""))
End With
End Sub
The problem occurs with the line
.SelStart = Len(Nz(Me.txtNewNote, ""))
When there are records to display, it works. When there are no records to display, it throws the Run-time error 2185 "You can't reference a property or method for a control unless the control has the focus."
Ironically, if I omit this line and make the .SetFocus the last line of code in the sub, the control is in focus with the entire text highlighted.
Why would an unbound textbox behave this way just because the filter does not show records?
Thanks!

Conditional formatting prevents combobox dropdown on continuous form

The following code filters the dropdown list of a combobox in a continuous subform and allows the user to move up and down the dropdown to select data
'Move through the dropdown using up/down arrow keys
Private Sub IngCombo_KeyDown(KeyCode As Integer, Shift As Integer)
Dim MsgBoxResponse As String
Select Case KeyCode
'Tab button is pressed with "" in the field. Access returns a warning message if not dealt with
Case 9 'Tab Button
If Me.IngCombo.Text = "" Then
MsgBoxResponse = MsgBox("Ingredient you entered is not in the list" & vbCrLf & "Would you like to try again?", vbYesNo, "Ingredient not recognised")
Select Case MsgBoxResponse
Case Is = 6
KeyCode = 0
Case Is = 7
KeyCode = 0
Me.Undo
End Select
End If
Case vbKeyDown
Me.IngCombo.Selected(Me.IngCombo.ListIndex + 1) = True
KeyCode = 0
Me.IngCombo.DropDown
Case vbKeyUp
Me.IngCombo.Selected(Me.IngCombo.ListIndex - 1) = True
KeyCode = 0
Me.IngCombo.DropDown
Case vbKeyEscape
Me.IngCombo.Text = ""
Me.Undo
End Select
End Sub
Private Sub IngCombo_KeyUp(KeyCode As Integer, Shift As Integer)
'Filter dropdown to match what the user has typed
'This combo's control source is the IngredientID, but the ID is hidden, hence the SQL selects both the ID and Ingredient text
Dim UserText As String
If Len(Me.IngCombo.Text) > 0 Then
UserText = Me.IngCombo.Text
Me.IngCombo.RowSource = "SELECT IngredientsTbl.IngredientID, IngredientsTbl.Ingredient " _
& "FROM IngredientsTbl WHERE (((IngredientsTbl.Ingredient) LIKE '*" & UserText & "*'));"
If Me.IngCombo.ListCount > 0 Then
Me.IngCombo.DropDown
End If
Else
Me.IngCombo.RowSource = "SELECT IngredientsTbl.IngredientID, IngredientsTbl.Ingredient " _
& "FROM IngredientsTbl;"
End If
End Sub
Private Sub IngCombo_LostFocus()
Me.IngCombo.RowSource = "SELECT IngredientsTbl.IngredientID, IngredientsTbl.Ingredient " _
& "FROM IngredientsTbl ORDER BY IngredientsTbl.Ingredient;"
End Sub
'If user types string that is found by the 'Key Up' routine above but is in the middle of a string i.e. 'milk' is typed and 'buttermilk' is highlighted
'Then pressing tab results in the not in list error
Private Sub IngCombo_NotInList(NewData As String, Response As Integer)
MsgBox "No ingredient matches your search", vbOKOnly, "Nothing Found"
Me.IngCombo = ""
Response = acDataErrContinue
End Sub
This works as intended.
I then add a conditional format, [text19]>50, to the ‘IngCombo’ combobox. This also works as intended but changes the behaviour of the 'IngCombo' combobox. The dropdown no longer appears, and when the user types in the ‘IngCombo’ field of a new record, it has the effect of filtering the text in the ‘IngCombo’ field of the other records of the continuous form. Eg if the user types ‘mint’, then records that contain mint in ‘IngCombo’ are shown, but all the others are blank.
The word ‘calculating’ appears momentarily in the bottom left, replacing the words 'form view' after a key is pressed. I assume this is the conditional formatting doing its work and disrupting the code, as ‘calculating’ does not appear when there is no conditional formatting.
Is there a way of maintaining the functionailty of the code and also having the conditional formating
Update after June7's comment that referenced Allen Browne code.Tried moving code into a Private Sub that is called from the combo's Change event
Private Sub IngCombo_Change()
Dim Cmbo As ComboBox
Set Cmbo = Me.IngCombo
Dim NewText As String
NewText = Cmbo.Text
Call ReloadIngCombo(NewText)
End Sub
Private Sub ReloadIngCombo(UserText As String)
If Len(UserText) > 1 Then
Me.IngCombo.RowSource = "SELECT IngredientsTbl.IngredientID, IngredientsTbl.Ingredient " _
& "FROM IngredientsTbl WHERE (((IngredientsTbl.Ingredient) LIKE '*" & UserText & "*'));"
If Me.IngCombo.ListCount > 0 Then
Me.IngCombo.DropDown
End If
Else
Me.IngCombo.RowSource = "SELECT IngredientsTbl.IngredientID, IngredientsTbl.Ingredient " _
& "FROM IngredientsTbl;"
End If
End Sub
This works as intended. Adding a conditional format to the combobox prevents the dropdowns being shown to the user

How to insert text, at cursor position, within existing text in a textbox using Command buttons?

I have a custom calculator whose buttons when pressed, insert their caption into a textbox.
Private Sub CmdBtn_Click()
Me.TextBox.Value = Me.TextBox.Value & " " & CmdBtn.Caption
End Sub
With this method one cannot insert text in a text that exists.
For example, if current text is "abc" then I want to input "x" between "a" and "bc" to give me end result of "axbc".
I will put my cursor at the position where new text should be placed.
Store the cursor position using a variable when leaving the textbox:
Option Compare Database
Option Explicit
Private LastPosition As Long
Private Sub CmdBtn_Click()
Dim Text As String
Text = Me!TargetBox.Value
' Insert source text at the stored position.
Me!TargetBox.Value = Left(Text, LastPosition) & Me!SourceBox.Value & Mid(Text, LastPosition + 1)
End Sub
Private Sub UserName_LostFocus()
' Store cursor position.
LastPosition = Me!UserName.SelStart
End Sub
The solution below works perfectly for inputting text at any position of TextBox. Let me know if someone has a better solution than this.
Private LastPosition as Long
Private Text as String
Private Sub TextBox_LostFocus()
If LastPosition = 0 and IsNull(Me!TextBox.Value) Then
Me!TextBox = " " + Me.ActiveControl.Caption
LastPosition = LastPosition + Len(Me.ActiveControl.Caption)
ElseIf
LastPosition >= AND Not IsNull(Me!TextBox.Value) Then
Text = Me!TextBox.Value
Me!TextBox.Value = Left( Text, LastPosition) & " " & _
Me.ActiveControl.Caption & Mid(Text, LastPosition + 1)
LastPosition = LastPosition + Len(Me.ActiveControl.Caption)
Else
Me!TextBox.Value = Me!TextBox.Value + " " + Me.ActiveControl.Caption
End If
End Sub
Interestingly, Access doesn't store SelStart if the form is in edit mode. No idea why. Perhaps this is why the OP and the first answer got different results, though.
Anyway, this fix will solve that issue.
Private LastPosition As Long
Private Sub UserName_Change()
' Store cursor position in edit mode
LastPosition = Me!UserName.SelStart
End Sub
Private Sub UserName_KeyUp(KeyCode As Integer, Shift As Integer)
' stores cursor position on non-change typing, like arrow keys
LastPosition = Me!UserName.SelStart
End Sub
Private Sub UserName_MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)
' stores cursor position when user clicks in a new spot
LastPosition = Me!UserName.SelStart
End Sub
Private Sub UserName_LostFocus()
' Store cursor position, but only if not in edit mode
If Not Me.Dirty Then LastPosition = Me!UserName.SelStart
End Sub

Can I use Excel's save progress bar? [duplicate]

I'm doing an Excel app that needs a lot data updating from a database, so it takes time. I want to make a progress bar in a userform and it pops up when the data is updating. The bar I want is just a little blue bar moves right and left and repeats till the update is done, no percentage needed.
I know I should use the progressbar control, but I tried for sometime, but can't make it.
My problem is with the progressbar control, I can't see the bar 'progress'. It just completes when the form pops up. I use a loop and DoEvent but that isn't working. Plus, I want the process to run repeatedly, not just one time.
Sometimes a simple message in the status bar is enough:
This is very simple to implement:
Dim x As Integer
Dim MyTimer As Double
'Change this loop as needed.
For x = 1 To 50
' Do stuff
Application.StatusBar = "Progress: " & x & " of 50: " & Format(x / 50, "0%")
Next x
Application.StatusBar = False
Here's another example using the StatusBar as a progress bar.
By using some Unicode Characters, you can mimic a progress bar. 9608 - 9615 are the codes I tried for the bars. Just select one according to how much space you want to show between the bars. You can set the length of the bar by changing NUM_BARS. Also by using a class, you can set it up to handle initializing and releasing the StatusBar automatically. Once the object goes out of scope it will automatically clean up and release the StatusBar back to Excel.
' Class Module - ProgressBar
Option Explicit
Private statusBarState As Boolean
Private enableEventsState As Boolean
Private screenUpdatingState As Boolean
Private Const NUM_BARS As Integer = 50
Private Const MAX_LENGTH As Integer = 255
Private BAR_CHAR As String
Private SPACE_CHAR As String
Private Sub Class_Initialize()
' Save the state of the variables to change
statusBarState = Application.DisplayStatusBar
enableEventsState = Application.EnableEvents
screenUpdatingState = Application.ScreenUpdating
' set the progress bar chars (should be equal size)
BAR_CHAR = ChrW(9608)
SPACE_CHAR = ChrW(9620)
' Set the desired state
Application.DisplayStatusBar = True
Application.ScreenUpdating = False
Application.EnableEvents = False
End Sub
Private Sub Class_Terminate()
' Restore settings
Application.DisplayStatusBar = statusBarState
Application.ScreenUpdating = screenUpdatingState
Application.EnableEvents = enableEventsState
Application.StatusBar = False
End Sub
Public Sub Update(ByVal Value As Long, _
Optional ByVal MaxValue As Long= 0, _
Optional ByVal Status As String = "", _
Optional ByVal DisplayPercent As Boolean = True)
' Value : 0 to 100 (if no max is set)
' Value : >=0 (if max is set)
' MaxValue : >= 0
' Status : optional message to display for user
' DisplayPercent : Display the percent complete after the status bar
' <Status> <Progress Bar> <Percent Complete>
' Validate entries
If Value < 0 Or MaxValue < 0 Or (Value > 100 And MaxValue = 0) Then Exit Sub
' If the maximum is set then adjust value to be in the range 0 to 100
If MaxValue > 0 Then Value = WorksheetFunction.RoundUp((Value * 100) / MaxValue, 0)
' Message to set the status bar to
Dim display As String
display = Status & " "
' Set bars
display = display & String(Int(Value / (100 / NUM_BARS)), BAR_CHAR)
' set spaces
display = display & String(NUM_BARS - Int(Value / (100 / NUM_BARS)), SPACE_CHAR)
' Closing character to show end of the bar
display = display & BAR_CHAR
If DisplayPercent = True Then display = display & " (" & Value & "%) "
' chop off to the maximum length if necessary
If Len(display) > MAX_LENGTH Then display = Right(display, MAX_LENGTH)
Application.StatusBar = display
End Sub
Sample Usage:
Dim progressBar As New ProgressBar
For i = 1 To 100
Call progressBar.Update(i, 100, "My Message Here", True)
Application.Wait (Now + TimeValue("0:00:01"))
Next
In the past, with VBA projects, I've used a label control with the background colored and adjust the size based on the progress. Some examples with similar approaches can be found in the following links:
http://oreilly.com/pub/h/2607
http://www.ehow.com/how_7764247_create-progress-bar-vba.html
http://spreadsheetpage.com/index.php/tip/displaying_a_progress_indicator/
Here is one that uses Excel's Autoshapes:
http://www.andypope.info/vba/pmeter.htm
I'm loving all the solutions posted here, but I solved this using Conditional Formatting as a percentage-based Data Bar.
This is applied to a row of cells as shown below. The cells that include 0% and 100% are normally hidden, because they're just there to give the "ScanProgress" named range (Left) context.
In the code I'm looping through a table doing some stuff.
For intRow = 1 To shData.Range("tblData").Rows.Count
shData.Range("ScanProgress").Value = intRow / shData.Range("tblData").Rows.Count
DoEvents
' Other processing
Next intRow
Minimal code, looks decent.
============== This code goes in Module1 ============
Sub ShowProgress()
UserForm1.Show
End Sub
============== Module1 Code Block End =============
Create a Button on a Worksheet; map button to "ShowProgress" macro
Create a UserForm1 with 2 Command Buttons and 3 Labels so you get the following objects
Element
Purpose
Properties to set
UserForm1
canvas to hold other 5 elements
CommandButton1
Close UserForm1
Caption: "Close"
CommandButton2
Run Progress Bar Code
Caption: "Run"
Bar1 (label)
Progress bar graphic
BackColor: Blue
BarBox (label)
Empty box to frame Progress Bar
BackColor: White
Counter (label)
Display the integers used to drive the progress bar
Then add this code to UserForm1:
======== Attach the following code to UserForm1 =========
Option Explicit
' This is used to create a delay to prevent memory overflow
' remove after software testing is complete
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub UserForm_Initialize()
Bar1.Tag = Bar1.Width ' Memorize initial/maximum width
Bar1.Width = 0
End Sub
Sub ProgressBarDemo()
Dim intIndex As Integer
Dim sngPercent As Single
Dim intMax As Integer
'==============================================
'====== Bar Length Calculation Start ==========
'-----------------------------------------------'
' This section is where you can use your own '
' variables to increase bar length. '
' Set intMax to your total number of passes '
' to match bar length to code progress. '
' This sample code automatically runs 1 to 100 '
'-----------------------------------------------'
intMax = 100
For intIndex = 1 To intMax
sngPercent = intIndex / intMax
Bar1.Width = Int(Bar1.Tag * sngPercent)
Counter.Caption = intIndex
'======= Bar Length Calculation End ===========
'==============================================
DoEvents
'------------------------
' Your production code would go here and cycle
' back to pass through the bar length calculation
' increasing the bar length on each pass.
'------------------------
'this is a delay to keep the loop from overrunning memory
'remove after testing is complete
Sleep 10
Next
End Sub
Private Sub CommandButton1_Click() 'CLOSE button
Unload Me
End Sub
Private Sub CommandButton2_Click() 'RUN button
ProgressBarDemo
End Sub
================= UserForm1 Code Block End =====================
I liked the Status Bar from this page:
https://wellsr.com/vba/2017/excel/vba-application-statusbar-to-mark-progress/
I updated it so it could be used as a called procedure. No credit to me.
Call showStatus(Current, Total, " Process Running: ")
Private Sub showStatus(Current As Integer, lastrow As Integer, Topic As String)
Dim NumberOfBars As Integer
Dim pctDone As Integer
NumberOfBars = 50
'Application.StatusBar = "[" & Space(NumberOfBars) & "]"
' Display and update Status Bar
CurrentStatus = Int((Current / lastrow) * NumberOfBars)
pctDone = Round(CurrentStatus / NumberOfBars * 100, 0)
Application.StatusBar = Topic & " [" & String(CurrentStatus, "|") & _
Space(NumberOfBars - CurrentStatus) & "]" & _
" " & pctDone & "% Complete"
' Clear the Status Bar when you're done
' If Current = Total Then Application.StatusBar = ""
End Sub
You can create a form in VBA, with code to increase the width of a label control as your code progresses. You can use the width property of a label control to resize it. You can set the background colour property of the label to any colour you choose. This will let you create your own progress bar.
The label control that resizes is a quick solution. However, most people end up creating individual forms for each of their macros. I use the DoEvents function and a modeless form to use a single form for all your macros.
Here is a blog post I wrote about it: http://strugglingtoexcel.wordpress.com/2014/03/27/progress-bar-excel-vba/
All you have to do is import the form and a module into your projects, and call the progress bar with: Call modProgress.ShowProgress(ActionIndex, TotalActions, Title.....)
I hope this helps.
Sub ShowProgress()
' Author : Marecki
Const x As Long = 150000
Dim i&, PB$
For i = 1 To x
PB = Format(i / x, "00 %")
Application.StatusBar = "Progress: " & PB & " >>" & String(Val(PB), Chr(183)) & String(100 - Val(PB), Chr(32)) & "<<"
Application.StatusBar = "Progress: " & PB & " " & ChrW$(10111 - Val(PB) / 11)
Application.StatusBar = "Progress: " & PB & " " & String(100 - Val(PB), ChrW$(9608))
Next i
Application.StatusBar = ""
End SubShowProgress
Hi modified version of another post by Marecki. Has 4 styles
1. dots ....
2 10 to 1 count down
3. progress bar (default)
4. just percentage.
Before you ask why I didn't edit that post is I did and it got rejected was told to post a new answer.
Sub ShowProgress()
Const x As Long = 150000
Dim i&, PB$
For i = 1 To x
DoEvents
UpdateProgress i, x
Next i
Application.StatusBar = ""
End Sub 'ShowProgress
Sub UpdateProgress(icurr As Long, imax As Long, Optional istyle As Integer = 3)
Dim PB$
PB = Format(icurr / imax, "00 %")
If istyle = 1 Then ' text dots >>.... <<'
Application.StatusBar = "Progress: " & PB & " >>" & String(Val(PB), Chr(183)) & String(100 - Val(PB), Chr(32)) & "<<"
ElseIf istyle = 2 Then ' 10 to 1 count down (eight balls style)
Application.StatusBar = "Progress: " & PB & " " & ChrW$(10111 - Val(PB) / 11)
ElseIf istyle = 3 Then ' solid progres bar (default)
Application.StatusBar = "Progress: " & PB & " " & String(100 - Val(PB), ChrW$(9608))
Else ' just 00 %
Application.StatusBar = "Progress: " & PB
End If
End Sub
About the progressbar control in a userform, it won't show any progress if you don't use the repaint event. You have to code this event inside the looping (and obviously increment the progressbar value).
Example of use:
userFormName.repaint
Just adding my part to the above collection.
If you are after less code and maybe cool UI. Check out my GitHub for Progressbar for VBA
a customisable one:
The Dll is thought for MS-Access but should work in all VBA platform with minor changes. There is also an Excel file with samples. You are free to expand the vba wrappers to suit your needs.
This project is currently under development and not all errors are covered. So expect some!
You should be worried about 3rd party dlls and if you are, please feel free to use any trusted online antivirus before implementing the dll.
There have been many other great posts, however I'd like to say that theoretically you should be able to create a REAL progress bar control:
Use CreateWindowEx() to create the progress bar
A C++ example:
hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPTSTR) NULL, WS_CHILD | WS_VISIBLE, rcClient.left,rcClient.bottom - cyVScroll,rcClient.right, cyVScroll,hwndParent, (HMENU) 0, g_hinst, NULL);
hwndParent Should be set to the parent window. For that one could use the status bar, or a custom form! Here's the window structure of Excel found from Spy++:
This should therefore be relatively simple using FindWindowEx() function.
hwndParent = FindWindowEx(Application.hwnd,,"MsoCommandBar","Status Bar")
After the progress bar has been created you must use SendMessage() to interact with the progress bar:
Function MAKELPARAM(ByVal loWord As Integer, ByVal hiWord As Integer)
Dim lparam As Long
MAKELPARAM = loWord Or (&H10000 * hiWord)
End Function
SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, 100))
SendMessage(hwndPB, PBM_SETSTEP, 1, 0)
For i = 1 to 100
SendMessage(hwndPB, PBM_STEPIT, 0, 0)
Next
DestroyWindow(hwndPB)
I'm not sure how practical this solution is, but it might look somewhat more 'official' than other methods stated here.
You can add a Form and name it as Form1, add a Frame to it as Frame1 as well as Label1 too.
Set Frame1 width to 200, Back Color to Blue.
Place the code in the module and check if it helps.
Sub Main()
Dim i As Integer
Dim response
Form1.Show vbModeless
Form1.Frame1.Width = 0
For i = 10 To 10000
With Form1
.Label1.Caption = Round(i / 100, 0) & "%"
.Frame1.Width = Round(i / 100, 0) * 2
DoEvents
End With
Next i
Application.Wait Now + 0.0000075
Unload Form1
response = MsgBox("100% Done", vbOKOnly)
End Sub
If you want to display on the Status Bar then you can use other way that's simpler:
Sub Main()
Dim i As Integer
Dim response
For i = 10 To 10000
Application.StatusBar = Round(i / 100, 0) & "%"
Next i
Application.Wait Now + 0.0000075
response = MsgBox("100% Done", vbOKOnly)
End Sub
I know this is an old thread but I had asked a similar question not knowing about this one. I needed an Excel VBA Progress Bar and found this link: Excel VBA StatusBar. Here is a generalized version that I wrote. There are 2 methods, a simple version DisplaySimpleProgressBarStep that defaults to '[|| ] 20% Complete' and a more generalized version DisplayProgressBarStep that takes a laundry list of optional arguments so that you can make it look like just about anything you wish.
Option Explicit
' Resources
' ASCII Chart: https://vbaf1.com/ascii-table-chart/
Private Enum LabelPlacement
None = 0
Prepend
Append
End Enum
#If VBA7 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr) 'For 64 Bit Systems
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'For 32 Bit Systems
#End If
Public Sub Test()
Call ProgressStatusBar(Last:=10)
End Sub
Public Sub Test2()
Const lMilliseconds As Long = 500
Dim lIndex As Long, lNumberOfBars As Long
Dim sBarChar As String
sBarChar = Chr$(133) ' Elipses …
sBarChar = Chr$(183) ' Middle dot ·
sBarChar = Chr$(176) ' Degree sign °
sBarChar = Chr$(171) ' Left double angle «
sBarChar = Chr$(187) ' Right double angle »
sBarChar = Chr$(166) ' Broken vertical bar ¦
sBarChar = Chr$(164) ' Currency sign ¤
sBarChar = Chr$(139) ' Single left-pointing angle quotation mark ‹
sBarChar = Chr$(155) ' Single right-pointing angle quotation mark ›
sBarChar = Chr$(149) ' Bullet •
sBarChar = "|"
For lIndex = 1 To 10
Call DisplayProgressBarStep(lIndex, 10, 50, LabelPlacement.Append, sBarChar)
Call Sleep(lMilliseconds)
Next
Call MsgBox("Status bar test completed.", vbOKOnly Or vbInformation, "Test2 Completed")
Call DisplayProgressBarStep(lIndex, 10, bClearStatusBar:=True)
End Sub
Public Sub Test2Simple()
Const lMilliseconds As Long = 500
Dim lIndex As Long, lNumberOfBars As Long
For lIndex = 1 To 10
Call DisplayProgressBarStep(lIndex, 10, 50)
Call Sleep(lMilliseconds)
Next
Call MsgBox("Status bar test completed.", vbOKOnly Or vbInformation, "Test2Simple Completed")
Call DisplayProgressBarStep(lIndex, 10, bClearStatusBar:=True)
End Sub
''' <summary>
''' Method to display an Excel ProgressBar. Called once for each step in the calling code process.
''' Defaults to vertical bar surrounded by square brackets with a trailing percentage label (e.g. [|||||] 20% Complete).
'''
''' Adapted
''' From: Excel VBA StatusBar
''' Link: https://www.wallstreetmojo.com/vba-status-bar/
''' </summary>
''' <param name="Step">The current step count.</param>
''' <param name="StepCount">The total number of steps.</param>
''' <param name="NumberOfBars">Optional, Number of bars displayed for StepCount. Defaults to StepCount. The higher the number, the longer the string.</param>
''' <param name="LabelPlacement">Optional, Can be None, Prepend or Append. Defaults to Append.</param>
''' <param name="BarChar">Optional, Character that makes up the horizontal bar. Defaults to | (Pipe).</param>
''' <param name="PrependedBoundaryText">Optional, Boundary text prepended to the StatusBar. Defaults to [ (Left square bracket).</param>
''' <param name="AppendedBoundaryText">Optional, Boundary text appended to the StatusBar. Defaults to ] (Right square bracket).</param>
''' <param name="ClearStatusBar">Optional, True to clear the StatusBar. Defaults to False.</param>
Private Sub DisplayProgressBarStep( _
lStep As Long, _
lStepCount As Long, _
Optional lNumberOfBars As Long = 0, _
Optional eLabelPlacement As LabelPlacement = LabelPlacement.Append, _
Optional sBarChar As String = "|", _
Optional sPrependedBoundaryText As String = "[", _
Optional sAppendedBoundaryText As String = "]", _
Optional bClearStatusBar As Boolean = False _
)
Dim lCurrentStatus As Long, lPctComplete As Long
Dim sBarText As String, sLabel As String, sStatusBarText As String
If bClearStatusBar Then
Application.StatusBar = False
Exit Sub
End If
If lNumberOfBars = 0 Then
lNumberOfBars = lStepCount
End If
lCurrentStatus = CLng((lStep / lStepCount) * lNumberOfBars)
lPctComplete = Round(lCurrentStatus / lNumberOfBars * 100, 0)
sLabel = lPctComplete & "% Complete"
sBarText = sPrependedBoundaryText & String(lCurrentStatus, sBarChar) & Space$(lNumberOfBars - lCurrentStatus) & sAppendedBoundaryText
Select Case eLabelPlacement
Case LabelPlacement.None: sStatusBarText = sBarText
Case LabelPlacement.Prepend: sStatusBarText = sLabel & " " & sBarText
Case LabelPlacement.Append: sStatusBarText = sBarText & " " & sLabel
End Select
Application.StatusBar = sStatusBarText
''Debug.Print "CurStatus:"; lCurrentStatus, "PctComplete:"; lPctComplete, "'"; sStatusBarText; "'"
End Sub
''' <summary>
''' Method to display a simple Excel ProgressBar made up of vertical bars | with a trailing label. Called once for each step in the calling code process.
'''
''' Adapted
''' From: Excel VBA StatusBar
''' Link: https://www.wallstreetmojo.com/vba-status-bar/
''' </summary>
''' <param name="Step">The current step count.</param>
''' <param name="StepCount">The total number of steps.</param>
''' <param name="NumberOfBars">Optional, Number of bars displayed for StepCount. Defaults to StepCount. The higher the number, the longer the string.</param>
''' <param name="ClearStatusBar">Optional, True to clear the StatusBar. Defaults to False.</param>
Private Sub DisplaySimpleProgressBarStep( _
lStep As Long, _
lStepCount As Long, _
Optional lNumberOfBars As Long = 0, _
Optional bClearStatusBar As Boolean = False _
)
Call DisplayProgressBarStep(lStep, lStepCount, lNumberOfBars, bClearStatusBar:=bClearStatusBar)
End Sub

A program to highlight words in a word document

I want a VB program that will allow me to put words into a text box on VB, (preferably those words are saved when closing the program so they are there for next time) have those words split more than likely with the s.Split to allow for multiple words, for example “famine”, “viewing”, “article”
That's the first part, the second part is I want the program to read the text in any open word document and those words in the text box, that are also in the word document will be highlighted. If it can't be done so it can read any open word document, then is it possible to make it able to attach a word document for it to read?
I want a program that you can write multiple words on, then have those words become highlighted in any word document.
I have been wrote this snippet time ago, it will teach you how to find and highlight word/phrases using Regular Expressions, you could easily adapt it to a WORD document reading, then the most important part of the problem would be solved with this:
#Region " [RichTextBox] FindNext RegEx "
' [ FindNext RegEx ]
'
' //By Elektro H#cker
'
' Examples :
'
' RichTextBox1.Text = "Hello World!, Hello World!, Hello World!"
'
' FindNext(RichTextBox1, "hello", FindDirection.Down, System.Text.RegularExpressions.RegexOptions.IgnoreCase, Color.LightBlue, Color.Black)
' FindNext(RichTextBox1, "hello", FindDirection.Up, System.Text.RegularExpressions.RegexOptions.IgnoreCase, Color.Red, Color.Black)
'
' Private Sub RichTextBox_Enter(sender As Object, e As EventArgs) ' Handles RichTextBox1.Enter
' ' Restore Selection Colors before search next match.
' sender.SelectionBackColor = DefaultBackColor
' sender.SelectionColor = DefaultForeColor
' End Sub
Public Enum FindDirection As Short
Up = 0
Down = 1
End Enum
' FindNext
Private Sub FindNext(ByVal [Control] As RichTextBox, _
ByVal SearchText As String, _
ByVal Direction As FindDirection, _
Optional ByVal IgnoreCase As System.Text.RegularExpressions.RegexOptions = System.Text.RegularExpressions.RegexOptions.None, _
Optional ByVal Highlight_BackColor As Color = Nothing, _
Optional ByVal Highlight_ForeColor As Color = Nothing)
If [Control].TextLength = 0 Then Exit Sub
' Start searching at 'SelectionStart'.
Dim Search_StartIndex As Integer = [Control].SelectionStart
' Stores the MatchIndex count
Dim matchIndex As Integer = 0
' Flag to check if it's first find call
Static First_Find As Boolean = True
' Checks to don't ommit the selection of first match if match index is exactly at 0 start point.
If First_Find _
AndAlso Search_StartIndex = 0 _
AndAlso Direction = FindDirection.Down Then
Search_StartIndex = -1
First_Find = False
ElseIf Not First_Find _
AndAlso Search_StartIndex = 0 _
AndAlso Direction = FindDirection.Down Then
First_Find = False
Search_StartIndex = 0
End If
' Store the matches
Dim matches As System.Text.RegularExpressions.MatchCollection = _
System.Text.RegularExpressions.Regex.Matches([Control].Text, _
SearchText, _
IgnoreCase Or If(Direction = FindDirection.Up, _
System.Text.RegularExpressions.RegexOptions.RightToLeft, _
System.Text.RegularExpressions.RegexOptions.None))
If matches.Count = 0 Then First_Find = True : Exit Sub
' Restore Highlight colors of previous selection
[Control].SelectionBackColor = [Control].BackColor
[Control].SelectionColor = [Control].ForeColor
' Set next selection Highlight colors
If Highlight_BackColor = Nothing Then Highlight_BackColor = [Control].BackColor
If Highlight_ForeColor = Nothing Then Highlight_ForeColor = [Control].ForeColor
' Set the match selection
For Each match As System.Text.RegularExpressions.Match In matches
matchIndex += 1
Select Case Direction
Case FindDirection.Down
If match.Index > Search_StartIndex Then ' Select next match
[Control].Select(match.Index, match.Length)
Exit For
ElseIf match.Index <= Search_StartIndex _
AndAlso matchIndex = matches.Count Then ' Select first match
[Control].Select(matches.Item(0).Index, matches.Item(0).Length)
Exit For
End If
Case FindDirection.Up
If match.Index < Search_StartIndex Then ' Select previous match
[Control].Select(match.Index, match.Length)
Exit For
ElseIf match.Index >= Search_StartIndex _
AndAlso matchIndex = matches.Count Then ' Select last match
[Control].Select(matches.Item(0).Index, matches.Item(0).Length)
Exit For
End If
End Select
Next match
' Set the current selection BackColor
[Control].SelectionBackColor = Highlight_BackColor
' Set the current selection ForeColor
[Control].SelectionColor = Highlight_ForeColor
' Scroll to Caret/Cursor selection position
[Control].ScrollToCaret()
End Sub
#End Region
Also here you can see a video demostration: http://www.youtube.com/watch?v=mWRMdlC5DH8