I have a sub in VBA that calls another sub to perform some analysis on data if the user pastes it in. The code below errors as:
Run-time error'-2147467259 (80004005)':
Method 'List' of object'_CommandBarComboBox' failed
Private Sub Worksheet_Change(ByVal Target As Range)
Dim UndoList As String
UndoList = Application.CommandBars("Standard").Controls("&Undo").List(1) 'Errors Here!
If Left(UndoList, 5) = "Paste" Then
Dim Annotations() As String
FindAnnots
End If
End Sub
Any idea as to why the object's list doesn't exist?
After using Bryan's answer about it being a CommandBarComboBox, I think you can check its enabled status to see if a list exists. Its the only way I've got it to work without using On Error Resume Next.
Use the following loop to capture the status of the ComboBox
Dim UndoList As String
If Application.CommandBars("Standard").Controls("&Undo").Enabled = True Then
UndoList = Application.CommandBars("Standard").Controls("&Undo").List(1)
If Left(UndoList, 5) = "Paste" Then
'Code to run here after paste action'
End If
End If
Short Answer: The .List isn't there yet because the user hasn't performed any actions that get saved in the Undo queue. The code is trying to access something that hasn't been created yet. I had to create an error handler for it.
Long Answer: In my search, I found that the Controls object in VBA doesn't officially have a List property. However, Controls("&Undo") Is not a Controls object. It's a CommandBarComboBox.
So in
Application.CommandBars("Standard").Controls("&Undo").List(1)
The .List property of Controls("&Undo") doesn't actually show up in the Excel VBA intellisense. It's looking at the Controls object for it's intellisense drop-down. However, if you try
? TypeName(Application.CommandBars("Standard").Controls("&Undo"))
In the immediate window, you'll see that it's of the type CommandBarComboBox, which does have a list property. However, like other ComboBox style controls, no list is created until a list member is added. In this case, when the user performs an action worthy of being stored in the Undo queue.
Related
I am trying to access a form button...as a test. But when I do so like this; I get the error that it doesn't support this property or method? What is the proper way to go about accessing a button like this?
With ActiveSheet.Shapes("FH_btnHideShowCNC")
MsgBox .Caption
Exit Sub
End With
Because ActiveSheet isn't necessarily a Worksheet, that property wraps your sheet in an Object, and then you basically have to guess what the correct incantations are and cross your fingers that there aren't any typos (Option Explicit can't save you from late-bound code).
Restore early binding as soon as possible, don't chain member calls to an Object or Variant.
Dim sheet As Worksheet
Set sheet = ActiveSheet
With sheet.Shapes("FH_btnHideShowCNC")
'Shapes.Item(String) returns a Shape object, so we're early-bound now.
'...
End With
Now that the interface of the With block variable is known at compile-time, you'll get the list of available members, and the code will blow up at compile-time if you try to invoke a member that doesn't exist (like .Caption), rather than blow up at run-time.
Is this what you are trying?
With ActiveSheet.Shapes("FH_btnHideShowCNC")
MsgBox .TextFrame.Characters.Text
Exit Sub
End With
And MsgBox .Name will give you the name of the button which is FH_btnHideShowCNC
Excel allows to start with a Modeless form and then display a Modal (but not the other way around)
I have an app with 4 Userforms : UF1 - Select a partner, UF2 - List existing transactions, UF21 - Display an existing transaction, UF22 - Make a new transaction. UF21 and UF22 both stem from UF2.
UF21 needs to be Modeless in order to display more than one transactions and compare side by side, therefore UF1, UF2 and UF21 are all Modeless. But I want UF22 to be Modal in order to issue one new transaction at a time.
My problem is that after I close UF22, even just ESCaping from the form right off the bat, all previous forms close. I should be able to return to UF2. If I make UF22 Modeless all is ok.
I have written a function to traverse the UserForms Collection and I am able to get a reference to the object of the Form I want to activate. So, I am able to return (in debug mode) to UF2 which is a listbox, activate the list box, but after the last pending statement both UF2 and UF1 close.
Is what I am trying to do impossible due to the nature of the Modal and Modeless forms or should I keep pushing for the correct code?
Since my original question is still open and my tested implementation of the proposed solution by #PeterT is not working properly, I include the code I have for the moment, based on #PeterT 's suggestion.
'===============
' Form UF1
'===============
Private Sub UserForm_Activate()
If ActivateUF22(FormID) = True Then Exit Sub
'.... more commands
End Sub
'============
' Form UF2
'============
Private Sub UserForm_Activate()
If ActivateUF22(FormID) = True Then Exit Sub
'.... more commands
End Sub
'----------------
Private Sub Cbn_OpenUF22_Click()
If ActivateUF22() = True Then
Exit Sub
Else
With New UF22
.Show vbModeless
End With
End If
End Sub
'================
' In a Module...
'================
Public Function ActivateUF22() As Boolean
Dim frm As Object
Set frm = GetFormFromID("UF22*") ' Custom function to get a form Object based on
' some criterion (FormID in a hidden TextBox)
If Not frm Is Nothing Then
' the only way I know to *Activate* an already .Show(n) form and compensate
' for the fact that the Close CommandButton may already have Focus
frm.TBx_UF22_CODE.SetFocus
frm.CBn_UF22_CLOSE.SetFocus
ActivateUF22 = True
Else
ActivateUF22 = False
End If
End Function
Well I finally managed to get the workaround to behave.
The remaining problem was the fact that clicking twice in a row on the same userform, besides the "Modal" one, would succeed and allow the user to break out.
I even tried the "AppActivate Application.caption" approach found in another SO thread but that didn't work either.
The only solution that works and does not bother me is to insert a MsgBox with a warning to the user, as such:
Public Function ActivateUF22() As Boolean
Dim frm As Object
Set frm = GetFormFromID("UF22*") ' Custom function to get a form Object based on
' some criterion (FormID in a hidden TextBox)
If Not frm Is Nothing Then
' the only way I know to *Activate* an already .Show(n) form and compensate
' for the fact that the Close CommandButton may already have Focus
frm.TBx_UF22_CODE.SetFocus
frm.CBn_UF22_CLOSE.SetFocus
ActivateUF22 = True
MsgBox("You cannot move away from this form until it is either completed or cancelled")
Else
ActivateUF22 = False
End If
End Function
Displaying the MsgBox does the trick internally, switches the focus to a different form from the one clicked and, upon return, the UserForm.Activate event fires normally and the ActivateUF22 function prevents the user from escaping the Pseudo-Modal form.
Thanks #PeterT for pointing me to a workaround. I managed to do what I set out to do, albeit in a different manner.
PS I still believe that there is a way to switch from a Modeless form to a Modal one. After all the MsgBox I use is obviously a Modal form and works just as I would like ;-)
I have recently Written some code to take an input from a userform text box and search for it in my database. If found I would like it to return the value and insert it into cell A1149; I have written the below code but it gives me error #424 "Object Required". I am very new to VBA so any and all help is greatly appreciated.
Private Sub CMDSearch_Click()
Dim pesquisa As Range
Set pesquisa = Worksheets("Petrobras").Activate.Range("$W:$W") _
.Find(What:=Opp_Num_Search.Value, LookIn:=xlValues, Lookat:=xlWhole).Activate
Worksheets("Petrobras").Range(A1149).Value = pesquisa.Value
UserForm1.Hide
End Sub
Range.Activate doesn't return anything, it's like a Sub procedure:
Public Sub DoSomething()
' does something...
End Sub
If you did Set foo = DoSomething.Something, you'd get the same error: an object is required, otherwise that .Something member call is illegal (except now the error would be at compile-time and not run-time, because of how binding works).
Set pesquisa = Worksheets("Petrobras").Activate...
You don't want to Activate any sheets.
Part of the problem is the implicit late-binding going on: Worksheets returns an Object, so everything you wrote after that, all these chained member calls, can only be resolved at run-time.
Make it early-bound, by declaring an explicit Worksheet variable:
Dim sheet As Worksheet
Set sheet = Worksheets("Petrobras")
Now if you want to activate it, you can do sheet.Activate (but you don't need to). And if you want to get a Range from that worksheet, you can make a .Range member call, and the IDE will now help you do it:
Dim result As Range
Set result = sheet.Range("$W:$W").Find(...)
NEVER chain any member calls to what Range.Find returns. If the search turned up a result, you have a Range object. If it didn't, you have Nothing - and any member call made against Nothing will always raise run-time error 91.
Validate the search result first:
If result Is Nothing Then
MsgBox "Could not find '" & Opp_Num_Search.Value & "' in column W."
Exit Sub
End If
Or:
If Not result Is Nothing Then
sheet.Range("A1149").Value = result.Value
End If
Note that A1149 is a string literal representing a cell address, and as such it must be surrounded with double quotes ("). If it's not in double quotes and it looks like a valid variable name, VBA will treat it as ..a variable... and that will cause yet another error (1004), because Range will be rather unhappy to work with a Variant/Empty value.
To prevent VBA from "declaring" on-the-fly variables with a typo (and causing hard-to-find bugs), make sure you have Option Explicit at the very top of every module in your project.
One last thing:
UserForm1.Hide
This hides the default instance of UserForm1, which may or may not be the current object / instance that's shown - and the form itself has no way to know how it was shown:
UserForm1.Show '<~ shows the default instance
With New UserForm1
.Show '<~ shows a new (non-default) instance
End With
For this reason, you should avoid referring to the default instance in a form's code-behind. Use Me instead:
Me.Hide
That way you're referring to whatever instance of the form is currently shown, whether that's the default instance or not. See UserForm1.Show for more information, tips, pitfalls and common mistakes involving userforms.
Note that Rubberduck has quite a few inspections that can identify and warn you about (and often, automatically fix) most of these problems. Rubberduck is a free and open-source VBIDE add-in project I manage.
I have a tool with Sub that saves and send the workbook. That all works fine but I want to add the date from the datepicker on the front page of the tool to the subject of the email. For this, I need to get that date into a variable as a string.
I've looked up a few methods and pasted a few of them below but nothing is working. Both have been cribbed from other threads where the OP was happy with the result and worked for them, so I'm unsure why it's not working for me.
Private Sub testdateGet()
Worksheets("TEST").Range("A7").Value = DTPicker1.Value
End Sub
Public Sub dateGet()
Dim dateVal2 As String
dateVal2 = DTPicker1.Value
Worksheets("TEST").Range("A7").Value = dateVal2
End Sub
The first method results in an error 'Run-time error '424' Object required but I'm not sure what that means. The second method runs without error but doesn't paste anything in the designated cell.
Is there a specific place I have to put the code for it to work as intended? Would I be able to set a public variable for it to update, that can then be used in another module?
Error 424 is a tell-tale sign that you're not specifying Option Explicit, and so the DTPicker1 identifier is just an undefined Variant/Empty - and you can't make a member call against a Variant/Empty, an object is required.
Where is the DatePicker control? On the TEST sheet? Get the control from the sheet's Shapes collection:
Private Property Get DatePickerControl() As DTPicker
Dim oleObj As OLEObject
Set oleObj = Worksheets("TEST").Shapes("DTPicker1").OLEObject
Set DatePickerControl = oleObj.Object
End Property
Now you can access it from anywhere in the module:
Public Sub DateGet()
With Worksheets("TEST").Range("A7")
.Value = DatePickerControl.Value
.NumberFormat = "yyyy-mm-dd"
End With
End sub
Or, if the sheet exists in ThisWorkbook at compile-time, get it from the sheet directly:
Worksheets("TEST").Range("A7").Value = Sheet1.DTPicker1.Value
You can set the sheet's (Name) property (here "Sheet1") in the properties toolwindow (F4); that makes the name identifier accessible (from anywhere in the VBA project) to refer to that particular sheet.
Assuming you have put up a DatePicker similar as stated here.
Open the editor and open the worksheet code (if the datapicker is located on Sheet1 you should open that sheet) and select the datepicker from the object selection menu (it states (General) by default).
In the drop down menu top right you can select the different actions a user can perform on the datepicker. In this case select Click.
Now add some code
Private Sub DTPicker1_Click()
Foo = DTPicker1.Value
End Sub
Every time a click is made on the DatePicker it's value is passed to Foo. You can insert a Cell here or pass it as an argument to another function.
Can someone confirm if there is an event in VBA for when a textbox drawn from the insert menu in Excel 2010 has changed? I tried RelevantTextBoxName_Change()but the sub is not called even though the contents of the textbox has changed.
Its a "Shapes" textbox.
Thanks.
I suggest you use this implementation
The above library uses a VBA implementation of what #lbo suggested. It's essentially a way of creating 'fake events' from existing events which already capture the functionality you desire. In this case we use CommandBars_OnUpdate event (which appears to trigger whenever shapes are changed / deleted / selected / deselected / created. Then we filter down the on the current state of the excel workbook to try to understand what actually occurred. By detecting the selected object is or was a shape, we can adequately determine if this event is occurring on a shape or not.
Public WithEvents bars As commandBars
Public old_selection As Object
Private Sub InitialiseEvents()
Set bars = Application.commandBars
End Sub
Private Sub bars_OnUpdate()
'This will call on each user action, here we can check our shapes:
If DetectShape Then
'Shape selected and changed event:
If GetName(old_selection) = GetName(Selection) Then
Debug.Print "Shape Changed"
Else
Debug.Print "Shape Selected"
End If
End If
Set old_selection = Selection
End Sub
Private Function GetName(ByVal obj As Object) As String
On Error Resume Next
GetName = obj.Name
End Function
Private Function DetectShape() As Boolean
On Error GoTo endDetect
DetectShape = Selection.ShapeRange.Count > 0
endDetect:
End Function
Running InitialiseEvents() will print "Shape Selected" whenever a shape is selected and "Shape Changed" whenever a shape might have changed. Note I leave detecting whether the shape has indeed changed to you.
The excel object model doesn't have any events to control manipulations with shapes. You need visual studio to make it happen. See this:
Create new events for shape in Excel