Set Min/Max of Spin Button Form Control with VBA - vba

I have a spin button on my worksheet (not in a userform), and I need to set the minimum and maximum values in VBA. Easy, right? I tried worksheetName.Shapes("shapeName").Min = x but I get Run-time error 438: Object doesn't support this property or method.
I used Excel's macro recorder and changed the min and max of the spin button, and it recorded the following:
ActiveSheet.Shapes("shapeName").Select
With Selection
.Min = x
.Max = y
End With
How is it that if I select the shape, I can then access its properties, but if I reference the shape directly I am unable to access the same properties? This does not make sense to me. Obviously, I would like to avoid selecting the shape and referencing "selection," as this generally is not best practice.

You can use the Shape.ControlFormat property:
Sub test()
Dim shp As Excel.Shape
Set shp = ActiveSheet.Shapes("Spinner 1")
With shp.ControlFormat
.Min = 2
.Max = 33
End With
End Sub

This answer will be helpful, although not particularly intuitive...
Excel-VBA: Getting the values from Form Controls
As I mention in the comments above, it is peculiar. The SpinButton is a member of the worksheet's Shapes collection, but it does not allow you to access those properties as a shape directly (see Doug's answer for how to do this another way which is probably better).
Try:
With ActiveSheet.Spinners("spinbutton1")
.Min = x
.Max = y
End With
Likewise, you can delcare a variable and iterate if you have multiple controls like this:
Dim spinbtn as Spinner 'or As Variant
For each spinbtn in ActiveSheet.Spinners
spinbtn.Min = x
spinbtn.Max = y
Next
Etc.

Simply you can try this:
Private Sub SpinButton1_SpinDown()
TextBox3.Text = val(TextBox3.Text) - 1
If TextBox3.Text < 0 Then TextBox3.Text = 0
End Sub

Related

PowerPoint vba group shapes using Shape objects, not shape names

I've written some code that formats text. The code doesn't work if user has put the cursor in a shape that is part of a group of shapes, the solution for which is to ungroup the shapes.
I want to regroup the shapes after executing the formatting code.
I am able to store the underlying shapes as objects, as well as their names. But, the normal approach to grouping (using shape names) doesn't work, because there can be multiple instances of those shape names on a given slide. E.g. this doesn't work as there could be multiple instances of "textbox" on the slide:
Set TempShapeGroup = TempSlide.Shapes.Range(Array("textbox", "header", "separator")).Group
https://learn.microsoft.com/en-us/office/vba/api/powerpoint.shaperange.group
But, I have the shape objects stored in an array, the crux of which is this (the object 'TempShape' is the group of shapes):
Dim ShapesArray() As Shape
ReDim ShapesArray(1 To TempShape.GroupItems.Count)
For i = 1 To TempShape.GroupItems.Count
Set ShapesArray(i) = TempShape.GroupItems.Item(i)
Next i
So, what I want to do is recreate the group of shapes, using the array of shape objects, so something to the effect of the below would be ideal:
Set MyShapesGroup= ShapesArray.Group
But any way to group shapes using Shape objects would be fine.
TIA
Here's some starter code that you can modify into a function that'll return a reference to the paragraph that contains the current selection cursor. It doesn't really need all the debug.print stuff, of course, but that might help to illustrate the object hierarchy:
Sub WhereIsTheCursor()
Dim oRng As TextRange
Dim oParentRange As TextRange
Dim x As Long
Dim lSelStart As Long
Dim lSelLen As Long
With ActiveWindow.Selection.TextRange
' find the selection start relative to first character in shape
lSelStart = .Start
' lSelLen = .Length
Debug.Print TypeName(.Parent)
Debug.Print TypeName(.Parent.Parent)
Debug.Print TypeName(.Parent.Parent.Parent)
Debug.Print .Paragraphs.Count
Set oRng = .Characters(.Start, .Length)
Debug.Print oRng.Text
' Reference the overall shape's textrange
Set oParentRange = .Parent.Parent.TextFrame.TextRange
' For each paragraph in the range ...
For x = 1 To oParentRange.Paragraphs.Count
' is the start of the selection > the start of the paragraph?
If lSelStart > oParentRange.Paragraphs(x).Start Then
' is the start < the start + length of the paragraph?
If lSelStart < oParentRange.Paragraphs(x).Start _
+ oParentRange.Paragraphs(x).Length Then
' bingo!
MsgBox "The cursor is in paragraph " & CStr(x)
End If
End If
Next
End With
End Sub
Not sure I'm completely understanding the problem, but this may help:
If the user has selected text within a shape, it doesn't really matter whether the shape is part of a group or not. You may need to test the .Selection.Type and handle things differently depending on whether the .Type is text or shaperange. Example:
Sub FormatCurrentText()
If ActiveWindow.Selection.Type = ppSelectionText Then
With ActiveWindow.Selection.TextRange
.Font.Name = "Algerian"
End With
End If
End Sub

Creating a template to create, add, and format text boxes to a slide based on user input in PPT using VBA

I have another questions that asks about the logic behind using several boolean radio buttons to ask survey questions, located here. The largets issue I have had was getting the text boxes to populate a separate slide to display the systems that will affect the customer based on their radio button input.
This post is an attempt to find a way to reduce the code needed to create, place, and format the text boxes. The below code is what I have so far, but it keeps telling me it expects an "=" when I run it.
Public Sub textBox(t As Integer, l As Integer, w As Integer, h As Integer, Sys As String)
Dim myTB As Shape
With ActivePresentation.Slides(7)
Set myTB = .Shapes.AddTextbox(msoTextOrientationHotizontal, l, t, w, h)
myTB.TextFrame.TextRange.Text = Sys
myTB.TextFrame.TextRange.Font.Color.RGB = RGB(255,255,255)
myTB.TextFrame.TextRange.Font.Size = 20
End With
End Sub
I think I may have the logic wrong in declaring the variables. What I want to be able to do is simply place textBox(200,300,200,25, "ERP Name") into my If Then or Case statement to format the boxes. Is there a better way to do this?
You'd need to use
CALL textBox(parm, parm, ... , parm)
or leave out the parentheses.
For clearing everything out, here's a starting point:
Sub ClearTheField()
Dim oSl As Slide
Dim oSh As Shape
For Each oSl In ActivePresentation.Slides
For Each oSh In oSl.Shapes
If oSh.Type = msoOLEControlObject Then
If Left$(oSh.OLEFormat.Object.Name, Len("TextBox")) = "TextBox" Then
oSh.OLEFormat.Object.Text = ""
End If
If Left$(oSh.OLEFormat.Object.Name, Len("CheckBox")) = "CheckBox" Then
oSh.OLEFormat.Object.Value = False
End If
If Left$(oSh.OLEFormat.Object.Name, Len("CommandButton")) = "CommandButton" Then
oSh.OLEFormat.Object.Caption = "I changed too"
End If
End If
Next
Next
End Sub

Reading checkbox values with a loop (Microsoft Word VBA)

I'm current trying to write a macro (VBA in Word) that will compile information from a collection of documents into a single document.
I order to do this I have a list of ~20 checkboxes that will determine which documents I want to include in the compilation. My issue is that when writing the macro, I can't figure out a way of checking the state of each checkbox on my list without re-writing the same block of code 20 times, only changing the name of the checkbox. eg CB1 to CB2, CB3 CB4 etc. each time.
This is the block of code in question. It does work if I rewrite it multiple times for the changing check box number but I would prefer it in a loop so the code is more compact and robust:
If ThisDocument.CB1.Value = True Then
Documents.Open(directory).Activate
Selection.WholeStory
Selection.Copy
Documents(NewFile).Activate
Selection.Paste
Documents("file.docx").Close
End If
Ideally I would like to have the check box named something like CBn, where n is a variable that I can redefine at the end of each loop.
There's no option for directly referring to a control by its name - you can wrap that up in a function though:
Sub Tester()
Dim x As Long, cb As Object
For x = 1 To 3
'find the checkbox
Set cb = ControlByName("CB" & x, ThisDocument)
'check we got something back
If Not cb Is Nothing Then
Debug.Print "CB" & x & " is " & cb.Value
End If
Next x
End Sub
Function ControlByName(sName, doc As Document) As Object
Dim obj
For Each obj In doc.InlineShapes
If obj.OLEFormat.Object.Name = sName Then
Set ControlByName = obj.OLEFormat.Object
Exit Function
End If
Next obj
End Function

Powerpoint Random Name Selection VBA

I have the following VBA code in Powerpoint 2010 to pick a name from a list at random:
Dim hat As New Collection
Sub fill_the_hat()
Dim items() As String
Dim x As Long
items = Split("Test\Names\John\Bob\Chris\Mike\Robert\Adam", "\")
For x = 0 To UBound(items)
hat.Add(items(x))
Next x
End Sub
Sub pick_one()
Dim x As Long
Randomize
x = Int(Rnd * hat.Count) + 1
MsgBox hat(x)
hat.Remove (x)
End Sub
I need to adapt this to output to a text box rather than a MsgBox but this doesn't seem as obvious as I thought it would be?
Any help would be greatly appreciated,
Many Thanks,
Josh
You will have to adress your UserForm and TextBox directly to change the contents. An example would be:
UserForm1.TextBox1.Text = x
This has to happen before the UserForm1.Show call or alternatively you have to refresh the form via UserForm1.Repaint
If you are just displaying the name there is no need at all to use an ActivX textbox. Just use a normal shape or textbox
ActivePresenation.Slides(1).Shapes ("nameofshape").Textframe.TextRange=hat(x)
Also when you use my code from the net it is more normal to say "I got this code from John Wilson's article here" rather that "I have this code"

How do I refer to a controls object, on a worksheet, using a variable name?

I have added a ListBox to a SHEET (not to a "UserForm")
I did this using the mouse.
I clicked the little Hammer and Wrench icon.
This ListBox seems to be easily referenced using code such as this:
ListBox1.Clear
or
ListBox1.AddItem("An option")
However, I have three of these ListBoxes (named, conveniently, ListBox1, ListBox2, and ListBox3) and I want to write a function to populate them with array data, like this:
Call populate_listbox(ListBox2, designAreaArray)
Where the first argument is the listbox name, the 2nd is the data.
But I do not know how to send "ListBox2" correctly, or refer to it correctly within the function.
For example:
Dim controlName as string
controlName = "ListBox1"
doesn't work, even if I define the function as follows:
Sub populate_listbox(LB As ListBox, dataArray As Variant)
Dim i As Integer: i = 0
For i = LBound(dataArray, 2) + 1 To UBound(dataArray, 2) ' Skip header row
LB.AddItem (dataArray(index, i))
Next i
End Sub
Clearly it results in a mis-matched data type error. I've tried defining "controlName" as a ListBox, but that didn't work either...
Though perhaps it is my reference to the listBox that is incorrect. I've seen SO MANY ways to refer to a control object...
MSForms.ListBox.
ME.ListBox
Forms.Controls.
Worksheet.Shapes.
The list goes on an on, and nothing has worked for me.
Try this:
Dim cMyListbox As MSForms.ListBox
Set cMyListbox = Sheet1.ListBox1 '// OR Worksheets("YourSheetName").Listbox1
cMyListbox.AddItem("An option")
Also you can populate a listbox without having to loop through the array, try this:
Dim cMyListbox As MSForms.ListBox
Dim vArray As Variant
Set cMyListbox = Sheet1.ListBox1
vArray = Range("A1:A6").Value
cMyListbox.List = vArray
Change the sub signature to match this:
Sub populate_listbox(LB As MSForms.ListBox, dataArray As Variant)
Now you can pass it like you were trying to originally.
NOTE: This only works if you used the "ActiveX" version of the listbox. I'm assuming you are because you are able to call ListBox1 straight from a module.
PS: The ActiveX controls are members off of the parent sheet object. So if you have the listbox1 on sheet1, you can also call it like Sheet1.ListBox1 so you don't get confused if you end up with multiple sheets with multiple listboxes. Also, you may want to change the name just to make it easier on yourself.
Dim controlName As OLEObject
Set controlName = Sheet1.OLEObjects("ListBox1")
Call populate_listbox(controlName, designAreaArray)
Sub populate_listbox(LB As OLEObject, dataArray As Variant)
Dim i As Integer: i = 0
For i = LBound(dataArray, 2) + 1 To UBound(dataArray, 2) ' Skip header row
LB.Object.AddItem (dataArray(Index, i))
Next i
End Sub
To access the state of a checkbox Active-X control on Sheet1:
Dim checkBox1 As Object
Set checkBox1 = Sheet1.OLEObjects("CheckBox1").Object
MsgBox checkBox1.Value