Good morning. I have a Microsoft Excel Macro Enabled Workbook with two worksheets: let's say Sheet1 and Sheet2. In Sheet2 I have a combo box (form control) that works as a table sorter. This table will be also in Sheet2. The combo uses the following code:
Option Explicit
Sub DropDown4_Change()
Dim comboValue As String
Dim Key1ColumnIndex As Integer
Dim Key2ColumnIndex As Integer
'You can get the name by doing something like this in the immediate window: "? ActiveSheet.Shapes(1).OLEFormat.Object.Name"
comboValue = ActiveSheet.Shapes("Drop Down 4").ControlFormat.List(ActiveSheet.Shapes("Drop Down 4").ControlFormat.ListIndex)
Select Case comboValue
Case "By Keyphrase"
Key1ColumnIndex = 18
Key2ColumnIndex = 19
Case "By Region"
Key1ColumnIndex = 19
Key2ColumnIndex = 18
Case "Default"
Key1ColumnIndex = 1
Key2ColumnIndex = 1
End Select
Range("DataValues").sort Key1:=Range("DataValues").Cells(1, Key1ColumnIndex), _
Order1:=xlDescending, Header:=xlNo, DataOption1:=xlSortNormal, _
Key2:=Range("DataValues").Cells(1, Key2ColumnIndex), order2:=xlDescending
End Sub
This code works like charm in this worksheet.
I'm using Aspose Cells for Java to use this Excel Workbook as a template to generate new workbooks with multiple datasheets based in Sheet2 copies (which contains my combo) but the problem is that when I do this, the combo doesn't work anymore as it did in the template.
In this line:
comboValue = ActiveSheet.Shapes("Drop Down 4").ControlFormat.List(ActiveSheet.Shapes("Drop Down 4").ControlFormat.ListIndex)
I get this error:
Run-time error '438' Object doesn't support this property or method
It looks like ControlFormat is not being recognized as a valid method for the combo shape.
This happens whether you use the combo name or the combo index (in this case is always 6).
"Drop Down 4" is the right name. I have alerted the name several times in each worksheet and both the index and the name are right.
So I hope you guys can help me out. Thank you for your patience and sorry if my English is not clear enough. Feel free to ask questions.
I figured it out by myself that hard coding the combo name (in this case "Drop Down 4") is a terrible way to do this since Excel assigns new names every time a copy of Sheet2 is added. Although Excel does this, combo names always starts with the word "Drop" (from Drop Down).
I modified a little bit the code and made it work:
Option Explicit
Sub DropDown4_Change()
Dim comboValue As String
Dim Key1ColumnIndex As Integer
Dim Key2ColumnIndex As Integer
Dim Index As Integer
Dim comboName As String
Dim comboName2 As String
Dim comboID As Integer
'You can get the name by doing something like this in the immediate window: "? Sheet1.Shapes(1).OLEFormat.Object.Name"
For Index = 1 To ActiveSheet.Shapes.Count
comboName = ActiveSheet.Shapes(Index).OLEFormat.Object.Name
If InStr(comboName, "Drop") > 0 Then
'MsgBox InStr(comboName, "Drop")
comboName2 = comboName
comboID = Index
End If
Next
comboValue = ActiveSheet.Shapes(comboID).ControlFormat.List(ActiveSheet.Shapes(comboID).ControlFormat.ListIndex)
Select Case comboValue
Case "By Keyphrase"
Key1ColumnIndex = 18
Key2ColumnIndex = 19
Case "By Region"
Key1ColumnIndex = 19
Key2ColumnIndex = 18
Case "Default"
Key1ColumnIndex = 1
Key2ColumnIndex = 1
End Select
Range("DataValues").sort Key1:=Range("DataValues").Cells(1, Key1ColumnIndex), _
Order1:=xlAscending, Header:=xlNo, DataOption1:=xlSortNormal, _
Key2:=Range("DataValues").Cells(1, Key2ColumnIndex), order2:=xlAscending
End Sub
Where is your code located?
This worked for me (in a general module)...
Sub DoSorting()
Dim dd, val
Set dd = ActiveSheet.Shapes(Application.Caller)
val = dd.ControlFormat.List(dd.ControlFormat.ListIndex)
MsgBox val
End Sub
Copying the sheet did not break it.
Related
I have a macro that is supposed to change the "WEEK"-filter on 5 pivot tables on the sheet "veckorapport".
This macro somehow changes "name" on the weeks to the number/text i put in the input box. See first picture, this numbers should be in descending order.
My code:
Sub Veckorapport_filter()
Dim num As String
Dim sht As Worksheet
Set sht = Sheets("Veckorapport")
num = InputBox(Prompt:="Vecka", Title:="ENTER WEEK")
sht.PivotTables("PivotTable1") _
.PivotFields("WEEK").CurrentPage = num
sht.PivotTables("PivotTable2") _
.PivotFields("WEEK").CurrentPage = num
sht.PivotTables("PivotTable3") _
.PivotFields("WEEK").CurrentPage = num
sht.PivotTables("PivotTable4") _
.PivotFields("WEEK").CurrentPage = num
sht.PivotTables("PivotTable5") _
.PivotFields("WEEK").CurrentPage = num
End Sub
I need help with two things: How can i write this code so it doesn't change "name" on the Weeks in the filter?
And 2nd: How can i change back the names to the proper names in the filter-list?
Link to picture: https://imgur.com/kndEnm6
EDIT: Picture how it should look: https://imgur.com/CU9fWex
EDIT 2: Those two weeks have switched places. It should be in descending order: https://imgur.com/PVS3JMf
If you change the .CurrentPage to something that does not exist, then it will instead rename the currently selected page/item. (If you do this manually, instead of via VBA, then you will get a confirmation box, "No item of this name exists in the PivotTable report. Rename 'Old_Name' to 'New_Name'?" with an "OK" and "Cancel" button)
You can partially prevent this by ensuring that "Show Items without Data" is turned on for that field, and/or you can use WorksheetFunction.CountIf to check if that value exists before changing the .CurrentPage
As for resetting the items: Set the .Name, .Value or .Caption of the PivotItem back to the SourceName:
Dim piTMP AS PivotItem, pfTMP AS PivotField
Set pfTMP = sht.PivotTables("PivotTable1").PivotFields("WEEK")
For Each piTMP In pfTMP.PivotItems
piTMP.Name = piTMP.SourceName
Next piTMP
If the names have been switched for 2 items, you will run into an issue (the same name cannot exist twice), so you either need to check for the name being in use, or to just brute-force it with 2 loops - which is probably the simpler solution to write:
Dim piTMP AS PivotItem, pfTMP AS PivotField
Set pfTMP = sht.PivotTables("PivotTable1").PivotFields("WEEK")
'Once through
For Each piTMP In pfTMP.PivotItems
piTMP.Name = piTMP.SourceName & "_AndSomeTextThatYouNeverUse"
Next piTMP
'Twice through
For Each piTMP In pfTMP.PivotItems
piTMP.Name = piTMP.SourceName
Next piTMP
To loop through all pivot tables on the sheet:
Sub Veckorapport_filter()
Dim num As String
Dim sht As Worksheet
Set sht = Sheets("Veckorapport")
num = InputBox(Prompt:="Vecka", Title:="ENTER WEEK")
If len(num) <> 0 then
dim pt as pivottable
for each pt in sht.pivottables
with pt.PivotFields("WEEK")
.clearallfilters
.CurrentPage = num
end with
next pt
end if
End Sub
I've got a question concerning combobox in Excel.
I've got an excel sheet that by default contains two comboboxes and their number is described by a variable x (x=2 by default). Each combobox is scripted to behave in a particular way in subs, for example I've got: private sub ComboBox1_DropButtonClick().
Nonetheless, sometimes I need to increase the number of these boxes by changing the value of X. I may need up to 10 comboboxes in total. Now the question is whether there's any way in which I can set the behaviour of an infinite number of comboboxes (for example in the event of DropButtonClick). What I did was to write a code for each of those comboboxes, so I've got a sub for ComboBox1_DropButtonClick(), ComboBox2_DropButtonClick(), ComboBox3_DropButtonClick(), etc.. The code varies a bit, but it's repeatable. So it all looks rather dumb and I'm searching for some more ingenious solution. Maybe all those comboboxes can be scripted in one go? If there's any way to do it, please share it with me.
Thanks, Wojciech.
[edit] Location of my code (marked in grey):
Screenshot from VBA editor in VBA
Here is some code to dynamically add controls to an Excel Userform, and add the code behind. The code added will make it display a MessageBox when the ComboBox receives a KeyDown.
The code is somewhat commented, but let me know if you have questions :)
Option Explicit
Sub CreateFormComboBoxes(NumberOfComboBoxes As Long)
Dim frm As Object
Dim ComboBox As Object
Dim Code As String
Dim i As Long
'Make a blank form called 'UserForm1', or any name you want
'make sure it has no controls or any code in it
Set frm = ThisWorkbook.VBProject.VBComponents("UserForm1")
With frm
For i = 1 To NumberOfComboBoxes
Set ComboBox = .designer.Controls.Add("Forms.ComboBox.1")
'Set the properties of the new controls
With ComboBox
.Width = 100
.Height = 20
.Top = 20 + ((i - 1) * 40) 'Move the control down
.Left = 20
.Visible = True
.ZOrder (1)
.Name = "ComboBox" & i
End With
'Add your code for each module, you can add different code, by adding a if statement here
'And write the code depending on the name, index, or something else
Code = Code & vbNewLine & "Private Sub " & "ComboBox" & i & "_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)" & _
vbNewLine & " MsgBox(""hi"")" & vbNewLine & "End Sub"
Next
'Add the code
.CodeModule.InsertLines 2, Code
End With
End Sub
'Run this
Sub Example()
CreateFormComboBoxes 5
End Sub
**Edit**
I figured I might as well add the other approach for adding controls dynamically to an Excel sheet. I'd recommend sticking to UserForms, but, here's a method that should help out when controls are needed in a Sheet.
Sub addCombosToExcelSheet(MySheet As Worksheet, NumberOfComboBoxes As Long, StringRangeForDropDown As String)
Dim i As Long
Dim combo As Shape
Dim yPosition As Long
Dim Module As Object
yPosition = 20
For i = 1 To NumberOfComboBoxes
yPosition = (i - 1) * 50
'Create the shape
Set combo = MySheet.Shapes.AddFormControl(xlDropDown, 20, yPosition, 100, 20)
' Range where the values are stored for the dropDown
combo.ControlFormat.ListFillRange = StringRangeForDropDown
combo.Name = "Combo" & i
Code = "Sub Combo" & i & "_Change()" & vbNewLine & _
" MsgBox(""hi"")" & vbNewLine & _
"End Sub"
'Add the code
With ThisWorkbook
'Make sure Module2 Exits and there is no other code present in it
Set Module = .VBProject.VBComponents("Module2").CodeModule
Module.AddFromString (Code)
End With
'Associate the control with the action, don't include the () at the end!
combo.OnAction = "'" & ActiveWorkbook.Name & "'!Combo" & i & "_Change"
Next
End Sub
Sub Example()
Dim sht As Worksheet: Set sht = ThisWorkbook.Sheets(1)
addCombosToExcelSheet sht, 10, "Sheet1!$A$1:$A$10"
End Sub
Program: Excel 2016.
I have a sheet with a lot of shapes. Each of them has its own specific name and most of them are label. I want to change their caption property, but i can't find a way but calling them one by one like this:
LblLabel_1.Caption = ...
LblLabel_2.Caption = ...
LblLabel_3.Caption = ...
Instead i was looking for something like this:
For BytCounter01 = 1 to 255
Shapes("LblLabel_" & BytCounter01).Caption = ...
Next
This one will result in error 438, basically saying Caption is not avaiable for this object. It still target the object, since this code:
Debug.print Shapes("LblLabel_" & BytCounter01).Name
will return me its name.
Looking for a solution:
-i've tried Controls("LblLabel_" & BytCounter01) instead of Shapes("LblLabel_" & BytCounter01) but it won't work since Controls is only for userforms, not for sheets;
-i've tried Shapes("LblLabel_" & BytCounter01).TextFrame.Characters.Text but it returns error 438 again;
-since the label is a part of a group, i've tried both
Shapes("ShpGroupOfShapes01").GroupItems(ShpShapeIndex).Caption
and
Shapes("ShpGroupOfShapes01").GroupItems(ShpShapeIndex).TextFrame.Characters.Text
but got 438 again.
Is there really no way to easily target a specific label on a sheet and change his caption?
Thank you.
EDIT: thanks to Excelosaurus, the problem is solved. Since my labels are ActiveX Controls i have to use something like this:
For BytCounter01 = 1 to 255
Shapes("LblLabel_" & BytCounter01)OLEFormat.Object.Object.Caption = ...
Next
You can check his response and comments for more details. Thanks again Excelosaurus!
To change the textual content of a shape, use .TextFrame2.TextRange.Text as shown below:
shtShapes.Shapes(sShapeName).TextFrame2.TextRange.Text = sShapeCaption
where shtShapes is the name of your worksheet's object as seen from the Visual Basic Editor in the Project Explorer,
sShapeName is a string variable containing the name of the target shape, and
sShapeCaptionis a string variable containing the desired caption.
A code example follows. I've thrown in a function to check for a shape's existence on a worksheet, by name.
Option Explicit
Public Sub SetLabelCaptions()
Dim bCounter As Byte
Dim sShapeName As String
Dim sShapeCaption As String
For bCounter = 1 To 255
sShapeName = "LblLabel_" & CStr(bCounter)
If ShapeExists(shtMyShapes, sShapeName) Then
sShapeCaption = "Hello World " & CStr(bCounter)
shtMyShapes.Shapes(sShapeName).TextFrame2.TextRange.Text = sShapeCaption
Else
Exit For
End If
Next
End Sub
Public Function ShapeExists(ByVal pshtHost As Excel.Worksheet, ByVal psShapeName As String) As Boolean
Dim boolResult As Boolean
Dim shpTest As Excel.Shape
On Error Resume Next
Set shpTest = pshtHost.Shapes(psShapeName)
boolResult = (Not shpTest Is Nothing)
Set shpTest = Nothing
ShapeExists = boolResult
End Function
The result should look like this:
You can't assign a Caption to a Shape. (Shapes don't have Captions). One approach is to loop over the Shapes and build a little table to tell you what to loop over next:
Sub WhatDoIHave()
Dim kolumn As String, s As Shape
Dim i As Long, r As Range
kolumn = "Z"
i = 1
For Each s In ActiveSheet.Shapes
Set r = Cells(i, kolumn)
r.Value = i
r.Offset(, 1).Value = s.Name
r.Offset(, 2).Value = s.Type
r.Offset(, 3).Value = s.TopLeftCell.Address(0, 0)
i = i + 1
Next s
End Sub
Which for my sample produced:
Seeing that I have both Forms and ActiveX (OLE) Controls, I know what to loop over next. I then refer to the Control by number and assign a Caption if appropriate.
I need to change the font size in an Excel combo box because the default font size is way too small. Is there a way to do this?
This is the code I use for my macro:
Option Explicit
Sub DropDown4_Change()
Dim comboValue As String
Dim Key1ColumnIndex As Integer
Dim Key2ColumnIndex As Integer
Dim Index As Integer
Dim comboName As String
Dim comboName2 As String
Dim comboID As Integer
'You can get the name by doing something like this in the immediate window: "? Sheet1.Shapes(1).OLEFormat.Object.Name"
For Index = 1 To ActiveSheet.Shapes.Count
comboName = ActiveSheet.Shapes(Index).OLEFormat.Object.Name
If InStr(comboName, "Drop") > 0 Then
'MsgBox InStr(comboName, "Drop")
comboName2 = comboName
comboID = Index
End If
Next
comboValue = ActiveSheet.Shapes(comboID).ControlFormat.List(ActiveSheet.Shapes(comboID).ControlFormat.ListIndex)
Select Case comboValue
Case "By Keyphrase"
Key1ColumnIndex = 18
Key2ColumnIndex = 19
Case "By Region"
Key1ColumnIndex = 19
Key2ColumnIndex = 18
Case "Default"
Key1ColumnIndex = 1
Key2ColumnIndex = 1
End Select
Range("DataValues").sort Key1:=Range("DataValues").Cells(1, Key1ColumnIndex), _
Order1:=xlAscending, Header:=xlNo, DataOption1:=xlSortNormal, _
Key2:=Range("DataValues").Cells(1, Key2ColumnIndex), order2:=xlAscending
End Sub
Thank you.
It can't be done
There's no formatting the font size of a forms dropdown - even programatically. If it's an absolute requirement, you'll have to switch this to an activeX control.
As said by #Alain you simply can't in a clean way, or you have to use an activeX control.
But here there's also a workaround.
With programming, you can temporarily zoom the worksheet, to make the data validation font size appear larger.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Address = "$A$2" Then
ActiveWindow.Zoom = 120
Else
ActiveWindow.Zoom = 100
End If
End Sub
Credits and more details: https://www.contextures.com/xldataval08.html#zoommacro
How to obtain a list of named range exist in a specific worksheet that start with particular string (for example all named range that start with total) and grab the value? I am trying to do Sub Total and Grand Total of accommodation cost based on the date. I will assign an unique name for each Sub Total based on the Date group. Then, I have a button that need to be clicked when it finishes to calculate the Grand Total based on the Named Range that I've assigned uniquely to each Sub Total.
Below is the code I wrote to do the Grand Total:
Sub btnTotal()
Dim Total, LastRowNo As Long
LastRowNo = ActiveSheet.UsedRange.Row + ActiveSheet.UsedRange.Rows.Count
Total = 0
For Each N In ActiveWorkbook.Names
Total = Total + IntFlight.Range(N.Name).Value
Next N
IntFlight.Range("$P" & LastRowNo).Select
Selection.NumberFormat = "$* #,##0.00;$* (#,##0.00);$* ""-""??;#"
With Selection
.Font.Bold = True
End With
ActiveCell.FormulaR1C1 = Total
End Sub
Note: the IntFlight from "Total = Total + IntFlight.Range(N.Name).Value" is the name of my worksheet.
The only problem with above code, it will looking all named range exist in the workbook. I just need to find named range exist in one particular worksheet, which start with given string and the row number (total26: means Sub Total from row 26) and then grab the value to be sum-ed as Grand Total.
Any ideas how to do this? Been spending 2 days to find the answer.
Thanks heaps in advance.
EDIT 1 (Solution Provided by Charles Williams with help from belisarius):
This is what I have done with the code from Charles Williams:
Option Explicit
Option Compare Text
Sub btnIntFlightsGrandTotal()
Dim Total, LastRowNo As Long
LastRowNo = FindLastRowNo("International Flights")
Dim oNM As Name
Dim oSht As Worksheet
Dim strStartString As String
strStartString = "IntFlightsTotal"
Set oSht = Worksheets("International Flights")
For Each oNM In ActiveWorkbook.Names
If oNM.Name Like strStartString & "*" Then
If IsNameRefertoSheet(oSht, oNM) Then
Total = Total + Worksheets("International Flights").Range(oNM.Name).Value
End If
End If
Next oNM
IntFlights.Range("$P" & LastRowNo).Select
Selection.NumberFormat = "$* #,##0.00;$* (#,##0.00);$* ""-""??;#"
With Selection
.Font.Bold = True
End With
ActiveCell.FormulaR1C1 = Total
End Sub
Function FindLastRowNo(SheetName As String) As Long
Dim oSheet As Worksheet
Set oSheet = Worksheets(SheetName)
FindLastRowNo = oSheet.UsedRange.Row + oSheet.UsedRange.Rows.Count
End Function
Thank you all for your help. Now, I need to come up with my own version for this script.
Here is some code that checks if a Defined Name starts with a string and refers to a range within the used range of a given worksheet and workbook.
Option Explicit
Option Compare Text
Sub FindNames()
Dim oNM As Name
Dim oSht As Worksheet
Dim strStartString As String
strStartString = "Total"
Set oSht = Worksheets("TestSheet")
For Each oNM In ActiveWorkbook.Names
If oNM.Name Like strStartString & "*" Then
If IsNameRefertoSheet(oSht, oNM) Then
MsgBox oNM.Name
End If
End If
Next oNM
End Sub
Function IsNameRefertoSheet(oSht As Worksheet, oNM As Name) As Boolean
Dim oSheetRange As Range
IsNameRefertoSheet = False
On Error GoTo GoExit
If Not oSht Is Nothing Then
If Range(oNM.Name).Parent.Name = oSht.Name And _
Range(oNM.Name).Parent.Parent.Name = oSht.Parent.Name Then
Set oSheetRange = oSht.Range("A1").Resize(oSht.UsedRange.Row + oSht.UsedRange.Rows.Count - 1, oSht.UsedRange.Column + oSht.UsedRange.Columns.Count - 1)
If Not Intersect(Range(oNM.Name), oSheetRange) Is Nothing Then IsNameRefertoSheet = True
Set oSheetRange = Nothing
End If
End If
Exit Function
GoExit:
End Function
The following function will output all the names and their totals in your Workbook.
I think it is the basic block you need to get your code running.
Sub btnTotal()
For Each N In ActiveWorkbook.Names
MsgBox N.Name + " " + CStr(Application.WorksheetFunction.Sum(Range(N)))
Next N
End Sub
Edit
Answering your comment:
Define your names in this way:
Then (and only then) the following code works:
Sub btnTotal()
For Each N In ActiveSheet.Names
If (InStr(N.Name, "!Total") <> 0) Then
MsgBox N.Name + " " + CStr(Application.WorksheetFunction.Sum(Range(N)))
End If
Next N
End Sub
If you do not define the scope of the names correctly you need a lot of extra work in your code.
Edit
As you forgot to mention that you are still working with Excel 2003, here you will find an addin to manage name scoping in that version. See screen cap below
HTH