Today we’re going to take a brief dive into something I think is a really cool and underutilized ability of Powershell and that is leveraging dynamic parameters. A while back when I first got into GUI creation, I was creating individual functions to create various types of form objects. I mean, how else would you do it? Functions for buttons, textboxes, labels, etc. I quickly got sick of this and decided to start digging into a way I could create objects on the fly and still preserve my beloved IntelliSense. This is when I started messing with the ValidateSet parameter.
What this allows you to do is have a predefined set of options a user can select that will allow tab-completion when writing code. So if we can predefine certain parameters why can’t we just have something that changes the available properties based on a certain parameter choice, and not having to run a command that builds sub-functions on the fly or anything like that. Let me give you an example:
Lets say you need to create a form, button, and label object with particular properties that may or may not be valid for each object type. Why should you need to create an entire library of functions when you can just build 1 that creates your object and returns it right back to you. Like an Add-FormObject function. This is exactly what the use of dynamic parameters allows you to do. Lets take a look at what this could look like:
$Form1 = Add-FormObject -Type Form -Property Text, Size -Value "Sample Form", '350,250'
$Textbox = Add-FormObject -Type TextBox -Property Text, Location -Value "Sample Text", '5,15' -AddToForm $Form1
$Button = Add-FormObject -Type Button -Property Text, Location, FlatStyle, BackColor, ForeColor -Value "Submit", '30,35', "Flat", Blue, White -AddToForm $Form1
For comparison, if you were to manually create these this is what your code would look like:
$Form1 = New-Object System.Windows.Forms.Form
$Form1.Text = "Sample Form"
$Form1.Size = '350,250'
$Textbox = New-Object System.Windows.Forms.TextBox
$Textbox.Text = "Sample Text"
$Textbox.Location = '5,15'
$Form1.Controls.Add($Textbox)
$Button = New-Object System.Windows.Forms.Button
$Button.Text = "Submit"
$Button.Location = '30,35'
$Button.FlatStyle = "Flat"
$Button.BackColor = "Blue"
$Button.ForeColor = "White"
$Form1.Controls.Add($Button)
I mean come on, I know you weren’t expecting that. Usually you have many more than 3 form objects, so you can see how quickly you get hundreds of lines deep before even writing functions. Oh and don’t forget about writing your events too! You probably think our Add-FormObject function is thousands of lines at this point to account for all the different types of form objects. What if I told you it was less than 200?
By creating an initial parameter set of our different types of form objects we can then run Get-Member to pull all the available properties from that object. Hang on, how does it know what the properties are if the object doesn’t even exist yet? That’s the beauty of dynamic parameters. Therefore we can simply type: Add-FormObject -Type Button -Property <tab> and start tabbing through all the available properties specific to the Button object type. Need to create a different object such as a label? Just change your type and the Property parameter dynamically changes to the complete property set for a label object.
Let’s take a look at what this looks like:
DynamicParam{
if($Type){
#Create a new ParameterAttribute Object
$PropertyAttribute = New-Object System.Management.Automation.ParameterAttribute
$PropertyAttribute.Position = 2
$PropertyAttribute.Mandatory = $false
#Create an AttributeCollection object for the attribute we just created
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
#Add our custom attribute
$AttributeCollection.Add($PropertyAttribute)
#Create specified object
$CreatedObject = New-Object System.Windows.Forms.$Type
#Find all available properties for specified object type
$Properties = ($CreatedObject | Get-Member -MemberType Properties).Name
$AttributeCollection.Add((New-Object System.Management.Automation.ValidateSetAttribute($Properties)))
#Add our parameter specifying the attribute collection
$PropertyParam = New-Object System.Management.Automation.RuntimeDefinedParameter('Property', [string[]], $AttributeCollection)
#Expose the name of our parameter
$ParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$ParamDictionary.Add('Property', $PropertyParam)
$ParamDictionary
}
}
Begin{
#Assign property parameter to a variable if it was provided
if($PSBoundParameters.Property){
$Property = $PSBoundParameters.Property
}
}
Process{
if($Property){
#Generate property hashtable
$Count = 0
$ObjectProperties = @{}
foreach($P in $Property){
$ObjectProperties[$P] = $Value.item($Count)
$Count++
}
#Add properties to newly created object
foreach($Property in $ObjectProperties.Keys){
$CreatedObject.$Property = $ObjectProperties[$Property]
}
}
So as you can see, rather than having 1 Property parameter, we have as many as we need. Any time a Type is specified the function is smart enough to know that it needs to immediately generate a Property parameter and make it available to you while you code. This is how it generates a unique list for each form object type. Then simply match up your properties with it’s values in a hashtable and you can generate pretty much any type of object with a single function. Pretty cool!
Now that you’ve seen a little bit of what you can do with dynamic parameters what sort of ideas do you have? I’d love to hear about it, feel free to contact me and share your thoughts!