I have excel Project that includes pictures. I have userform that has ImageBox. This form shows pictures dynamically according to row number with using selection change event.
This event triggered when cell selection change. But i want to triggered this event by clicking on shape. Is there any solution for that?
Please see image.
These are codes for cell selection change event.
Private Sub App_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
Application.ScreenUpdating = False
Dim Pic As Shape
For Each Pic In ActiveSheet.Shapes
If Pic.TopLeftCell.Row = ActiveCell.Row Then
If Pic.Type = msoPicture Then
Pic.Select
Dim sheetName As String, MyPicture As String, strPicPath As String
sheetName = ActiveSheet.Name
MyPicture = Selection.Name
strPicPath = SavedPictureTo(MyPicture)' This function save image
Load ImageViever 'ImageViewer is UserForm name
With ImageViever
.Image1.Picture = LoadPicture(strPicPath)
.Show vbModeless
End With
Exit For
End If
End If
Next
Application.ScreenUpdating = True
End Sub
As written in the comments, you can assign a (parameterless) Sub to a shape that is executed when a shape is clicked.
In Excel, you can assign the macro by right-clicking on the shape and select "Assign macro".
With VBA, you write the name of the macro to the OnAction-property of the shape:
Dim sh As Shape
For Each sh In ActiveSheet.Shapes ' <-- Select the sheet(s) you need
sh.OnAction = "HelloWorld" ' <-- Change this to the name of your event procedure
Next
If you want to know which shape was clicked, you can use the Application.Caller property. When the macro was called by clicking on a shape, it contains the name of that shape.
Sub helloWorld()
Dim sh As Shape
On Error Resume Next
Set sh = ActiveSheet.Shapes(Application.Caller)
On Error GoTo 0
If sh Is Nothing Then
MsgBox "I was not called by clicking on a shape"
Else
MsgBox "Someone clicked on " & sh.Name
End If
End Sub
Related
I load a userform via a double-click event executed on a range of cells. Once any of the cells in the range gets double clicked, my userform is loaded.
I would like the input boxes of the userform populated with data that is based on an offset of the triggering cell.
I am struggling with capturing the address of the cell that triggered the event, and consequently would need to figure out how to offset from that cell's column and obtain the relevant value for population in the userform.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Sheets("Daily Summary").Range("D27:D93")) Is Nothing Then
Cancel = True
CommentDetails.Show
End If
End Sub
a) How do I capture the dynamic cell address that triggered the userform load?
b) How do I offset three columns to the right, capture that cell's value and load it into the userform's input field named first_name?
Thanks to #Zwenn in the comments for pointing me in the right direction with Application.Caller. Updated code below, it executes but shows a Object Required error.
The name of the form is CommentDetails, the name of the input field is TextBoxArrival, both of which matches the code.
Private Sub Userform_initialize()
Me.TextBoxArrival.Value = Cells(Application.Caller.Row, Application.Caller.Column + 1)
'TextBoxArrival.Value = Cells(Application.Caller.Row, Application.Caller.Column + 1)
'MsgBox Cells(Application.Caller.Row, Application.Caller.Column + 1).Value, vbOKOnly
End Sub
I understand I have to declare application.caller along with the calling method, which in my case is Sub Worksheet_BeforeDoubleClick. Still getting the same error. I tried circumventing this by calling another separate sub before loading the userform.
Where do I define application.caller?
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim callingCellRow As Integer
If Not Application.Intersect(Target, Sheets("Daily Summary").Range("D27:D29")) Is Nothing Then
Select Case TypeName(Application.Caller)
Case "Range"
callingCellRow = Application.Caller.Row
callingCellColumn = Application.Caller.Column
Case "String"
callingCellRow = Application.Caller.Row
callingCellColumn = Application.Caller.Column
callingCellSheet = Application.Caller
Case "Error"
MySheet = "Error"
Case Else
MySheet = "unknown"
End Select
With CommentDetails
.Tag = callingCellRow '<~~ tell the UserForm there's something to bring in so that it'll fill controls from the sheet instead of initializing them
.Show
.Tag = ""
End With
Unload CommentDetails
End If
End Sub
There's 3 ways to do this explained on Daily Dose of Excel by Dick Kusleika (18 years ago!). I prefer the 3rd option as it handles the form instance with a variable.
In Worksheet_BeforeDoubleClick you can have this:
Option Explicit
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim objForm As UserForm1
If Not Application.Intersect(Target, Me.Range("B2:D2")) Is Nothing Then
Cancel = True
Set objForm = New UserForm1 ' <-- use a variable for the form instance
Set objForm.rngDoubleClicked = Target ' <-- set property of the form here with Target
objForm.Show
End If
End Sub
And then in the form code:
Option Explicit
Private m_rngDoubleClicked As Range
' set only
Public Property Set rngDoubleClicked(rng As Range)
Set m_rngDoubleClicked = rng
End Property
' use the property
Private Sub UserForm_Activate()
Dim strAddress As String
Dim rngOffset As Range
' m_rngDoubleClicked is now the range that was double clicked
strAddress = m_rngDoubleClicked.Parent.Name & "!" & m_rngDoubleClicked.Address
Set rngOffset = m_rngDoubleClicked.Offset(3, 0)
Me.TextBox1.Text = "The address of the double clicked cell is " & strAddress
Me.TextBox2.Text = "The value 3 rows down from double clicked cell is " & rngOffset.Text
End Sub
Private Sub UserForm_Initialize()
' no args for initialization unfortunately :(
End Sub
Example:
I have an Excel that functions as a sort of decision tool, where questions are being asked and one needs to navigate through the workbook with buttons.
I have made a macro to function as a "go back" button, which activates the previously active sheet and hides the one you are on now. It works, but it keeps showing the navigation buttons from the first sheet. The text from the correct sheet does appear. If I go to another sheet and back, the data appears correct.
Is there a way to refresh the sheet so the correct information shows up, or is this a problem to do with the buttons and the macros behind them?
The macro I have used for the go back button:
(in workbook):
Sub Workbook_SheetDeactivate(ByVal sh As Object)
LastSheet = sh.Name
End Sub
(in module):
Global LastSheet As String
Sub goback()
Sheets(LastSheet).Visible = True
Sheets(LastSheet).Activate
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
With ws
If .Name <> "BM" Then
.Range("H9:R31").Font.Color = RGB(255, 255, 255)
End If
End With
Next ws
Sheets(LastSheet).Visible = False
End Sub
Even though there are no calculations, I did try including ActiveSheet.EnableCalculation but that did nothing.
Any help would be greatly appreciated.
Maybe I am reading this incorrectly but is the issue that the buttons are not dissapearing? if so this may help:
Sub hidebutton()
Dim sh As Shape
For Each sh In Sheet1.Shapes
If sh.Name = "Button 1" Then
sh.Visible = True
End If
Next
End Sub
What I am trying to do is to create a program that adds combo boxes with some options. These options should then, depending on the option selected, change some values in some cells that I specify in code.
This is how I make the combo lists:
Private Sub Workbook_Open()
With Worksheets("Sheet1").Columns("E")
.ColumnWidth = 25
End With
For i = 1 To 6
Set curCombo = Sheet1.Shapes.AddFormControl(xlDropDown, Left:=Cells(i, 5).Left, Top:=Cells(i, 5).Top, Width:=100, Height:=15)
With curCombo
.ControlFormat.DropDownLines = 3
.ControlFormat.AddItem "Completed", 1
.ControlFormat.AddItem "In Progress", 2
.ControlFormat.AddItem "To be done", 3
.Name = "myCombo" & CStr(i)
.OnAction = "myCombo_Change"
End With
Next i
End Sub
I want each of the dropdown values trigger the event myCombo_Change and then simply change the cell "D" For example, combo box 3 is located at E3 and I want the "To be done" to clear the cell D3 and the completed to simply store the date (and time) to the cell D3. This should be done for all combo boxes in the E Column.
Private Sub myCombo_Change(index As Integer)
Me.Range("D" & CStr(index)) = Me.myCombo.Value
End Sub
This is the code I started thinking about, but I have no idea how to call the event with an integer as the index parameter NOR how to access the cell using said index.
The effect I want is something along the lines of this:
Use Application.Caller to get the name of the control that called the myCombo_Change event.
Sub myCombo_Change()
Dim curCombo As Shape
Set curCombo = ActiveSheet.Shapes(Application.Caller)
curCombo.TopLeftCell.Offset(0, -1) = Now
End Sub
Assign the myCombo_Change to all existing DropDown:
Sub AssignMacroToAllListBoxes()
Dim sh As Shape
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
For Each sh In ws.Shapes
If TypeName(sh.OLEFormat.Object) = "DropDown" Then
sh.OLEFormat.Object.OnAction = "myCombo_Change"
End If
Next
Next
End Sub
Delete all DropDowns on Sheet1
Sub DeleteAllDropDownsOnSheet()
For Each sh In Sheet1.Shapes
If TypeName(sh.OLEFormat.Object) = "DropDown" Then
sh.Delete
End If
Next
End Sub
I have a file that someone made and I was tasked with simply adding an autoupdater function that updates the cell next to the dropdown menu.
The way the dropdown menu is created is by going to data validation and selecting list and make list in cell. The values are read from elsewhere.
Now, what I tried was to loop over all shapes like this:
Dim dd As DropDown
Dim i As Integer
Debug.Print Sheet1.DropDowns.Count
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim s As Shape
For Each s In ws.Shapes
Debug.Print CStr(s.Type)
Next
Next
End Sub
This prints the following:4 is a comment, 8 is a control form
444444444444444444444444444
8
So even though I have many drop down menus none come out when I loop over them.
I wanted to make it so that anyone can add a dropdown box and my code would attach an OnAction Sub that fills in the cell next to the dropdown box so the user can add as many boxes they want, but they have to only remember to keep the cell next to it, to the right for example, empty as it will be overridden.
Dim sh As Shape
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
For Each sh In ws.Shapes
If sh.Type = msoFormControl Then
If sh.FormControlType = xlListBox Then
sh.OLEFormat.Object.OnAction = "UpdateLBCell"
End If
End If
Next
Next
The original code above causes an object error on the innermost line.
Am I just stupid or is it not possible to loop over these dropdown boxes?
If it is impossible, can I make some other dropdown single select boxes that fit inside a cell? Combobox I tried, but they lie on top and dont match.
Any insight in alternative ways to do this is very appreciated as well.
I put a list validation on a few cells, then ran this code
Sub Test()
Dim dd As DropDown
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Dim s As Shape
For Each s In ws.Shapes
Debug.Print CStr(s.Type), s.Top, s.Left
s.Visible = msoCTrue '<<<<
Next
Next
End Sub
Before and after (yellow cells have data validation):
So it seems as though if you have a "list" data validation set up, Excel manages a single (normally invisible and empty) drop-down which is typically positioned at the current active cell. It's only made visible when that's also one of the cells with validation set up.
EDIT: here's an example of how you could handle updates to cells with drop-down DV lists in a generic way -
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
On Error GoTo haveError
Application.EnableEvents = False
For Each c In Target.Cells
If HasDVList(c) Then
c.Offset(0, 1) = Now
End If
Next c
haveError:
Application.EnableEvents = True
End Sub
'does a cell have DV list?
Function HasDVList(rng As Range)
Dim v
On Error Resume Next
v = rng.Cells(1).Validation.Type
On Error GoTo 0
HasDVList = (v = 3)
End Function
The Shape should be Visible, whether the cell is "clicked-on" or not. I put a single DV dropdown on a sheet and ran:
Sub ShapeLister()
Dim s As Shape
For Each s In ActiveSheet.Shapes
MsgBox s.Type & vbCrLf & s.Name
Next s
End Sub
and got:
Try this on a fresh worksheet and tell us what you see.
Here is the macro which is in the Sheet1 module:
Private Sub Worksheet_Calculate()
Dim oPic As Picture
Me.Pictures.Visible = False
With Range("F1")
For Each oPic In Me.Pictures
If oPic.Name = .Text Then
oPic.Visible = True
oPic.Top = .Top
oPic.Left = .Left
Exit For
End If
Next oPic
End With
End Sub
In the work book this is what it looks like:
How do I gain access to Me.Pictures? We'd like to add pictures and delete some of the existing pictures.
Try running this which just uses the third line in your existing code
Private Sub MakeAllPicsVisible()
Me.Pictures.Visible = True
End Sub
Me refers to the worksheet object the code is in. And each picture appears to be named with the items in the list
To change the picture name, enter a new name via the Name Box (left of Formula bar).