Excel VBA End User Selecting a Chart Object - excel

I would like to generate some code that allows an end user to select one of many charts from a sheet, after which I will do a bunch of manipulation based on that selection.
I am looking for something similar to the Application.Inputbox Type:=8 that allows for an object selection instead of a range selection.
Am I asking to much of humble old VBA??

It's a lot easier to select the chart first, then run code on the selected chart(s), than it is to pause the code and try to select the chart(s) from within the code.
But it can be done.
You need a userform, called F_ChartChooser with two buttons, btnCancel and btnContinue.
The code in the F_ChartChooser module:
Option Explicit
Private Sub btnCancel_Click()
CancelProcedure
End Sub
Private Sub btnContinue_Click()
ContinueProcedure
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' so clicking red X doesn't crash
If CloseMode = 0 Then
Cancel = True
CancelProcedure
End If
End Sub
The code in the regular module consists of a main procedure which must get the chart(s) from the user. It has to call the userform modelessly so the user can select charts in the worksheet. This means the code continues running while the form is displayed, so the thing to do is end the sub when the userform is called.
Based on what happens with the userform, the code either continues with CancelProcedure or with ContinueProcedure. Here's the code:
Option Explicit
Dim mfrmChartChooser As F_ChartChooser
Sub Main()
' code here
' need to select chart(s) here
Application.Goto ActiveCell
Set mfrmChartChooser = New F_ChartChooser
mfrmChartChooser.Show vbModeless
End Sub
Sub CancelProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
MsgBox "User canceled.", vbExclamation
End Sub
Sub ContinueProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
If Not ActiveChart Is Nothing Then
' do something with active chart
' this demo is announcing that it was selected
MsgBox """" & ActiveChart.ChartTitle.Text & """ was selected.", vbExclamation
' end of demo code
ElseIf TypeName(Selection) = "DrawingObjects" Then
Dim sh As Shape
Dim vCharts As Variant
Dim nChart As Long
ReDim vCharts(0 To nChart)
For Each sh In Selection.ShapeRange
If sh.HasChart Then
' do something here with each chart
' this demo is building a list of selected charts
nChart = nChart + 1
ReDim Preserve vCharts(0 To nChart)
vCharts(nChart) = sh.Chart.ChartTitle.Text
' end of demo code
End If
Next
' this demo now is showing the list of selected charts
If nChart = 0 Then
MsgBox "No chart selected.", vbExclamation
Else
If nChart = 1 Then
MsgBox """" & vCharts(nChart) & """ was selected.", vbExclamation
Else
Dim sPrompt As String
sPrompt = nChart & " charts selected:" & vbNewLine & vbNewLine
Dim iChart As Long
For iChart = 1 To nChart
sPrompt = sPrompt & """" & vCharts(iChart) & """" & IIf(iChart < nChart, vbNewLine, "")
Next
MsgBox sPrompt, vbExclamation
End If
End If
' end of demo code
Else
' do nothing because no chart was selected
' this demo is announcing that nothing was selected
MsgBox "No chart selected.", vbExclamation
' end of demo code
End If
End Sub
The CancelProcedure and ContinueProcedure routines above have excess code in them just to help with the demo. In real code I would streamline them like this, probably not even bother to notify the user when nothing was selected (they know they canceled, right?), and just process the selected chart(s):
Sub CancelProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
End Sub
Sub ContinueProcedure()
Unload mfrmChartChooser
Set mfrmChartChooser = Nothing
If Not ActiveChart Is Nothing Then
' do something with active chart
ProcessChart ActiveChart
ElseIf TypeName(Selection) = "DrawingObjects" Then
Dim sh As Shape
Dim vCharts As Variant
Dim nChart As Long
ReDim vCharts(0 To nChart)
For Each sh In Selection.ShapeRange
If sh.HasChart Then
' do something here with each chart
ProcessChart sh.Chart
Next
End Sub

Related

Change Worksheet after personalized right click option added

I have the following code that works as expected:
Private Sub Worksheet_BeforeRightClick(ByVal Target As Range, Cancel As Boolean)
Dim cmdBtn As CommandBarButton
Dim currentUPC As String
Dim currentArticle As String
Set cmdBtn = Application.CommandBars("Cell").FindControl(, , "testBt")
If Intersect(Target, Range("C21:C42")) Is Nothing Then
If Not cmdBtn Is Nothing Then cmdBtn.Delete
Exit Sub
End If
If Not cmdBtn Is Nothing Then Exit Sub
Set cmdBtn = Application.CommandBars("Cell").Controls.Add(Temporary:=True)
currentUPC = ActiveCell.Value
currentArticle = ActiveCell.Offset(0, 1).Value
With cmdBtn
.Tag = "testBt"
.Caption = "Goto UPC"
.Style = msoButtonCaption
.OnAction = "=gotoUPC(" & currentUPC & ")"
End With
End Sub
But the macro it runs is not quite working as planned:
Sub gotoUPC(currentUPC As String)
Sheets("UPC Summary").Range("A21").Value = currentUPC
Worksheets("UPC Summary").Activate
End Sub
The value in Range A21 updates correctly but it will not activate the worksheet as required.
Any additional thoughts on my method of passing the parameter appreciated #FaneDuru
Any thoughts appreciated, many thanks, Alan.
If you want to go to the worksheet 'UPC Summary' you could use this.
Application.Goto Sheets("UPC Summary").Range("A1"), True

Excel Worksheet_Change not detecting changes

Unfortunately Worksheet_change is not working for me. I am using a Sheet where the first column is NOW() function. If I click anywhere in the excel the time on this cell changes, but the Worksheet_Change just don't detect it.
A2 is using =NOW()
Anyone knows how to fix this issue? I have tried several different ways and no one works.
'Option Explicit
'Option Base 1
Dim xVal As Double
'Update by Extendoffice 2018/8/22
'Private Sub Worksheet_Calculate(ByVal Target As Range)
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Address = Range("$A$2").Address Then
'Rows(3).Insert Shift:=xlDown
'Range("$A$3").Value = Now
'Range("$B$3").Value = xVal
'Range("$C$3").Value = Range("$C$2").Value
'Else
If xVal <> Range("$B$2").Value Then
Debug.Print xVal & " <- xVal IF"
Debug.Print Range("B2").Text & "<- Text IF"
Rows(3).Insert Shift:=xlDown
Range("$A$3").Value = Now
Range("$B$3").Value = xVal
Range("$C$3").Value = Range("$C$2").Value
End If
End If
Application.EnableEvents = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
xVal = Range("$B$2").Value
End Sub
A Worksheet_Calculate Event Study
Google Drive
Cell A2 in worksheet Sheet1 contains the formula =B2.
Sheet1
Option Explicit
Private Sub Worksheet_Calculate()
WsCalc
End Sub
' Only to trigger the calculate event when different cell is selected.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
ThisWorkbook.Worksheets("Sheet1").Range("B2") = Int(Rnd() * 2 + 1)
End Sub
ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
WsCalc
End Sub
Module1
Option Explicit
Public lngSource As Long ' Current Source Value
Sub WsCalc()
Dim rngSource As Range ' Source Cell Range
' Create a reference to Source Cell Range (rngSource).
Set rngSource = ThisWorkbook.Worksheets("Sheet1").Range("A2")
' When the workbook opens, Current Source Valuec (lngSource) is equal to "".
' Therefore:
If lngSource = 0 Then
' Initialize Current Source Value (lngSource) i.e. write value
' of Source Cell Range (rngSource) to Current Source Value (lngSource).
lngSource = rngSource.Value
MsgBox "Monitoring started (lngSource=" & lngSource & ")."
Exit Sub
End If
' If you need disabling events, this is how you implement it. Not needed
' in this code.
' Application.EnableEvents = False
On Error GoTo ProgramError
' Check value of Source Cell Range (rngSource)
' against Current Source Value (lngSource).
If rngSource.Value <> lngSource Then
' The value has changed.
MsgBox "The value has changed from '" & lngSource & "' to '" _
& rngSource.Value & "'."
lngSource = rngSource.Value
Else
' The value hasn't changed (usually no code).
MsgBox "Value NOT changed, still '" & lngSource & "'"
End If
SafeExit:
' MsgBox "Enabling events before exiting."
' Application.EnableEvents = True
Exit Sub
ProgramError:
' Improve this error handling.
MsgBox "An unexpected error occurred."
On Error GoTo 0
GoTo SafeExit
End Sub

VBA pass argument to userform

I have a userform which I wish to pass a range to. I have tried a couple of different ways to do it but does not seem to work.
Here is the sub code:
Option Explicit
Sub Additional_Comments_Normal()
Dim MSG1 As Integer
Dim msg As String
Dim act As Range
On Error GoTo ErrHandler
'Calls userform
MSG1 = MsgBox("Would you like to add comments", vbYesNo, "Add comments")
If MSG1 = vbYes Then
With AddComments
On Error Resume Next
Set act = Application.InputBox(Prompt:="Please choose files you wish to add comments to", Type:=8)
If act Is Nothing Then
Exit Sub
End If
Application.ScreenUpdating = True
.Show
End With
Else
Exit Sub
End If
ErrHandler:
If Err.Number <> 0 Then
msg = "Error # " & Str(Err.Number) & " was generated by " _
& Err.Source & Chr(13) & Err.Description
MsgBox msg, , "Error", Err.HelpFile, Err.HelpContext
End If
End Sub
And the userform code is here:
Public act As Range
Private Sub CommandButton1_Click()
Dim ctl As Control
Dim rng As Range
Dim MSG2 As Integer
Dim sfile As String
If act.Column > 1 Then
MsgBox ("Please choose File name from Column 1")
Exit Sub
End If
If act.Row < 4 Then
MsgBox ("Please choose a valid file")
Exit Sub
End If
If Me.TxtComment.Value = "" Then
MsgBox "Please add comments", vbExclamation, "Additional Comments"
Me.TxtComment.SetFocus
Exit Sub
End If
If Me.TxtName.Value = "" Then
MsgBox "Please add your name", vbExclamation, "Additional Comments"
Me.TxtName.SetFocus
Exit Sub
End If
MSG1 = MsgBox("Add Comments ?", vbYesNo, "Add comments")
If MSG1 = vbNo Then
End If
If MSG1 = vbYes Then
act.Offset(0, 16).Value = act.Offset(0, 16).Text & " " & Me.TxtComment.Value
act.Offset(0, 17).Value = act.Offset(0, 17).Text & " " & Me.TxtName.Value
For Each ctl In Me.Controls
If TypeName(ctl) = "TextBox" Then
ctl.Value = ""
End If
Next ctl
AddComments.Hide
Application.DisplayAlerts = False
ActiveWorkbook.Save
Application.DisplayAlerts = True
End If
End Sub
Private Sub CommandButton2_Click()
End Sub
Private Sub CommandButton3_Click()
Unload Me
End Sub
Private Sub UserForm_Click()
End Sub
I then get an error about the act not being defined variable.
Can anyone shed some light on better process for this?
You have set Option Explicit at the top of your code. That means that all variables need to be defined (which is considered good programming practice). So, you have two options to resolve this:
(1) Remove the line Option Explicitfrom your code or
(2) define all of your variables using the Dim command. In this case you'd have to add Dim act as Range to your Sub CommandButton1_Click on the form.
If you want to pass a variable to another sub then you can do so calling that sub with that variable like so:
Call Additional_Comments_Normal(act)
and the sub header neeeds to change like so:
Sub Additional_Comments_Normal(ByVal act as Range)
'(your code)'
End Sub
If "passing a variable to another sub" is too much trouble then you can also save the range somewhere in your file like so:
SomeHiddenSheet.Range("A1").Value2 = act
and in the other sub you can initiate act again:
act = SomeHiddenSheet.Range("A1").Value2

Excel vba list issue

I have created a Command Button on my work sheet with the following codes. My excel file has more than 80 sheets. Now the issue in this list appears partially due to a big list. (first 40 items only)
How can I divide this list into 2 or 3 vertical lists?
Private Sub CommandButton1_Click()
Dim myList As String
Dim mySht
For i = 1 To ActiveWorkbook.Sheets.Count
myList = myList & i & " - " & ActiveWorkbook.Sheets(i).Name & " " & vbCr
Next i
mySht = InputBox("Select Sheet to go to." & vbCr & myList)
If mySht = "" Then
'MsgBox "User pressed CANCEL or empty string is submitted"
Exit Sub
End If
If Not IsNumeric(mySht) Or mySht < 1 Or mySht > ActiveWorkbook.Sheets.Count Then
MsgBox "Wrong input"
Exit Sub
End If
ActiveWorkbook.Sheets(CInt(mySht)).Select
End Sub
You are creating a list and entering it in the prompt where there isn't enough space. I suggest creating a custom form with a listbox like so:
Go to the VBA screen
Insert Userform
Add a Listbox
Add a Command button
Enter this code in the Userform module
Option Explicit
Private Sub UserForm_Initialize()
Dim i As Long
For i = 1 To ActiveWorkbook.Sheets.Count
Me.ListBox1.AddItem ActiveWorkbook.Sheets(i).Name
Next i
End Sub
Private Sub CommandButton1_Click()
Dim myStr As String
myStr = Me.ListBox1
ActiveWorkbook.Sheets(myStr).Activate
Me.Hide
End Sub
Then have your command button open the userform

Test if a workbook contains password protected chart sheets

I am using this code to test whether worksheets are password protected in the specified workbook objXL.
Function IsProtected(objXL As Object) As Boolean
Dim wksht As Excel.Worksheet
Dim cell As Excel.Range
Select Case TypeName(objXL)
Case "Worksheet"
If objXL.ProtectContents Then
IsProtected = True
Exit Function
End If
Case "Workbook"
If objXL.ProtectStructure Then
IsProtected = True
Exit Function
End If
For Each wksht In objXL.Worksheets
If wksht.ProtectContents Then
IsProtected = True
Exit Function
End If
Next wksht
Case "Range"
If objXL.Cells.Count = 1 Then
If (objXL.Locked And objXL.Parent.ProtectContents) Or (IsProtected(objXL.Parent.Parent)) Then
IsProtected = True
Exit Function
End If
Else
For Each cell In objXL
If (cell.Locked And cell.Parent.ProtectContents) Or (IsProtected(cell.Parent.Parent)) Then
IsProtected = True
Exit Function
End If
Next cell
End If
End Select
End Function
The function fails to detect chart sheets that are password protected. Any ideas how I can modify this?
I believe it should work if you loop through all the sheets instead of all the worksheets (which don't include chart sheets). Try running the codes below in a workbook with chart sheets and see the difference.
Sub wkshts()
For Each ws In Worksheets
Name = Name & " " & ws.Name & vbNewLine
Next
MsgBox Name
End Sub
Sub shts()
For Each ws In sheets
Name = Name & " " & ws.Name & vbNewLine
Next
MsgBox Name
End Sub

Resources