Convert String To Property or Object (VBA) - string

I am building an array in VBA for Powerpoint.
The sub sweeps through all shapes in each slide in the presentation, and I want to store into the array:
The property (as string or object)
The value of that property.
I will then load this array into a list box.
When the user clicks on the selected item in the list box I want the property to be set with the value.
In code it would be something like this:
Dim s_MyProperty as string
Dim s_Value as string
'Remember the variables
s_MyProperty = ".PageSetup.SlideHeight"
s_Value = ActivePresentation.PageSetup.SlideHeight
' This is the part I need help with
' Apply the property
Dim o_Object as object ' or something similar
o_Object = ActivePresentation
o_Object & s_MyProperty = s_Value
The code would be similar in Excel.
Any ideas?

check out the CallByName function... its an oldie but a goodie.
CallbyName o_Object, s_MyProperty, VbLet, s_Value

Related

Refer to object properties dynamically

I have an exemplar of the class ClsFruit With following member variables:
I also have an excel sheet with data, like this:
I don't feel like Populating the object using direct references like
Fruit.Name = FruitSheet.Cells(1,2).Value
Fruit.Color = FruitSheet.Cells(2,2).Value
Fruit.Price = FruitSheet.Cells(3,2).Value
is the way to go because it's tons of repetitive code and positions of items on the worksheet might change in the future. So I wanted to loop through the first column in excel Name-Color-Priceand populate the object dynamically something like this:
Dim rg As Excel.Range
Set rg = FruitSheet.Range("A1", "A3")
Dim Cell As Variant
For Each Cell In rg
Fruit(Cell.Value) = Cell.Offset(0, 1).Value
Next Cell
But this Fruit(Cell.Value) construct doesn't work, I get "Object doesn't support this property or method" error. Is there a way around it?
You probably need to do something like that
For Each Cell In rg
Select Case Cell.Value
Case "Name"
fruit.Name = Cell.Offset(0, 1).Value
Case "Color"
fruit.Color = Cell.Offset(0, 1).Value
Case "Price"
fruit.Price = Cell.Offset(0, 1).Value
End Select
Next Cell
Another way would be to have corresponding propertiers in your class. Then you could use CallByName
For Each Cell In rg
CallByName fruit, cell.value, VbLet, Cell.Offset(0, 1).Value
Next Cell
Update: The class has to be changed like that
Option Explicit
Public mName As String
Public mColor As String
Public mPrice As Long
Property Let name(nValue As String)
mName = nValue
End Property
Property Get name() As String
name = mName
End Property
' continue with similar properties for the other member variables
Update 2:As pointed out in the comments it is not neccessary to have Let/Get etc. One can stick to public member variables and CallByName will just work fine. It is just IMHO the cleaner approach on the long run, see here
Using a worksheet proxy will do what you want, I think. This will return a collection of your objects:
WorksheetProxy class
Option Explicit
Private Property Get Table() As ListObject
Set Table = Sheet1.ListObjects(1)
End Property
Private Property Get NameColumnIndex() As Long
NameColumnIndex= Table.ListColumns("Name").Index
End Property
Private Property Get ColorColumnIndex() As Long
ColorColumnIndex= Table.ListColumns("Color").Index
End Property
Private Property Get PriceColumnIndex() As Long
PriceColumnIndex= Table.ListColumns("Price").Index
End Property
Private Property Get Data() As Collection
Dim result As Collection
Set result = New Collection
Dim currentRow As ListRow
For Each currentRow In Table.ListRows
Dim currentItem As ClsFruit
Set currentItem = New ClsFruit
currentItem.Name= currentRow.Range(ColumnIndex:=NameColumnIndex).value
currentItem.Color= currentRow.Range(ColumnIndex:=ColorColumnIndex).value
currentItem.Price= currentRow.Range(ColumnIndex:=PriceColumnIndex).value
result.Add currentItem
Next
Set Data = result
End Property
Mathieu Guindon has a discussion of this approach here: https://rubberduckvba.wordpress.com/2017/12/08/there-is-no-worksheet/
In the comments section is a link to his example workbook.

VBA returning a dictionary from a function

Problem: I want to create a function that gets one string and returns a dictionary.
After much reading in SO I reproduced exactly the code given in some questions and I end up with a solution that looks to work. But there are things that I dont understand.
here the code which is a bit long because includes some comments:
Private Sub testdictionary()
Dim dict_in_sub As Scripting.Dictionary
' Do I really need this??:
'Set dict_in_sub = New Scripting.Dictionary
' or this:
'Set dict_in_sub =CreateObject("Scripting.Dictionary")
Call returning_dict_func("word1.word2")
Set dict_in_sub = returning_dict_func("whatever.second")
MsgBox "in code:" & Chr(10) & dict_in_sub("A") & Chr(10) & dict_in_sub("B")
End Sub
This is the function called:
Function returning_dict_func(SC As String) As Scripting.Dictionary
' should this function return a dictionary or an object or a scrippting dictionary??
Dim dictionary_in_function
Set dictionary_in_function = CreateObject("Scripting.Dictionary")
'take the first and second part of the string separated by the period
dictionary_in_function("A") = Mid(SC, 1, InStr(SC, ".") - 1)
dictionary_in_function("B") = Mid(SC, InStr(SC, ".") + 1, Len(SC))
MsgBox dictionary_in_function("A") & Chr(10) & dictionary_in_function("B")
Set returning_dict_func = dictionary_in_function
End Function
Even if I got the code running, there are a few things that make returning a dict from a function in VBA strange
Introducing the following changes into the code make it not working:
Dim dict_in_sub as Dictionary
' it does not work. What is the difference between dictionary and scrippting.dictionary?
' defining the function as returning a dictionary does not work
Function returning_dict_func(SC As String) As Dictionary
' if both are dictionaries it does not work
Dim dict_in_sub as Dictionary
Function returning_dict_func(SC As String) As Dictionary
Why is dictionary different than scripting.dictionary?
When should you initialize with
Set dict_modifiers = CreateObject("Scripting.Dictionary")
thanks
Note:
I had a look to:
a) VBA in Excel 2010: Scripting.Dictionary return value of a function won't pass as parameter to a wrapping function call
b) Returning dictionary from function in vba. Error 450
c) Returning an dictionary object from a function in VBA
The difference between CreateObject and New is briefly you need to reference a library with the object (Microsoft Scripting Runtime) for the latter (New), and you don't need to reference anything using CreateObject.
You can also have some intellisense hints when you use the latter definition, as the type is declared and known.
You can find more in another answer: What are the differences between using the New keyword and calling CreateObject in Excel VBA?
As for the Dictionary object, Scripting.Dictionary is the right type to use. Read more about it in https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/dictionary-object.
And by the way, in VBA you have to assign variables values using Set when the value is an object (usually something different than a number or String or Date).

Use Excel-VBA to create and Insert String/Text AND Image to Word Document table-cell

I tried since more days to create a Word Document with Excel-VBA
Step by Step:
first: create Word-Document and add a Table (Mailing-Label)
second: fill sometext into some cells. Works great!
Now my Problem:
at last, i want to append an Picture in the cell.
My Problem is, the Image RANGE clear the old text.
And i don't know, how to set the Image and the text at the end of the Loop.
My code
oDoc.Tables(1).Cell(zeile, spalte).Range.Text = "some string"
oDoc.Tables(1).Cell(zeile, spalte).Range.InlineShapes.AddPicture path_to_image
The way to understand what's happening is to think about how this would work if you were doing this manually, working with a selection. When you assign text to a Range that's like typing it in, as you'd expect. The second line of code, inserting the image, is like selecting the entire cell (in this case) then inserting the image: it replaces what's in the Range. When working manually, if you had selected the entire cell, you'd press Right Arrow or click at the end to put the focus after what had been typed.
The same principle applies when using a Range object: it needs to collapse in order to add something to it.
The following code example demonstrates this. It also highlights how the code can be made more efficient by assigning the table and the target range to objects.
Dim tbl As Word.Table 'or As Object if using late-binding
Dim rng As Word.Range 'or As Object if using late-binding
Dim chrCount As Long
Set tbl = oDoc.Tables(1)
Set rng = tbl.Cell(zeile, spalte).Range
rng.Text = "test"
chrCount = rng.Characters.Count
'Get the end of the cell content
Set rng = rng.Characters(chrCount - 1)
rng.Collapse wdCollapseEnd
rng.InlineShapes.AddPicture path_to_image
May be something like
Sub Test()
Dim Wrd As Word.Application
Dim oDoc As Word.Document
Set Wrd = CreateObject("Word.Application")
Wrd.Visible = True
Set oDoc = Wrd.Documents.Add
oDoc.Tables.Add oDoc.Range, 3, 3
zeile = 2
spalte = 2
path_to_image = "C:\Users\user\Desktop\Pull2.jpg"
oDoc.Tables(1).Cell(zeile, spalte).Range.Select
With Wrd.Selection
.TypeText Text:="some string"
.InlineShapes.AddPicture path_to_image
End With
End Sub

Excel VBA reference issues

I was following some examples online in which im trying to pass a value from one sub into a another sub in VBA but get the error:
Compile Error:
Procedure declaration does not match description of event or procedure having the same name.
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Call set_button_Click(nextsat)
End Sub
Sub set_button_Click(ByRef nextsat As String)
......
End Sub
Change the sub name in something else like SetButtonOnClick.
The _Click keyword is reserved by excel for the Click event on buttons if you have a button called with the same name.
You can't change the parameters for an event handler (except for the parameter name). That also means you can't add any parameters if none are expected. Not even Optional ByRef nextsat As String will work.
There are three ways to pass a value between event handlers in a UserForm:
Using a global variable (not recommended, ever);
Via the UserForm.tag property (recommended for simple values such as strings). Obviously cannot be used if it already has a permanent use;
Via one or more hidden controls (recommended for multiple or complex values as well as simple ones).
I've used the second method:
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = Weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Me.Tag = nextsat
End Sub
Sub set_button_Click()
Dim nextsat As String
nextsat = Me.Tag
......
End Sub
A better solution in your case might be to have a visible TextBox in which you store the calculated date when the user clicks next_sat, so that the user can see it. Then in your set_button handler, grab it from TextBox.Text.

vba sub insert text string into class object reference

I'm pretty new to this, so I'm sorry if I screw up any of the lingo. Thanks a bunch for anybody's help or thoughts.
I have the following wrong code:
Sub ExampleSub(text As String)
ClassObject." & text & "_attribute = 1
End Sub
So if I call ExampleSub("cheese"), I would like it to set ClassObject.cheese_attribute equal to 1.
Any thoughts? I'm not even sure it's possible.
Thanks so much!
Here is another method that might work. Use a scripting dictionary object as one of the classobject's Properties. The Dictionary Object is pretty neat, storing elements in key/value pairs, where the key is a string and the value can be any other type (object, range, Workbook, integer, variant/array, etc.)
So you can use the dictionary object to contain all of these named attributes. In your class module, add code like:
Private pAttributes as Object
Sub Class_Initialize()
'## Initialize this object to avoid a 91 error
Set pAttributes = CreateObject("Scripting.Dictionary")
End Sub
Public Property Get Attributes() As Object
Set Attributes = pAttributes
End Property
Public Property Let Attributes(lAttributes As Object)
Set pAttributes = lAttributes
End Property
Then, in your code you can simply do:
Sub ExampleSub(text As String)
ClassObject.Attributes(text) = 1
End Sub
Calling a dictionary key automatically adds the item if it doesn't already exist, but if you wanted more control you could do:
Sub AnotherExample(text as String)
If ClassObject.Attributes.Exists(text) Then
MsgBox text & " already exists!", vbInformation
Else:
ClassObject.Attributes(text) = 1
End If
End Sub

Resources