I needed an easy way to create forms dynamically on the fly in PowerShell. As usual, I looked to Google, more out of sheer laziness than desperation. Being a self-proclaimed power-searcher I thought I'd have it licked in no time. So many generous coders out there willing to share their secrets and show off their 'mad skillz'.
Unsurprisingly I was able to find tonnes of useful information, however, much to my surprise, after poking around for the good part of an hour I did not find what I was looking for, nor did my attempts to hack it out on the command line bear any fruit.
So I threw in the search towel (which is a hard thing for a self-proclaimed 'power-seacher' to admit) and put on my under-used spinner hat and started to create a new function. After diving in and out of it for a week, and quite a few lightbulb moments, I created exactly what I was looking for. And what better way to celebrate than leaving the cocoon I entered as a 'code leech' to emerge as one of the more admiral sharers of this wonderful World-Wide-Web.
So here it is, my first instalment on this newfangled blog thingamajiggy....
<#
FUNCTION : Flexible dynamic form control populator.
CREATED BY : Brendan DeCelis
DATE : 15/05/2012
VERSION : v0.3
LAST UPDATE: Brendan DeCelis, 21/05/2012
USAGE : The function "Test-Form" loads a very straightforward sample form using simply a
direct text string. It's unlikely you would bother going to the effort for
this usage, but it is an easy to follow example.
The function "Test-DynamicForm" shows how this might be applied to create a
form using variables as the input to creating the form. This is where the
use for this script comes into its own.
The usage is documented extensively within each function.
LICENSE : May be used and distributed freely, with acknowledgement to the creator under
GNU General Public License v3.0. See http://www.gnu.org/licenses/ gpl.html
DISCLAIMER : These functions are provided "AS IS" and "WITH ALL FAULTS,"
without warranty of any kind, including without limitation the warranties
of merchantability, fitness for a particular purpose and non-infringement.
The creator makes no warranty that these functions are free of defects or
is suitable for any particular purpose. In no event shall the creator be
responsible for loss or damages arising from the installation or use of
these functions, including but not limited to any indirect, punitive, special,
incidental or consequential damages of any character including, without
limitation, damages for loss of goodwill, work stoppage, computer failure
or malfunction, or any and all other commercial damages or losses. The entire
risk as to the quality and performance of the Software is borne by you.
Should any part of these functions prove defective, you and not the creator
assume the entire cost of any service and repair.
UPDATE 1 : 21/05/2012 - Updated to include "noResize" option to prevent auto-resizing of
the form. Particularly handy if using to update an existing form
UPDATE 2 : 21/05/2012 - Updated to allow form variable to be passed as a string and new form
#>
#region Main Function - populate form from string
function Populate-FormFromString
{
<#
NB This will populate an existing form.
You will need to create the form previous to calling this function, and display the form dialog outside of this function.
You can do this be creating a wrapping function, such as:
> $testForm = New-Object System.Windows.Forms.Form
> Populate-FormFromString $testForm "CheckBox||chkNewChk1||This is not checked - vanilla;;`
> $testForm.ShowDialog()
The controls are separated by a double-delimiter.
A custom function called "Split-MultiCharDelimiter" has been defined to process this delimiter and must be available to this function.
Controls to add input format:
Each control is separated by the delimiter ";;"
Each control varaiable is separated by "||"
The 4 variables (in order) that can be defined are:
Control Type - Mandatory, can be a Checkbox, Button, Label, TextBox or potentially any other label (not tested)
Control Name - The name to give the
Each action or control variable separated by "^^",
action / variable type and action / variable value separated by "=="
Type | Name | DefaultText/Title | Enabled/Checked/Codeblock
ie. Populate-FormFromString "myForm" "CheckBox||chkMyNewCheckbox|| This is a checkbox||Checked==`$true"
Populate-FormFromString "frmADSIUser" "CheckBox||chkNewChk1||This is not checked - vanilla;;`
The form will automatically resize to the outer dimensions of the controls added by the function
UNLESS the optional switch -noResize is explicitly declared
#>
Param
(
#[Parameter(Mandatory = $true, Position = 0)][System.Windows.Forms.Form] $thisForm,
[Parameter(Mandatory = $true, Position = 0)] [string] $thisFormVariable,
[Parameter(Mandatory = $true, Position = 1)] [string] $controlsToAdd,
[Parameter(Mandatory = $false, Position = 2)]
[ValidateSet("Horizontal", "Vertical")] [string] $controlDirection = "Vertical",
[Parameter(Mandatory = $false, Position = 3)] [int] $controlsPerDimension = 10,
[Parameter(Mandatory = $false, Position = 4)] [int] $OriginX = 30,
[Parameter(Mandatory = $false, Position = 5)] [int] $OriginY = 30,
[Parameter(Mandatory = $false, Position = 6)] [int] $vertControlSpacing = 30,
[Parameter(Mandatory = $false, Position = 7)] [int] $horzControlSpacing = 80,
[Parameter(Mandatory = $false)] [switch] $noResize,
[Parameter(Mandatory = $false)] [switch] $showForm
)
Process
{
# If the form has not yet been created, create using black magic
[bool] $global:formExists = $true
#[string] $formCheck = "if (`$$thisFormVariable -eq `$null){`$$thisFormVariable = new-object System.Windows.Forms.Form;`$ createForm = `$executioncontext. InvokeCommand.NewScriptBlock(` $codeForForm);Invoke-Command `$createForm;}"
[string] $formCheckString = "if (`$$thisFormVariable -eq `$null){`$global:formExists = `$false}"
$formCheck = $executioncontext.InvokeComman d.NewScriptBlock($ formCheckString)
Invoke-Command $formCheck
if ($formExists -eq $false)
{
$codeForForm = "`$$thisFormVariable = new-object System.Windows.Forms.Form"
$createForm = $executioncontext.InvokeComman d.NewScriptBlock($codeForForm)
Invoke-Command $createForm
}
# Split controls if multiple are defined
$arrControls = @()
$arrControls = Split-MultiCharDelimiter "$controlsToAdd" ";;"
# Account for existing controls that have been added and set start position accordingly
$controlCount = 0
$stackCount = 0
$horzStartPos = $OriginX
$vertStartPos = $OriginY
[int] $maxHeight = 0
[int] $maxWidth = 0
foreach($checkControl in $thisForm.Controls)
{
# Check to see if the control was created by this function
if ($checkControl. AccessibleDescription -eq "DynamixControl")
{
$controlCount++
$stackCount++
if ($controlDirection -eq "Horizontal")
{
if ($stackCount -eq $controlsPerDimension)
{
}
else
{
}
}
else
{
if ($stackCount -eq $controlsPerDimension)
{
}
else
{
}
}
}
#check for maximum height and width of form
if (($OriginX + $horzControlSpacing) -gt $maxWidth)
{
$maxWidth = ($OriginX + $horzControlSpacing)
}
if (($OriginY + $vertControlSpacing) -gt $maxHeight)
{
$maxHeight = ($OriginY + $vertControlSpacing)
}
}
foreach($control in $arrControls)
{
$arrControlProperties = Split-MultiCharDelimiter "$control" "||"
$controlParameterCount = $arrControlProperties. GetUpperBound(0)
$controlToCreate = $arrControlProperties[0]. ToString()
#Replace any funky characters in the control type, which may be created by formatting as per the example in Load-TestForm
$controlToCreate = $controlToCreate -Replace('[^\w\.]','')
$codeForControl = "`$global:newControl = New-Object System.Windows.Forms.$ controlToCreate"
$createControl = $executioncontext.InvokeComman d.NewScriptBlock($ codeForControl)
Invoke-Command $createControl
$newcontrol. AccessibleDescription = "DynamixControl"
#The second part of the control string contains the name of the control object
if ($controlParameterCount -ge 1)
{
$newControl.Name = $arrControlProperties[1]. ToString().Replace(" ","")
# The third part of the control string contains the text property of the control
if ($controlParameterCount -ge 2)
{
$newControl.Text = $arrControlProperties[2]
# The fourth part of the object contains any additional values with
# property name and value separated by "==" and additional properties separated by "^^"
if ($controlParameterCount -ge 3)
{
{
}
}
else
{
$newControl.Text = "$($arrControlProperties[1])"
}
}
else
{
$newControl.Name = "control$controlCount"
$newControl.Text = "control$controlCount"
}
$newControl.Location = New-Object System.Drawing.Point($OriginX, $OriginY)
$newControl.AutoSize = $true
$newControl.TabIndex = $controlCount
$thisForm.Controls.Add($ newControl)
$controlCount++
$stackCount++
if ($controlDirection -eq "Horizontal")
{
if ($stackCount -eq $controlsPerDimension)
{
$OriginX = $horzStartPos
$OriginY += $vertControlSpacing
$stackCount = 0
}
else
{
$OriginX += $horzControlSpacing
}
}
else
{
if ($stackCount -eq $controlsPerDimension)
{
$OriginY = $vertStartPos
$OriginX += $horzControlSpacing
$stackCount = 0
}
else
{
$OriginY += $vertControlSpacing
}
}
#check for maximum height and width
if (($OriginX + $horzControlSpacing) -gt $maxWidth)
{
$maxWidth = ($OriginX + $horzControlSpacing)
}
if (($OriginY + $vertControlSpacing) -gt $maxHeight)
{
$maxHeight = ($OriginY + $vertControlSpacing)
}
}
# Scale the form to fit controls
if (!$noResize)
{
if ($thisForm.Width -lt $maxWidth)
{
$thisForm.Width = $maxWidth
}
if ($thisForm.Height -lt $maxHeight)
{
$thisForm.Height = $maxHeight + $vertControlSpacing + $vertStartPos
}
}
if ($showForm)
{
$thisForm.ShowDialogue()
}
}
}
#endregion
#region Custom split function
function Split-MultiCharDelimiter
{
Param
(
[Parameter(Mandatory = $true, Position = 0)] [string] $originalString,
[Parameter(Mandatory = $true, Position = 1)] [char[]] $delimiterChars
)
[array] $arrMySplit = @()
[int] $charsInDelimiter = $delimiterChars.Count
if ($charsInDelimiter -eq 1)
{
$arrMySplit = $originalString.Split($ delimiterChars)
return $arrMySplit
}
else
{
[int] $startChar = 1
[int] $currChar = 1
[Char[]] $originalChars = $originalString
[int] $charsToProcess = $originalChars.Count
[bool] $passedTest = $true
[int] $charsToCount = 1
# Step through array and identify empty entries.
# Starts at LowerBound + 1 to prevent the script from trying to access a record lower than the lower bound
for ($char = $startChar; $char -le $charsToProcess - 1; $char++)
{
[char[]] $compareChar = ""
[int] $delimCount = 0
for ($delimiterChar = $currChar; $delimiterChar -le ($currChar + $charsInDelimiter - 1); $delimiterChar++)
{
#$compareChar += $originalChars[$delimiterChar]
if ($originalChars[$delimiterChar ] -ne $delimiterChars[$delimCount])
{
$passedTest = $false
#$charsToCount++
}
}
#if ($compareChar -eq $delimiterChars)
if ($passedTest -eq $true)
{
#Write-Host "Found it at $currChar"
$arrMySplit += $originalString.Substring($ startChar-1,$charsToCount)
$startChar = $currChar + $charsInDelimiter + 1
$currChar = $currChar + $charsInDelimiter
$charsToCount = 0
}
else
{
$currChar++
$charsToCount++
}
[bool] $passedTest = $true
}
}
if (([int]$remaining = $originalString.Length - $startChar + 1) -gt $charsInDelimiter)
{
$arrMySplit += $originalString.Substring($ startChar-1,$remaining)
}
return $arrMySplit
}
#endregion
#region Test functions
function Test-Form
{
# Create new instance of a form
$testForm = New-Object System.Windows.Forms.Form
# Populate form using a direct string to define the controls
Populate-FormFromString "testForm" "CheckBox||chkNewChk1||This is not checked - vanilla;;`
Button||btnClose||Click My Red Spot||add_Click==`$testForm.
# Show the form
$testForm.ShowDialog()
}
function Test-Form-WithoutExistingForm
{
# Populate form using a direct string to define the controls, creating a new form from the populate function
Populate-FormFromString "testForm" "CheckBox||chkNewChk1||This is not checked - vanilla;;`
Button||btnClose||Click My Red Spot||add_Click==`$testForm. Dispose()^^ForeColor==`"Red`""
# Show the form
$testForm.ShowDialog()
}
function Test-DynamicForm
{
# Create new instance of a form and provide a title
$testForm = New-Object System.Windows.Forms.Form
$testForm.Text = "Testing dynamic form"
#region Test Button - Comment this out to remove pesky button on test dynamic form
# add a button on the form to show that it does not get counted as a dymaic control
$testbutton = New-Object System.Windows.Forms.Button
$testbutton.Text = "Not Dynamic"
$testbutton.Location = New-Object System.Drawing.Point(50, 70)
$testForm.Controls.Add($ testbutton)
#endregion
# Populate form with checkboxes named incrementally
for ($i=1;$i -le 40;$i++)
{
[string] $controlString = "CheckBox||chkNewChk$i|| Checkbox #$i"
#set every even box to checked and make the text red
if ([bool]!($i%2))
{
$controlString += "||Checked==`$true^^Forecolor= =`"Red`""
}
#set every odd box to blue, align to middle left and add a click action which disables and enables the maximise box on the form
else
{
$controlString += "||TextAlign==`"MiddleLeft`"^^ Forecolor==`"Blue`"^^add_ Click==if(`$testForm. MaximizeBox -eq `$true){`$testForm.MaximizeBox = `$false}else{`$testForm. MaximizeBox = `$true}"
}
Populate-FormFromString
"testForm" $controlString -horzControlSpacing 120
}
# Show the form
$testForm.ShowDialog()
}
#endregion
No comments:
Post a Comment