Where can I find a list of events in Excel? I want to write macros in VBA based on them. I already know of Worksheet_BeforeDoubleClick, but I more or less just discovered that randomly/remembered it was used in Access.
Does anyone have a complete list or know where I can find one of the different events in Excel?
Here is the excel object model overview which you can use to navigate to the members of each model.
http://msdn.microsoft.com/en-us/library/wss56bz7(VS.80).aspx
You would use this to get to:
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.application_members.aspx
Scroll down for events.
These are some good links to learn about Excel's events:
Beginning VBA: Events
Event Macros, Worksheet Events and
Workbook Events
Events in Excel VBA
Another way to find them is open the VBE (Alt+F11), click on the object's class module (such as ThisWorkbook or Sheet1), and use the drop down boxes at the top of the code panel. If, for instance, you select ThisWorkbook from left drop down, the right drop down will contain all of the events available to you.
For objects that aren't Workbook or Worksheet (Application, QueryTable, etc), create a custom class module in your project (Insert - Class Module) and type (for example)
Public WithEvents qt As QueryTable
Now 'qt' will appear in the left drop down and all of the events for a QueryTable will appear in the right one. You'll notice that the Intellisense only shows a limited number of objects when you include WithEvents. These are the only objects that have exposed events. So you can't type
Public WithEvents rng As Range
because the Range object doesn't expose any events. A little more cumbersome than James' answer, but a nice way to browse the events when you know the object and to get a list of objects with exposed events.
Related
I have this project which main purpose is to modify and use an excel sheet. Everything is done through userform for the sake of user-friendliness. The sheet to be imported will be in a workbook sent by an external source, by different means (mail, retrieved from the network...) and will have a different name each time.
I'm looking for the most user friendly way to get the data from this sheet.
The most user friendly way I could figure, is to temporarily hide the userforms and ask the user to open the workbook from which the datas are needed. Then, to have the code copy the sheet I need among my main project sheets (if it passes a series of tests, to be certain it's the right one and not some other randomly opened workbook).
I was looking for a more intuitive solution, like drag'n drop the workbook on a userform of my project. I'm not certain it can be done, I was reading about the need of treeview, which according to some source doesn't support 64, and according to others works fine. I also read about a JPK replacement for treeview, but I'm not sure how this replacement can be considered as a safe option on the long term.
Any hints ? I'm looking for a solution without really knowing what to look for.
First of all, I would like to say that I am not the 'father' of the next solution. I have it in my collection of useful pieces of code and tips...
I do not remember from where I picked it up and adapted according to my needs.
Add a reference to Microsoft Windows Common Controls 6.0 ... This can be done from IDE -> Tools -> References...
Place a TreeViewcontrol on your form. Usually your ToolBox doesn't show it. So, after right click on the ToolBox, the option Additional Controls must be chosen. Then it is only necessary to check the Microsoft TreeView Control, version... and press OK.
Drag the new control on your form and press F4. In the property window which will show up, you must set 'OLEDropMode' as OLEDropManual. Press Enter, select the form and press Save.
Create a variable at the form module level (on top of it, at its declarations area). Not mandatory to declare it there, but it would be good to have it there for future use, if necessary. It happened I needed it there...
Private strExcelWPath As String
Double click on the TreeView control (default - TreeView1) and open, in this way its events code. Press the right top little down arrow and choose OLEDragDrop event. It will create the next event code:
Private Sub TreeView1_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
End Sub
You must insert the next code lines (or similar) and it becomes like that:
Private Sub TreeView1_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
strExcelWPath = Data.Files(1)
Debug.Print strExcelWPath 'just checking of what it returns
droppedWorkbookProcess strExcelWPath 'calling the sub able to process the workbook
End Sub
Your function able to process the dropped workbook path must look like this:
Sub droppedWorkbookProcess(strFullName As String)
'Here you place the processing code, using of passed strFullName parameter
End Sub
The code can be adapted for multiple selection, also, if necessary...
I have a workbook with some shapes that I don't want users to delete easily. They are linked with an index, and the index number is linked with a whole other bunch of stuff.
However, the user needs to move the shapes around and resize them.
How can I set the protection on the sheet to allow users to modify the shapes but not delete them?
The ability to do this in VBA would be useful too, as a few of my macros unprotect and then protect cells.
This is not possible.
You can lock all shapes in the workbook by using this code
Private Sub Workbook_Open()
ActiveSheet.Protect Password:="test", userinterfaceonly:=True
Dim shape As shape
For Each shape In ActiveSheet.Shapes
shape.Locked = True
Next
End Sub
or you build a plugin for Excel in dot.net.
This blog describes how you would do that for shapes and there you can use events to overide the delete.
create excel events for shapes
or
Microsoft excel shape events
This is indeed possible but with a slight workaround. No windows API. However, what are your shapes?
You can utilize ActiveX image controls and manipulate the MouseDown/MouseUp events to detect starting position and ending position via the left and top properties. Once calculating the difference in positions, you can set the image's new top/left properties to the appropriate numbers. You can even look into possible drag effects for the image so the cursor does not appearing totally still during a drag.
Alternatively, you can turn on that sheet's protection for editing objects which prevents moving/deleting innately. It still allows clicking, at which point you can assign a macro that reacts somewhat similarly to solution #1 but is more complex. Your macro can read the picture name by referencing the "application.caller" property. Each picture name can have different procedures associated with it. Read the current top and left properties of the shape. Continuing breaking the automation down into 2 steps. Programmatically unprotect the sheet, and monitor with a public boolean that an operation is in progress. Use the selection change event to monitor the next cell click at which point you can capture the subsequent top and left properties, eventually calculating where the shape will move. Programmatically protect worksheet again.
I am attempting to make a custom hotkey that will go to the previous sheet viewed.
As far as I understand, this would involve making a Class Module as a Worksheet object that will capture Sheet Change, Sheet Activate, and Sheet Deactivate Events. Once the event has been captured, I would set the previous active worksheet to an object variable. Then I would make a custom macro that would go to the previously set worksheet object once a hotkey is pressed. Is this the right path?
And if it is the right path, where would I save the Macro code so that it's usable by all future workbooks? I have a PERSONAL.xlsb file created and ready to edit. Would I make the class module in PERSONAL.xlsb? How would I initialize the object in PERSONAL.xlsb when I create a new workbook?
Answers to these questions would be greatly appreciated, thank you!
Generally on the right line, but:
you only need the Deactivate event to capture the last sheet
to code this for all workbooks, you would use an Application level event handler. See cPearson site
you may want to handle WorkbookDeactivate as well
I would create a class event handler (as described in the link) including a property for the last sheet, and a Module level Sub to get the LastSheet from the class, and activate it
I need a way to programatically launch the macro-recorder in Excel, and supply the name for the new macro that will get created.
This can be from VSTO or VBA, or using the Office interop assemblies.
Any ideas how this can be accomplished?
In VBA:
Dim ctrlStart As CommandBarControl, ctrlStop As CommandBarControl
Set ctrlStart = Application.CommandBars.FindControl(ID:=184)
Set ctrlStop = Application.CommandBars.FindControl(ID:=2186)
ctrlStart.Execute
'name part would go here, but first you have to deal with a modal dialog
ctrlStop.Execute
It looks like the Execute method on the RecordMacro control opens a modal dialog. There is no way to feed a parameter to this, or to do anything like SendKeys. The only way I see to do it is to write a sub that will rename the macro after the fact. It will be a little complicated to determine what the name of the new macro is, and you will still have a dialog box to deal with.
ActiveX combobox objects in Excel do not behave well when their ListFillRange refers to a formula-based Named Range (Defined Name).
I think I have encountered other errors and possibly even Excel crashes thanks to this, but right now all that happens is the combobox_change() event is triggered anytime ANY cell in the workbook is changed.
I am not sure if this is really a bug, or if there is a fix, or a workaround. If it is a bug, how do I report it to the Excel people?
And finally, the real meat of my question is "How do I work around this issue best?" I would like to have some formula-based named ranges, but it seems like this won't be possible.
To reproduce this bug, do the following:
Create a new workbook. On Sheet3, create a small table 3 columns across, and several rows high.
Create a named range with this formula (or an equivalent): =OFFSET(Sheet3!$A$2:$C$36,0,0,COUNTA(Sheet3!$A:$A),COUNTA(Sheet3!$4:$4)) To do this use Input>Name>Define. Name the range something like "demoRange"
Go to Sheet1 and create a combobox, (it must be on a separate sheet). (Use the Control Toolbox menu, not the Forms menu).
Click on the Design Mode button (the blue triangle with pencil), then right click on the combo box and go to Properties.
In the properties window for the combobox, change the ListFillRange property so that it points at the named range you created in step 2 ("demoRange").
You may want to change the ColumnCount property to 3, and the ColumnWidths property to "50,50,50"
Set the linkedCell property to cell "A1" by typing A1 in the linkedCell property.
Close the properties window, and double click on the combobox to define its change() event.
Put a Debug.Assert(false) or Msgbox("demo") line in the subroutine for the new combobox's change event.
Exit design mode
important - Now select an item in the combobox. The event should trigger normally the first time. (The bug will not show if you don't do this step--something must be selected in the combobox)
Edit cells anywhere in the workbook [Edit] or any other open workbook [/edit], on any sheet and any location. Each time you edit any cell, (at least for me), the onchange event for the combo box is run.
Again, is this normal, and what is the best alternative for what I am doing? This combo box gets linked to various cells, and is supposed to be a replacement for the tiny font in the data validation dropdowns excel provides by default.
My advice is to never use ListFillRange and LinkedCell. They are just trouble. Fill your listbox with List and use the Change event to write to the cell. Somewhere, maybe the Workbook_Open event, fill the listbox
Private Sub Workbook_Open()
Sheet2.ListBox1.Clear
Sheet2.ListBox1.List = Sheet1.Range("demoRange").Value
End Sub
Then in the change event in the Sheet2 module, check that something was clicked and write it to the cell
Private Sub ListBox1_Change()
If Me.ListBox1.ListIndex >= 0 Then
Sheet2.Range("A1").Value = Me.ListBox1.Value
End If
End Sub
I have a few options available that I am aware of thus far. The best I can come up with is this:
Avoid directly using formula-based named ranges. Instead, define a subroutine that will check whether the defined range "demoRange" should be changed from what its current value is. Run this subroutine on the workbook_open and sheet3_deactivate events. If needed, prompt the user to ask if it's all right to update the named range. [edit] The macro that updates "demoRange" could probably just copy from a "demoRange_FormulaBased" named range into "demoRange" which would be static. [/edit]
This solution works well because you can keep using the linkedcell property, you don't have to use VBA to populate the comboboxes, and the named range can still be used for whatever other purposes it already had. Avoid using the onchange event to run this new subroutine, since it might end up being triggered thousands of times if a user opens the Find/Replace dialog and chooses "Replace All".