I am creating a userform using Excel VBA that is meant to be used to register some sales. The form looks like this:
As you may have noticed, I am using an image as a button. This is because the CommandButton included in VBA looks very outdated. However, using an image as a button also creates me a new error (or not, depending on how you see it) that is driving me crazy. The usual process for filling this is entering a product, a quantity, a price, a customer and clicking the button to save all the information to a worksheet. The payment textbox is only filled sometimes.
I created a data validation mechanism for all these fields, including the customer combobox. If the user types an invalid entry or leaves the field empty after clicking it, a message box appears. The code for that is the following:
Private Sub cmbCustomer_AfterUpdate()
If cmbCustomer.ListIndex > -1 Then
Else
MsgBox "Please choose a valid customer", vbExclamation
cmbCustomer.Value = ""
End If
End Sub
This works great for avoiding invalid entries. The tricky part is that, once the button is clicked, all the fields are automatically erased. The data is correctly saved, but if the last field used before clicking was cmbCustomer (or any other, actually, because all of them have a similar mechanism to avoid empty or invalid data) and the user decides to begin filling the form again starting by the product, the message box appears, because it is empty and the code detects the invalid entry. I know this is the expected behavior for my code, but this doesn't happen if I use a traditional CommandButton because when clicking it the focus goes to said button. If I use my image-based button the focus remains on the last text field used before clicking it.
One solution would be to override the message box in this specific situation (when saving the data). The second one would be to reset the focus of the form, or set focus to the image like what happens with a regular CommandButton. Any suggestions would be greatly appreciated.
You can do yourself what the traditional CommandButton does automatically: Set the focus where you want it:
Private Sub cmbCustomer_AfterUpdate()
If cmbCustomer.ListIndex > -1 Then
Else
MsgBox "Please choose a valid customer", vbExclamation
cmbCustomer.Value = ""
myButton.SetFocus
End If
End Sub
If SetFocus doesn't work, be mindful of where you are in the UI event chain: Why is my .setfocus ignored?
As mentioned in the comments, an image button can't acquire the focus. A transparent CommandButton behind it can be used as a proxy.
Related
I really don't know what causes error 400.
Below code runs perfectly fine in normal mode but as soon as i enable my excel in sharing mode and tries to user form, it gives me VBA 400.
What i am trying to do here is to change shape's text and disable its OnAction event, once user form is shown to user. so that another user accessing same file will come to know that someone is using "User Form" to enter data.
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
If shp.TextEffect.Text = "Sort Customer" Then
shp.OnAction = ""
shp.TextEffect.Text = "Wait!!!"
End If
Next
Q. Is there any way to publish changes made by any user in shared excel automatically.
I suspect that your code falls in one of the numerous limitations of Excel shared mode, described here (see unsupported features), including
Using a data form to add new data
Using drawing tools
Inserting or changing pictures or other objects
(Please note that, due to its format, I could not easily copy that list of unsupported features in my answer.)
As far as I know, in order to keep the changes you should choose if the first one who introduces the data rules or you will choose in case of conflict. As you are looking for an "automatic" way, you should chose the first one.
You can find a good explanation described here
At Review > Share Workbook , Advanced Tab. At "Conflicting changes between users", you should chose "The changes being saved win". So as the data are introduced and saved, they are reflected.
Hope it helps.
Create a vba function in the sheet (NOT A MODULE) where users can activate the user form:
insert the following function there:
Function HyperlinkClick()
'source: https://stackoverflow.com/a/33114213/11971785
Set HyperlinkClick = Range("B2")
If HyperlinkClick.Value = "Sort Customer" Then
'sets info on WAIT
HyperlinkClick.Value = "WAIT!!!"
'shows userform
UserForm1.Show
Else
'sets info back to normal value
HyperlinkClick.Value = "Sort Customer"
End If
End Function
In the user form you can add an userform_terminate Event, which automatically changes the value in B2 back (I guess you could also do that for an workbook Close Event be on the safe side).
Private Sub userform_terminate()
'Code goes here
Range("B2").Value = "Sort Customer"
End Sub
In Excel now create a "Frontend" such as this:
and add the formula:
=HYPERLINK("#HyperlinkClick()";"Click")
to the cell where a user needs to click to open the UserForm (in this case to D2).
If you now share the workbook and click on "Click" in D2 an Event is triggered and the VBA Function "HyperlinkClick()" is called. In this function you can essentially do anything now.
Explaination:
Instead of using a graphic, button etc. which will not work correctly in shared mode, we can simply use links (which work) to trigger an Event.
Instead of "creating" and "deleting" Hyperlinks (which also does not work in shared mode) we simply build dynamic links which Point to userform.show or to nothing, depending of the situation.
Error 400 Problem: Should be solved by skipping the modify object part of the code.
Multiple User Problem: Should be solved, since only one user can activate the userform.
Is there any way to publish changes made by any user in shared excel automatically.: I guess so, please provide more information on what exactly you want to achive (incl. example).
Tip:
In General you might want to check out MS Access since it has as default feature multi-user Access and users there can use the same form at the same time, since the users only get exclusive Access for specific datapoints not the whole table/workbook or file.
Apologies for not showing my code but after much trial and error I'm thinking the issue I describe could possibly be a textbox property issue rather than coding error or ommission. The code itself works as it should but there is a phenomenom which frustratingly persists.
On a wsheet amongst a number of ActiveX controls I have a textbox and 2 images. They are used for a search function. As you would expect the textbox is for user entry and the images are for 'run search' and 'erase search'. I set the search text as a string.
My issue is when hitting either 'run search' OR 'erase search' the textbox momentarily shows the previous text string. I have set this previous string to "" all over but without success.
This is best observed when setting a search text which will knowingly fail.
The sequence is...
1) Enter 'XXXX' to search
2) Hit 'run search'
3) Search code executes
4) Prior to textbox narrative returning "XXXX not found" it momentarily shows the previous entry, say "AAAA", before returning the correct result.
How can this be prevented?
EDIT
With no response I posted this on Jon Peltier's site at https://peltiertech.com/forms-controls-and-activex-controls-in-excel/#comment-1481602
Kindly he tested and concluded "It looks like an ActiveX thing, and I guess you’re stuck with it."
From tests this phenomenon occurs even when selecting any cell, not only the image controls. In other words it is triggered as soon as the textbox loses focus. Arguably, because it is a momentary change it does not seem possible to trap the text.
I was experiencing a similar problem and raised a question myself, but managed to have more luck in getting some responses, see:
After setting ActiveX textbox to empty value, previous text briefly appears in box before disappearing again
Through the help of the responders I was able to create a work around using Application.SendKeys. Possibly one of the answers might be able to help you in getting around this issue.
For me personally Application.SendKeys was required as this overwrote the value in the text box and refreshed it meaning the previous value was no longer present. For completeness here is a snippet of the code I used:
'Select text box to update
Sheet1.userName.Activate
DoEvents
'Replace value with ""
Sheet1.userName.Application.SendKeys ("")
'Copy above for other textboxes on sheet
Sheet1.emailAddr.Activate
DoEvents
Sheet1.emailAddr.Application.SendKeys ("")
'Change text box currently active to force change values to take place
Sheet1.userName.Activate
I am selecting my text boxes one at a time then using SendKeys("") as a way to emulate the user deleting the current field in the box, after that I switch back to another text box as this is needed to force the refresh of the value.
Being able to force the text boxes to be blank after I was done with the values meant that there were no previous values that could inadvertently appear later on.
Thank you Skenworthy for your suggestion. Over time I kept returning to my issue but without success however your code has formed the basis of a solution.
I only had 1 textbox used as a search input plus 2 buttons, 1 to begin search and the other to clear.
My solution was to create a hidden dummy textbox and each time I wished to make a comment in the search textbox I would use sendkeys to the dummy box. However that was not the final code. Switching focus between the 3 controls became too complex given the type of potential user errors that could be made. So I abandoned the Begin search button relying instead on the Enterkey and the Clear search button.
Now each time I need to return a response message I use the code below immediately prior to clear the search textbox. It has consistently worked without flaw. So, a long time coming, my specific solution seems so simple in hindsight but thank you again for the pointer.
Sub RemovePrevText()
''' use dummy txtbox to clear old message from SearchInput
With Sheets(1)
.txtDummy.Activate
DoEvents
.txtDummy.Application.SendKeys ("")
End With
End Sub
I'm creating a system that allows an individual to select module choices for their degree.
The users have to navigate between multiple forms (e.g. semester 1 & 2, user info, confirmation pages etc.). I want the users to be able to return to a previous form to make changes after progressing (i.e. return to semester 1 choices after moving onto semester 2 choices) and be able to still edit all the data they inputted into the first form.
I have tried using the hide and show methods but I keep getting an error (Run Time error '400' - Form already displayed, cannot show modally)
'on the AM1 form
AM2.Show
AM1.Hide
'(first form I want to close)
'on the AM2 form
Unload me
AM1.Show
'(I want to return to the first form and close the second)
I want to be hide the first form (AM1) and keep all the info available to be re-edited when successfully returning to it.
Try hiding AM1 first before showing AM2 and that should fix your issue.
Private Sub CommandButton1_Click()
UserForm1.Hide
UserForm2.Show
End Sub
Also I think using the multipage control is probably better than using multiple userforms.
I can't get the RowSource property of a list box to update via VBA. From another thread, I found the syntax, so I think this is correct. But, despite not failing, it doesn't do anything to the RowSource property (it remains blank). Below:
frmAddIngredient is the user form.
lbxIngredient is a listbox control in that form.
UniqueIngredients is one of the sheets in the workbook.
NumberOfItems is 1 (in this case).
It doesn't give an error, but it doesn't change anything, either. The form itself is not active at this time. This code is supposed to set up the form for later showing.
frmAddIngredient.lbxIngredient.RowSource = Sheets("UniqueIngredients").Range("A1:A" & CStr(NumberOfItems)).Address
The most recent code is
frmAddIngredient.lbxIngredient.RowSource = "=UniqueIngredients!A1:A1"
but, it still doesn't change anything in the actual form.
Also, can I add a new post, or do I have to continue editing this one and adding stuff?
What you want (as discussed in the comments on your question) is not possible. Setting something by code does not change it's property in the properties window and is only so until your project resets.
Consider a Userform with 2 buttons, with their original name and caption and then in a module paste these 2 subs.
Sub demo1()
UserForm1.CommandButton1.Caption = "Demo 1"
UserForm1.Show
End Sub
Sub demo2()
UserForm1.CommandButton2.Caption = "Demo 2"
UserForm1.Show
End Sub
When you run the first Sub demo1 Button 1's caption has changed but Button 2's caption has not.
Close the Userfom and now run demo2, you'll see that Button 1's caption is back to it's original hard set (properties window) name and that now Button 2 has a different name.
I have been playing around with form designs and now I have constructed a form, which is almost complete, just one problem left. Let me explain first:
Form is bound to a "Join Table", which has only 2 fields - ID from "Table1" and ID from "Table2". Based on those two fields I have added fields from "Table1" & "Table2", on same form. Then I have added 2 option buttons, 1 Combobox and 2 Subforms.
This allows me to watch records from two different tables that are joined, from each point of view - "Table1" or "Table2" view. I am selecting this views with Option buttons, which changes Combobox rowsource, so you can navigate to records from Combobox.
Here is code :
Private Sub OptButton0_Click()
If Me.OptButtonO.Value = True Then
Me.OptButton1.Value = False
Me.Cmbox.RowSource = "SELECT [Table1].[Field1], [Table1].[Field2], [Table1].[Field3] FROM Table1 ORDER BY [Field1];"
Me.Cmbox.SetFocus
Me.Cmbox = Me.Cmbox.ItemData(0)
End If
End Sub
Private Sub Cmbox_AfterUpdate()
If Me.OptButton0.Value = True Then
If IsNull(Me!Cmbox) Then Exit Sub
With Me.RecordsetClone
.FindFirst "[Field1] = " & Me!Cmbox
If Not .NoMatch Then
If Me.Dirty Then Me.Dirty = False
Me.MySubform.Width = 8280
Me.MySubform.SourceObject = "MySubform"
Me.Bookmark = .Bookmark
Else
Me.MySubform.Width = 8000
Me.MySubform.SourceObject = ""
End If
End With
Me.Cmbox.SetFocus
DoCmd.Requery
End If
End Sub
This posted code is only for one Option button, second one is same, just opposite. Now what is problem ?
Problem is that when I navigate through record via Combobox, click second Option button for another view AND THEN RETURN to same view, my subform results stays same as they were when I clicked another Option button, although Combobox listIndex is 0. If I select combobox Listindex from Combobox, code works again.
SO BASICALLY - I NEED CODE THAT WILL TRIGGER COMBOBOX CHANGE WHEN OPTION BUTTONS ARE CLICKED. It works when you're clicking in Combobox, but not when clicking in Option button.
I know It's complicated to understand, please take a look at code, and ask anything. Any help appreciated.
Another option: I had a similar issue: with an unbound combobox. In the '*_Change' event in a combobox, if I pick a value from the dropdown, the value is there; but if I delete the existing value, the prior value still shows up.
...
If Me.series_filter > "" Then
lstrMetric = lstrMetric & "and X.series_name = '" & Me.series_filter & "' "
End If
...
Me.Filter = Mid(lstrMetric, 5)
...
I have a dropdown for a filter on the form: picking a value is meant to set (or clear) the filter. It worked when I picked a value, but would not clear if I delete it.
I added a line to the start of the code to commit updates:
Me.dirty = false
The code now recognizes the null value when the combobox is cleared. This works in my case - of course it would be a problem if you didn't want any updated fields written to the database.
call the the combobox afterupdate event in the option button click event:
private sub optbutton0_click()
...
cmbox_afterupdate()
end sub
PS: Rather than having events for option buttons directly, you should put them in a frame, (even if you then have to make the frame transparent to stop it from appearing) and use the afterupdate or click events of the frame, whereby you can get the selected option button by option value:
private sub frame0_click()
select case frame0
case 0 'option button 0 is selected
...
case 1
...
end select
end sub
In Access, controls can be funny. I mean, when you change the option, you see it change, it changes on the screen... but the control doesn't always change its .value right away... meaning it doesn't Update. For a simpler illustration of this principle, create a textbox, type some stuff into it, and look at the textbox.value in the immediate window. Then, with the cursor still in the textbox, backspace some of it. Check the value again. Do that a few more times. Start including textbox.text in your tests. There are multiple ways around this, but it is most important to understand when controls actually update, so you can understand better when and which work arounds to use.
Now, in this situation, you typed 'click' every time when you referenced selecting an option. I think I will hold you to that. Personally, I use Tab and the arrow keys sometimes, which would be a slightly more complicated answer, but one you will be better equipped to solve yourself, after understanding the textbox example above. But, if you change AfterUpdate above to Click, you should be good.
That said, there are events besides mouse clicks that can activate the Click event. Usually not a problem, but since you are changing the look of your form, specifically the width, you may want to be aware that the subform width may flash back and forth sometimes while using your tool. Not often, probably won't be too much of an annoyance, but I might rethink why I needed to change width at all, or if there might be better triggers for it.