VBS - Create object and pass that through functions and subs? - arrays

I'm trying something out. I've got this system where a bunch of scripts are triggered at certain event handlers. This is used during the creation of Users in Active Directory.
So, what I think I need is a function that basically collects the properties of the user being processed into - hopefully, an object. And then returning that object and using it in other functions and subs. So that I can perhaps reach the properties I want using only "myObject.firstname" and that's it.
How can I create an object, create and set values in it and returning the entire object? Is this possible in VBS?
Edit: What would be best if I could create an Object that holds these properties and just being able to access them whenever I need to.
I'm basically after something like this:
Public Sub Main()
Set userStream = New objUser
msgbox objUser.firstname
msgbox objUser.lastname
msgbox objUser.username
End Sub
Edit: To explain furter, I have this library script which is called 'libGetUserProperties'
It's in this I want the function and Class. Then I just import this library script in the actual script that is run at runtime. Looks like this:
libGetUserProperties:
Public Function getobjUser(ByRef Request, ByVal From)
Dim thisUserObject
Set thisUserObject = New objUser
'If type = 0, data comes from AD
If From = 0 Then
thisUserObject.firstname = "some string"
thisUserObject.lastname = "some string"
End If
'If type = 1, data comes from Request stream
If From = 1 Then
thisUserObject.firstname = "some string"
thisUserObject.lastname = "some string"
End If
Set getobjUser = thisUserObject
End Function
Class objUser
Public firstname
Public lastname
Public username
End Class'
This is what the actual runtime Script looks like:
Dim libGetUserProperties
Set libGetUserProperties = ScriptLib.Load("Script Modules/Library Scripts/libGetUserProperties")
Sub onPreCreate(Request)
Dim userStream
Set userStream = New objUser
'Do whatever with objUser which contains all of the properties I'm after
End Sub

That’s not possible using VBScript, unless you want to dynamically generate the code for a class, including the properties, save that to file, and then execute that file as a script.
This is so much easier to do using JScript. Since Windows Scripting can handle JScript as well as VBScript, I’d recommend going that way.
If you definitely need VBScript, the best you can go for is a Scripting.Dictionary that would contain the properties as keys:
Dim dicUser
dicUser = CreateObject("Scripting.Dictionary")
dicUser("FirstName") = objUser.FirstName
which would allow you to access the first name as follows:
dicUser("FirstName")
Update
If you want to write a function which creates a dictionary and returns that, this is how to do it in VBScript:
Function RememberTheUser(Request)
Dim dicResult
Set dicResult = CreateObject("Scripting.Dictionary")
dicResult("FirstName") = Request.GetTheFirstName
Set RememberTheUser = dicResult
End Function
Which can then be assigned to some other variable:
Dim dicUser ' in the main section of the script, this means it's a global variable
Sub onPreCreate(Request)
' ...
Set dicUser = RememberTheUser(Request)
' ...
End Sub
Sub onSomethingLater()
WScript.Echo dicUser("FirstName") ' It's still accessible here
End Sub
In JScript, it’s a lot easier: just create a new object, and tack on new properties.
function readObject(dataSource) {
var result = {};
result.source = dataSource;
result.firstName = dataSource.firstName;
result['lastName'] = dataSource.FunctionThatGetsLastName();
}
Note that in JScript, object properties can be accessed using the object.property notation, but also through the object['property'] notation, which is very useful when you have dynamically-named properties. ('property' in the second notation is a string, and can be replaced by any expression that returns a string).

Keeping with VBScript, a disconnected recordset would work as well and is possibly faster than a dictionary; see here for an example.

Related

How can I store an array in my.settings in vb.net

I am trying to store a user input array in a my.settings variable in vb.net. I would like for the user to enter an array in the form {1,2,3}, store that as a string in the setting and then be able to use the setting value later to create a new array. The code will be something like:
Dim inputarray()
Dim outputarray()
inputarray=textbox1.text
my.settings.inputstoredarray.add(inputarray)
outputarray=my.settings.inputstoredarray
textbox2.text=outputarray(0)
'If the user types "{1,2,3}' in textbox1, textbox2 should show "1"
I have tried multiple versions of this but there seem to always be type conversion errors. I don't understand why it works if I hardcode:
inputarray={1,2,3}
and yet the below code does not work:
inputarray=my.settings.inputstoredarray
How can I store a user provided array in my.settings and retrieve it for use later?
does not work even if I go into settings and set the string value for the setting to {1,2,3}
Set up your setting in Project Properties, Settings tab as follows.
Then save the setting as follows.
Private Sub SaveStringToSettings()
My.Settings.StringOfInts = TextBox1.Text 'User types in {1, 2, 3}
End Sub
To retrieve the setting and turn it into an array
Private Sub CreateArrayFromSettings()
Dim SettingValue = My.Settings.StringOfInts
Debug.Print(SettingValue) 'View this in the Immediate window
'Get rid of the braces
Dim TrimmedString = SettingValue.Trim({"{"c, "}"c})
'Split the string by the commas into an array
Dim Splits = TrimmedString.Split(","c)
'Get rid of the spaces
For i = 0 To Splits.Length - 1
Splits(i) = Splits(i).Trim
Next
TextBox1.Text = Splits(0) 'Displays 1
End Sub
You can browse for additional setting types in your Project properties/Settings tab but you'll find it fairly limited with the most common message being that you can't use that type. Array is one you can't use, I'm afraid.
But you can use the fully-supported StringCollection:
Dim saveTest As New StringCollection() From {"1", "2", "3", "4"}
My.Settings.MySetting = saveTest
My.Settings.Save()
Dim loadTest As StringCollection = My.Settings.MySetting
Another option would be to use, say, and XML or JSON string that you de-serialize but that's getting a bit involved.
You can't store an array in application settings. What you can do is create a setting of type StringCollection. You can then use it as a StringCollection in code or you can transfer the data back and forth between the StringCollection and an array if you really need an array.
Start by opening the Settings page of the project properties and creating a new setting of type System.Collections.Specialized.StringCollection. For this example, I'll name it MyStringCollection but you should name it appropriately for your app. When you do this, notice that the Value field is empty by default. This means that the setting is Nothing by default. That's OK but it means that you need to actually create the collection object in code before using it for the first time, e.g.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Settings.MyStringCollection Is Nothing Then
My.Settings.MyStringCollection = New StringCollection
End If
'Use My.Settings.MyStringCollection here.
End Sub
Alternatively, you can force the settings UI to create the object for you. To do that, select the Value field for that setting and click the browse (...) button. Add any arbitrary character and click the OK button. Notice that the Value field is populated with an XML snippet that contains the text you entered. Click the browse (...) button again, delete the text and click OK again. Notice that the XML remains, even though the text you had entered is removed. That XML will now create the StringCollection object automatically, so you don't need code to do so.
If you're happy wiith a collection of String values then you can use the setting directly in code wherever you like. It works, for the most part, like a List(Of String), allowing you to add and remove items at will. If you specifically need an array or you need a type other than String then you will have to do some translation, e.g.
'Load the collection items into a String array.
Dim myStringArray = My.Settings.MyStringCollection.Cast(Of String)().ToArray()
'Load the collection items into an Integer array.
Dim myIntegerArray = My.Settings.MyStringCollection.Cast(Of String)().Select(Function(s) CInt(s)).ToArray()
'Repopulate the collection from a String array.
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(myStringArray)
'Repopulate the collection from an Integer array.
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(myIntegerArray.Select(Function(n) n.ToString()).ToArray())
If you want to display the contents of your collection in a TextBox than you might do something like this:
TextBox1.Text = String.Join(",", My.Settings.MyStringCollection.Cast(Of String)())
That will create a single, comma-delimited String containing all the items. To repopulate the collection from a TextBox containing such text, do this:
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(TextBox1.Text.Split(","c))

public array loaded from useform vba

Iam creating a macro, and I need to access the array that is created and filled in useform (button_click action) in module.
Private Sub CommandButton1_Click()
Dim tmojo As Worksheet
Dim mojocell As Range
Set tmojo = Sheets("table mojo")
colls = tmojo.Range("N1").End(xlToLeft).Column
i = 1
For Each cCont In Me.Controls
If TypeName(cCont) = "ComboBox" Then
If cCont.Enabled = True Then
If cCont.Value = Empty Then
MsgBox "you havent specified all the columns"
Exit Sub
End If
ReDim Preserve collname(i)
collname(i) = cCont.Value
i = i + 1
End If
End If
Next cCont
Call createregion
End Sub
I fill the array collname with values from multiple comboboxes (column names). Then I want to call createregion sub which is located in module and I want to access the values in collname().
im getting error saying that:
Constants, fixed-length strings, arrays, user-defined types, and Declare statements not allowed as Public members of an object module
I need to access this array in multiple subs, is there any workaround?
Thank you in forehand.
The proper way is to transfer your UserForm code into a regular module, and declare your array as public, on the top of the module, before every subs :
Public MyArr()
When you transfer your code, you will need to call the subs into your UserForm's events, and so change all the Me and Me. to the full name of your UserForm.
And if you are lacking time, you can simply declare on the top of the UserForm module :
Dim MyArr()

VBScript not returning an array through a class

I just inherited an old project to maintain using asp and vbs. In vbs it passes back a single object and i need to make this into an array and everything I have tried ends up giving me a critical exception. I have my SQL read going from a single record into an array but I can't pass it back to my main object where I need it and nobody on my project knows vbs.
Class LoadFileBS
Public Function getDocument(req, userProfile)
Dim dao
Set dao = new FileDAO 'define my class
Dim document
Set document = dao.retrieve(fileId) 'this was a single return that i'm trying to make into an array
If (checkAuthorization(document(1)) = true) Then 'tried making document an array and that didn't work for me. The (1) was a test to just try and get the array working in this class and not meant as finalized code.
Class FileDAO
Public Function Retrieve(fileId)
'Run the query and return the results
Set Retrieve = runDocumentQuery(fileId)
End Function
Private Function runDocumentQuery(fileId)
... 'making SQL call and it returns and i fill document with the info.
Dim document
Set document = new DocumentTO
Call document.setFileId(Trim(rs.fields(SQL_FILE_ID).value))
...
Set documents(UBound(documents)) = document
rs.MoveNext
If (NOT rs.Eof) Then
ReDim Preserve documents(UBound(documents) + 1)
End If
... 'now try and return
If (isObject(documents(1))) Then
Set runDocumentQuery = documents '<-- array with multiple records
Else
Set runDocumentQuery = nothing
End If
End Function
Any help or tips would be appreciated.
Thanks,
Josh
Arrays aren't objects, so you don't use the Set statement. They can contains object references, however.
If (isObject(documents(1))) Then
runDocumentQuery = documents ' Return the array (removed "Set" here)
Else
runDocumentQuery = Array() ' Return empty array? Up to you.
End If
Then you'll need to do the same for your Retrieve function:
Public Function Retrieve(fileId)
'Run the query and return the results
Retrieve = runDocumentQuery(fileId) ' (Removed "Set" here)
End Function
And, finally, back to your original call:
Dim document
document = dao.retrieve(fileId) ' (Removed "Set" here)

Reference a cell in another workbook. And make my query not refresh if values are equal

It says I have too few parameters expected 15... It is something with my before and after refresh.
Class 1
Public WithEvents qt As QueryTable
Private Sub qt_AfterRefresh(ByVal Success As Boolean)
Application.Worksheets("RawDataLines").Range("A1") = Application.Worksheets("RawDataLines").Range("C1")
Application.Run "'Operation Get Ipads.xls'!Assembly1_Button"
End Sub
Class 2
Public WithEvents qut As QueryTable
Private Sub qut_BeforeRefresh(Cancel As Boolean)
Worksheets("RawDataLines").Range("C1") = _
"='H:\Departments\Manufacturing\Production Links\DashBoard Breakdown\[MASTER_LIVE_STATUS_DATA.xls]Sheet1'!R1C1"
If Application.Worksheets("RawDataLines").Range("C1") = Application.Worksheets("RawDataLines").Range("A1") Then
Cancel = True
End If
End Sub
Initialize:
Dim T As New Class1
Dim H As New Class2
Sub Initialize_It()
Set T.qt = ThisWorkbook.Sheets(3).QueryTables(1)
Set H.qut = ThisWorkbook.Sheets(3).QueryTables(1)
End Sub
Private Sub qt_BeforeRefresh(ByVal Success As Boolean)
the argument is usually Cancel and this is what your code refers to. Change it to Cancel.
You can refer to the current workbook (where this code is running) as ThisWorkbook. Assuming it is the MASTER one, then you can use:
ThisWorkbook.Worksheets("sheet1").Range("A1")
The Workbooks collection refers only to open workbooks, so you will need to temporarily open the other workbook:
Dim wbOther As Workbook
Set wbOther = Workbooks.Open("full path and filename.xlsx", False)
the False argument indicates that you do not wish to Update Links when opening the book (if appropriate).
When you have finished with the other book, use:
wb.Close False 'False says that you do not need to Save Changes
Set wb = Nothing
It is possible to get a single value from another workbook without opening and closing it, but if that book also has links this may cause an issue:
Debug.Print ExecuteExcel4Macro("'F:\Documents and Settings\student\My Documents\[AndysData7.xlsx]Staff List'!R6C4")
Note, the formula needs to use R1C1 notation. (Rather than Debug.Print you would store the value in a variable.) Note also that I am not necessarily recommending this approach, as it is undocumented, but thought I'd mention it in the context of the question.

How to have a global Dictionary in VB.NET/WPF application to save data from different windows?

I am new to VB.NET and WPF.
I am building a "Questionnaire" app. Users will be presented sequentially with different questions/tasks (windows). After they respond on each question/task and press a "submit" button a new window will open with a new question/task, and previous window will close. After each question, when the button is pressed, I need to store data to some global object. After all questions are answered the data of this object should be written out to the output file.
I figured out that Dictionary will be the best to store the results after each window.
I am not sure how, where to create this global Dictionary and how to access it. Should I use View Model? If yes, can you give an example? Or, should it be just a simple class with shared property? (something like this)
EDIT 2: I tried many different ways recommended online
GlobalModule:
Module GlobalModule
Public Foo As String
End Module
GlobalVariables:
Public Class GlobalVariables
Public Shared UserName As String = "Tim Johnson"
Public Shared UserAge As Integer = 39
End Class
Global properties:
Public Class Globals
Public Shared Property One As String
Get
Return TryCast(Application.Current.Properties("One"), String)
End Get
Set(ByVal value As String)
Application.Current.Properties("One") = value
End Set
End Property
Public Shared Property Two As Integer
Get
Return Convert.ToInt32(Application.Current.Properties("Two"))
End Get
Set(ByVal value As Integer)
Application.Current.Properties("Two") = value
End Set
End Property
End Class
Here is where I save the data to global variables/properties in the first window. I need to store data in this subroutine before closing an old window and opening a new window. I use MessageBox just for testing.
Private Sub btnEnter_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnEnter.Click
Dim instructionWindow As InstructionsWindow
instructionWindow = New InstructionsWindow()
Application.Current.Properties("number") = textBoxValue.Text
Globals.One = "2"
Globals.Two = 3
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
GlobalVariables.UserName = "Viktor"
GlobalVariables.UserAge = 34
GlobalModule.Foo = "Test Foo"
'testing if it saved tha value
'MessageBox.Show(Application.Current.Properties("number"))
Application.Current.MainWindow.Close()
instructionWindow.ShowDialog()
End Sub
Next subroutine is where I am trying to retrieve the value from global Properties/variables in the second window, but message boxes come out empty. There might also the case that I am assigning values in a wrong way, or not reading them in a right way (casting?) :
Private Sub FlowDocReader_Initialized(ByVal sender As Object, ByVal e As System.EventArgs) Handles FlowDocReader.Initialized
' Get a reference to the Application base class instance.
Dim currentApplication As Application = Application.Current
MessageBox.Show(currentApplication.Properties("number"))
MessageBox.Show("One = " & Globals.One & " Two = " & Globals.Two)
MessageBox.Show("GlobalVariables: UserName=" & GlobalVariables.UserName & " UserAge=" & GlobalVariables.UserAge)
MessageBox.Show("GlobalModule.Foo = " & GlobalModule.Foo)
Dim filename As String = My.Computer.FileSystem.CurrentDirectory & "\instructions.txt"
Dim paragraph As Paragraph = New Paragraph()
paragraph.Inlines.Add(System.IO.File.ReadAllText(filename))
Dim document As FlowDocument = New FlowDocument(paragraph)
FlowDocReader.Document = document
End Sub
Thanks.
You can make public Dictionary property for form and put your dictionry to this property or make constructor with Dictionary argument.
You already have this dictionary Application.Properties
Look here, please.
First, you can define a dictionary (list of lists) as follows at the beginning of a form or in a module
Dim dic As New Dictionary(Of String, List(Of String))
As the user completes questions on a form, write the partucular form number and query results to a single record in the dic before going to the next form (place this code into the "Next" button):
'Assume q1response=3, q2response=4,..., qpresponse="text", etc.
Dim myValues As New List(Of String)
myValues.Add(formname)
myValues.Add(q1response)
myValues.Add(q2response)
.
.
myValues.Add(qpresponse)
dic.Add(username, myValues)
When a user is done, there will be multiple records in the dictionary, each of which starts with their name and is followed by question responses. You can loop through multiple dictionary records, where each record is for a user using the following:
For Each DictionaryEntry In dic 'this loops through dic entries
Dim str As List(Of String) = DictionaryEntry.Value
'here you can do whatever you want with results while you read through dic records
'username will be = str(0)
'formname will be str(1)
'q1 response on "formname" will be str(2)
'q2 response on "formname" will be str(3)
'q3 response on "formname" will be str(4)
...
Next
The trick is that there will be multiple dictionary records with results for one user, where record one can have results like "John Doe,page1,q1,q2,q3" and record 2 will be "John Doe,page2,q4,q5,q6." Specifically, the "str" in the above loop will be an array of string data containing all the items within each dictionary record, that is, in str(0), str(1), str(2),... This is the information you need to work with or move, save, analyze, etc.
You can always put all the code I provided in a class (which will be independent of any form) and dimension the sic is a Sub New in this class, with the updating .Add values lines in their own sub in this same class). Then just Dim Updater As New MyNewClassName. Call the Updater in each continue button using Call Updater.SubNameWithAddValues(q1,q2,...qp). It won't matter where you are in your program since you using a specific class. The one thing I noticed with my code is that you can only use the line that adds the "key" or the username once, so use it after the last query -so put it in a Sub Finished in your new class and call as Call Updater.Finished(username,q30,q31,last)

Resources