Excel VBA: How to trigger an Event from code? - vba

I have a Worksheet_BeforeDoubleClick event that opens a listbox in the target cell. I was asked to provide the same functionality via a button instead of (or in addition to) the double-click.
In the button's Click event I entered:
Call Worksheet_BeforeDoubleClick(Selection,true)
...so the button simply "doubleclicks" the cell. It seems to work well, but before I start using this technique throughout my project, I'd like to know if there are pitfalls I should be aware of.
What are the best practices when calling an event, either from another event or from a standard code module?

I'd like to know if there are pitfalls I should be aware of.
Yes there is one major pitfall. The Selection necessarily might not be a range. See this example
Insert a button
Insert a blank chart
Insert an image in the chart. Let the image be highlighted
Let's say we have this code
Private Sub CommandButton1_Click()
Call Worksheet_BeforeDoubleClick(Selection, True)
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
MsgBox Target.Address
End Sub
Now press the button and you will get an error.
The worksheet events WILL fire when they NEED too... They won't when the selection is not appropriate.
Having said that you CAN make your command button code work like this
Private Sub CommandButton1_Click()
'~~> Check if what the user selected is a valid range
If TypeName(Selection) = "Range" Then
Call Worksheet_BeforeDoubleClick(Selection, True)
Else
MsgBox "Not a Valid Range"
End If
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
MsgBox Target.Address
End Sub
But then where all and what all CHECKS will you place :) The best way is to place it in a Sub as #Sam suggested and then call it either from Button/Worksheet_BeforeDoubleClick.
If you still want to call it from a button then ensure that all relevant checks are in place including a proper error handler.

Related

Display MsgBox on cell click - even when the cell is already selected

I want a MsgBox to appear when cell A1 is clicked. I want it to appear even if A1 is already active when it is clicked:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.EnableEvents = False
If Target.Row = 1 And Target.Column = 1 Then
MsgBox ("message")
End If
Application.EnableEvents = True
End Sub
This code works only if cell A1 is not already selected when I click on it. Currently the message box does not appear in this case.
Is there a way to fix this?
Your code is using Worksheet_SelectionChange which only fires when a different cell is selected (hence the name Selection Change).
Alternatively, if it's okay if your [unknown] goal is attained using double click or right click then there are other worksheet events that will help:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
MsgBox Target.Address & " was double clicked"
Cancel = True 'don't edit cell
End Sub
 
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
MsgBox Target.Address & " was right clicked"
Cancel = True 'don't open context menu
End Sub
Note that the code for these event procedures need to be placed in the worksheet module.
Edit: More creative ways
Click Event via PeekMessage API
If it must be a single click, there are "sneakier" ways to accomplish this, such as adding a Click event. This is not a built-in feature of Excel VBA, and thus, this method is not generally recommended.
It involves checking for the WM_MOUSEMOVE message when a cell is mouse-clicked, which is accomplished by calling the PeekMessage API inside the Worksheet_SelectionChange event. More info and examples here.
Transparent command button
There could also be a round-about way to accomplish this using an ActiveX Command Button with no caption, with the BackStyle property set to frmBackStyleTransparent.
Neither of these methods have been tested and you might need to do some fancy coding to get them to work. Depending on how often the same cell will be clicked that is already selected (and therefore how that functionality is to you), you may want to simply re-think the layout of your worksheet.
For example, you could add an extra column and have the user click the cell next to the one with the value to activate your message box.
More Information:
MSDN : Worksheet.BeforeDoubleClick Event (Excel)
MSDN : Worksheet.BeforeRightClick Event (Excel)
Chip Pearson : Events in Excel VBA

Private Sub App_SheetChange Doesnt work if an error occurs in a previous macro

I have the following code to activate a macro when a change is made to cell A1
Class Module
Option Explicit
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Name = "S" Then
Dim rngKeyCells As Range
Set rngKeyCells = Sh.Range("A1")
If Intersect(rngKeyCells, Target) Is Nothing Then
Exit Sub
End If
Application.Run "a"
End If
End Sub
This_Workbook Code
Private OurEventHandler As EventHandler
Private Sub Workbook_Open()
'Initiates the data change when the filter is changed
Set OurEventHandler = New EventHandler
End Sub
This works absolutely fine usually, however an issue occurs if i try making a change in A1 after i open VBA.
It will work fine 90% of the time but if during one of the previous macro's that i run, there is an error, it won't work.
Example - I run a macro that deletes the Worksheet to the left of the active one. If there is no worksheet to the left of the active one it will error. I press end and that's fine. Now if i try to change the cells A1 and expect the macro above to run, nothing happens.
Is this the kind of thing that is solvable without showing the entire macro? Or could it be something that is inbuilt into the rest of the macro that is causing the issue?
Thanks
In the programming there is something named Design Patterns. In your case, it would be really useful to make a Singleton for the App variable.
Here is a good example of it for VBA:
How to create common/shared instance in vba
As already mentioned in the comments: When an error happens and end is pressed, the whole VBA-Context is reset, the content of global Vars is lost (so your variable OurEventHandler is nothing).
If you can't catch all errors to ensure that this reset isn't happening (and I think you never really can), maybe it is easiest to implement the event handler in the worksheet itself:
Private Sub Worksheet_Change(ByVal Target As Range)
' Ne need to check Worksheet as the Hander is valid only for the Worksheet
if target.row = 1 and target.column = 1 then
Application.Run "AIMS_and_eFEAS_Report.AIMS_Criteria"
end if
End Sub

What's the best practice for getting a value from a combobox on a userform?

There's a lot of information and a lot that you can do with userforms but I can't really find a standard way to use them. Let's say I have a userform with a standard dropdown list that asks someone to choose a fruit. In the userform code I will put the below code after adding a combo box called fruitcombo:
Private Sub UserForm_Initialize()
userform1.fruitcombo.AddItem "Peach"
userform1.fruitcombo.AddItem "Pear"
userform1.fruitcombo.AddItem "Grape"
End Sub
I will also add a commandbutton which will be labeled "Submit" and in that event:
Private Sub Submit_Click()
Me.Hide
End Sub
That's where it starts to get hazy. What's the best way to capture the answer that was selected? One way I can think would be to make a global variable called fruitanswer and then instead of the Me.Hide we can skip straight to Unload Me
Ex:
Private Sub Submit_Click()
fruitanswer = fruitcombo.value
Unload Me
End Sub
Or we can have fruitanswer as a private variable in the module where the userform is called and then unload it in there. There are also multiple ways to initialize the userform. I'm also wondering the best way to initialize it. The Show method will automatically initialize it, but the Hide method WON'T automatically de-initialize it. For that, the Unload statement is necessary. So does anyone initialize it before calling the Show method using the Load statement?
Ex:
Load userform1 'Any point to including this?
userform1.show
'user chooses a fruit and clicks submit button
'userform is hidden by commandbutton but not unloaded yet
fruitanswer = fruitcombo.value
Unload userform1
Out of these options, which is the best method? Is there anything to make it more efficient?
You can wrap the whole thing in a single function call:
strFruit = UserForm1.GetFruit()
Then, in your UserForm, have it do the work of displaying and unloading itself like so:
Private bOK As Boolean
Public Function GetFruit() As String
bOK = False
Me.Show vbModal
If bOK Then GetFruit = ComboBox1.Text
Unload Me
End Function
Private Sub cmdOK_Click()
bOK = True
Me.Hide
End Sub
Private Sub cmdCancel_Click()
Me.Hide
End Sub
This assumes you have buttons named cmdOK and cmdCancel and a combobox named ComboBox1.
Since Show() is being called modally, the code after it won't execute until the form is closed or hidden. When either button is clicked (or the form is closed by other means) then the code continues, the selected text is returned (if OK was clicked), and the form unloads itself.
The beauty of doing it this way is that your calling code doesn't need to worry about instantiating and destroying the form each time it's called. It's just a single statement to load the form and get the return value.
Of course, you'll need to add the code to populate the combobox with whatever items you wish to display.

VBA - leave current cell

I have some code that reacts to a double click. This action works just fine, my question though.. after filling in the form that pops up due to the double click action the form is unloaded, no problem here, but the user is left in edit mode inside the cell he double clicked on.
Now normally in Excel you can press CTRL-Enter and the cell will just simply be selected and you out of edit mode. How do I achieve this in VBA ?
All I can seem to work is moving the cell up one, but really just want the user to exit Edit mode and stay on the current cell, the one he double clicked on.
Private Sub Cancel_Click()
Cells(ActiveCell.Column, ActiveCell.Row - 1).Activate
Cells(ActiveCell.Column, ActiveCell.Row).Activate
Unload Term
End Sub
Any suggestions ?
Use the BeforeDoubleClick's Cancel argument, which ... cancels clicking into the cell:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
UserForm1.Show
End Sub

How do i perform the desired operation If one of the command button is OK and the other is CANCEL

Can anyone tell me How do i do certain task based on the command button options.
I have a Userform where user submits his data and it has 2 command buttons one is OK and the other is CANCEL. I have to exit when user clicks CANCEL and continue the Process when user clicks ok
file_name=userform1.textbox1.value
This is how we can get the data of the text box into our functions but what is the way to know which command button key is pressed ? Becuase if i press OK or CANCEL the operation is still being performed. I have tried like these
value=userform1.commandbutton1.value
value2=userform1.commandbutton2.value
not working. I even tried these
commandbutton1_click() \\ this is the OK command button
call main
end sub
commandbutton2_click() \\ this is the CANCEL command button
end sub
I have tried To call the main sub_routine from the OK sub routine But it says main routine not found becuase I have written these codes in the thisworkbook.and the commandbutton1_click routine is inside the Userform1 module
SO how do i make it work? I hope you understood where I'm Stucking up.Thank you in advance
Private Sub CommandButton1_Click() 'should be called cmdOk
main
End Sub
Private Sub CommandButton2_Click() 'should be called something like cmdCancel
Unload Me
End Sub
Private Sub main()
'DO PROCESSING
End Sub
Do you call main on Form_Load() or Form_Activate() or anything like this? When clicking Cancel, the above unloads the form. When clicking Ok, it starts the processing. There is not more to it.
If you want to keep the main method outside of your userform, you can try something like this, based on the example given by #cularis
' This way you can keep your code in ThisWorkbook
' I would recommend creating a new module.
' You can name your module whatever you'd like.
Private Sub CommandButton1_Click()
ThisWorkbook.main
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub