Using variables for events on dynamically generated control - forms

I have a function Add-TextBox, which creates a TextBox control, and assigns a block to it's event handler.
The problem I am facing is that the block is presumably being called with global namespace, and the variables declared inside the Add-TextBox function aren't accessible to it. How do I make them accessible?
Edit: added full code
Add-Type -AssemblyName System.Windows.Forms
Function Add-Control() {
Param (
[System.Windows.Forms.Form]$Form,
[string]$ControlType,
[System.Windows.Forms.Control]$AfterControl = $null,
[int]$Padding = 0
)
$control = New-Object System.Windows.Forms.$ControlType
$control.AutoSize = $true
$x = 5
$y = 5
if ($AfterControl) {
$y = $AfterControl.Location.Y + $AfterControl.Size.Height + $Padding
}
$control.Location = "5,$y"
$form.Controls.Add($control)
return $control
}
Function Add-TextBox() {
Param (
[System.Windows.Forms.Form]$Form,
[string]$Placeholder = "",
[System.Windows.Forms.Control]$AfterControl = $null,
[int]$Padding = 0
)
$control = Add-Control -Form $Form -ControlType "TextBox" -AfterControl $AfterControl -Padding $Padding
$control.Add_GotFocus({
Write-Host "Placeholder is null: $($Placeholder -eq $null)"
if ($this.Text -eq $Placeholder) {
$this.ForeColor = "Black"
$this.Text = ""
}
})
$control.Add_LostFocus({
if ($this.Text -eq "") {
$this.ForeColor = "Darkgray"
$this.Text = $Placeholder
}
})
return $control
}
$form = New-Object system.Windows.Forms.Form
$textbox = Add-TextBox -Form $form -Placeholder "(XXXXXX, npr. 012345)"
$form.ShowDialog()

Seems I can use .GetNewClosure() on script blocks to capture the local variables and retain them.
Add-Type -AssemblyName System.Windows.Forms
Function Add-Control() {
Param (
[System.Windows.Forms.Form]$Form,
[string]$ControlType,
[System.Windows.Forms.Control]$AfterControl = $null,
[int]$Padding = 0
)
$control = New-Object System.Windows.Forms.$ControlType
$control.AutoSize = $true
$x = 5
$y = 5
if ($AfterControl) {
$y = $AfterControl.Location.Y + $AfterControl.Size.Height + $Padding
}
$control.Location = "5,$y"
$form.Controls.Add($control)
return $control
}
Function Add-TextBox() {
Param (
[System.Windows.Forms.Form]$Form,
[string]$Placeholder = "",
[System.Windows.Forms.Control]$AfterControl = $null,
[int]$Padding = 0
)
$control = Add-Control -Form $Form -ControlType "TextBox" -AfterControl $AfterControl -Padding $Padding
$control.Add_GotFocus({
if ($this.Text -eq $Placeholder) {
$this.ForeColor = "Black"
$this.Text = ""
}
}.GetNewClosure()) # Here...
$control.Add_LostFocus({
if ($this.Text -eq "") {
$this.ForeColor = "Darkgray"
$this.Text = $Placeholder
}
}.GetNewClosure()) # And here.
return $control
}
$form = New-Object system.Windows.Forms.Form
$textbox = Add-TextBox -Form $form -Placeholder "(XXXXXX, npr. 012345)"
$form.ShowDialog()

Related

How do you create an Outlook Appointment / Meeting in PowerShell?

How do you create an Outlook Appointment / Meeting in PowerShell?
The following code will create an Outlook Appointment in PowerShell.
Function Create-Appointment(){
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)][String]$Subject,
[Parameter(Mandatory=$true)][DateTime]$MeetingStart,
[Parameter()][String]$Recipients,
[Parameter()][String]$Categories,
[Parameter()][String]$Location,
[Parameter()][String]$Body=$Subject,
[Parameter()][int]$ReminderMinutesBeforeStart=15,
[Parameter()][int]$DurationInMinutes=30
)
$ol = New-Object -ComObject Outlook.Application
$meeting = $ol.CreateItem('olAppointmentItem')
if($ReminderMinutesBeforeStart -gt 0){
$meeting.ReminderSet = $true
$meeting.ReminderMinutesBeforeStart = $ReminderMinutesBeforeStart
}
if($PSBoundParameters.ContainsKey('Recipients')){
foreach($email in $Recipients -split ";"){
if($email -ne ''){
$meeting.Recipients.Add($email)
}
}
$meeting.MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus]::olMeeting
} else {
$meeting.MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus]::olNonMeeting
}
if($PSBoundParameters.ContainsKey('Location')){
$meeting.Location = $Location
}
if($PSBoundParameters.ContainsKey('Categories')){
$meeting.Categories = $Categories
}
$meeting.Subject = $Subject
$meeting.Body = $Body
$meeting.Start = $MeetingStart
$meeting.Duration = $DurationInMinutes
$meeting.Save()
$meeting.Send()
}

Powershell Form GUI with draggable objects

I want to create a PowerShell based GUI with windows forms to display items in different containers and enable the user to drag items (control objects) from one container to another. Because of some bad flickering I copied the answer from this question and converted the code into PowerShell:
How to double buffer .NET controls on a form?
The problem is, that the objects are "smearing" on the form when I start dragging them and I can't find how to solve this.
class Dictionary : System.Collections.DictionaryBase
{
Dictionary() {}
[Bool]Exists([System.Object] $Key) {return ($Key -in $this.Keys) }
}
function SetDoubleBuffered()
{
param([System.Windows.Forms.Control] $TargetControl)
[System.Reflection.PropertyInfo] $DoubleBufferedProp = [System.Windows.Forms.Control].GetProperty("DoubleBuffered", [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance)
$DoubleBufferedProp.SetValue($TargetControl, $True, $Null)
}
function Button_MouseDown()
{
[CmdletBinding()]
param(
[parameter(Mandatory=$True)][System.Object] $Sender,
[parameter(Mandatory=$True)][System.EventArgs] $EventArguments
)
$Sender.Tag.DragStart = $False
$Sender.Tag.StartX = $EventArguments.X
$Sender.Tag.StartY = $EventArguments.Y
}
function Button_MouseUp()
{
[CmdletBinding()]
param(
[parameter(Mandatory=$True)][System.Object] $Sender,
[parameter(Mandatory=$True)][System.EventArgs] $EventArguments
)
$Sender.Tag.DragStart = $False
$Sender.Tag.StartX = 0
$Sender.Tag.StartY = 0
}
function Button_MouseMove()
{
[CmdletBinding()]
param(
[parameter(Mandatory=$True)][System.Object] $Sender,
[parameter(Mandatory=$True)][System.EventArgs] $EventArguments
)
if ($EventArguments.Button.value__ -gt 0)
{
if (-not $Sender.Tag.DragStart)
{
if ([System.Math]::sqrt([System.Math]::Pow($Sender.Tag.StartX - $EventArguments.X, 2) + [System.Math]::Pow($Sender.Tag.StartY - $EventArguments.Y, 2)) -ge 10)
{
$Sender.Tag.DragStart = $true
}
}
else
{
$Sender.Left = $Sender.Left + ($EventArguments.X - $Sender.Tag.StartX)
$Sender.Top = $Sender.Top + ($EventArguments.Y - $Sender.Tag.StartY)
}
}
else
{
$Sender.Tag.DragStart = $False
$Sender.Tag.StartX = 0
$Sender.Tag.StartY = 0
}
}
function OpenForm()
{
$Form = [System.Windows.Forms.Form]::new()
$Form.Text = "DragTest"
$Form.Width = 900
$Form.Height = 900
$Button = [System.Windows.Forms.Button]::new()
$Button.Left = 10
$Button.Top = 30
$Button.Height = 20
$Button.Width = 100
$Button.Text = "MyButton"
$Button.Name = "SomeButton"
$Button.Tag = [Dictionary]::new()
$Button.Add_MouseDown({Button_MouseDown -Sender $Button -EventArguments $_})
$Button.Add_MouseUp({Button_MouseUp -Sender $Button -EventArguments $_})
$Button.Add_MouseMove({Button_MouseMove -Sender $Button -EventArguments $_})
$Form.Controls.Add($Button)
SetDoubleBuffered -TargetControl $Form
SetDoubleBuffered -TargetControl $Button
$Form.ShowDialog() | Out-Null
}
cls
OpenForm

No PowerShell Form datagrid selected

I have created a script that must select all the items in a powershell form, based on a variable. If the variable $bolCheckAllNumberedItems has the value '$False, then the grid should not be selected, in case the variable $bolCheckAllNumberedItems has the value '$True', the whole grid should be selected.
The code I have:
Clear-Host
$bolCheckAllNumberedItems = $True
$frmTest = New-Object 'System.Windows.Forms.Form'
$btnOk = New-Object 'System.Windows.Forms.Button'
$chkAllItems = New-Object 'System.Windows.Forms.CheckBox'
$DataGridForTest = New-Object 'System.Windows.Forms.DataGridView'
$InitialFormWindowState = New-Object 'System.Windows.Forms.FormWindowState'
$frmTest.Controls.Add($btnOk)
$frmTest.Controls.Add($chkAllItems)
$frmTest.Controls.Add($DataGridForTest)
$frmTest.AutoScaleDimensions = New-Object System.Drawing.SizeF(6, 13)
$frmTest.AutoScaleMode = 'Font'
$frmTest.ClientSize = New-Object System.Drawing.Size(284, 261)
$frmTest.Name = 'frmTest'
$frmTest.StartPosition = 'CenterScreen'
$frmTest.Text = 'Test'
#
# btnOk
#
$btnOk.Location = New-Object System.Drawing.Point(186, 220)
$btnOk.Name = 'btnOk'
$btnOk.Size = New-Object System.Drawing.Size(75, 23)
$btnOk.TabIndex = 1
$btnOk.Text = '&Ok'
$btnOk.UseVisualStyleBackColor = $True
$btnOk.add_Click($btnOk_Click)
#
# chkAllItems
#
$chkAllItems.Location = New-Object System.Drawing.Point(22, 22)
$chkAllItems.Name = 'chkAllItems'
$chkAllItems.Size = New-Object System.Drawing.Size(104, 24)
$chkAllItems.TabIndex = 1
$chkAllItems.Text = 'Select all items'
$chkAllItems.UseVisualStyleBackColor = $True
$chkAllItems.add_CheckedChanged($chkAllItems_CheckedChanged)
#
# DataGridForTest
#
$DataGridForTest.ColumnHeadersHeightSizeMode = 'AutoSize'
$DataGridForTest.Location = New-Object System.Drawing.Point(22, 52)
$DataGridForTest.Name = 'DataGridForTest'
$DataGridForTest.Size = New-Object System.Drawing.Size(240, 150)
$DataGridForTest.TabIndex = 0
$DataGridForTest.TabStop = $False
$DataGridForTest.ColumnCount = 1
$DataGridForTest.ColumnHeadersVisible = $true
$DataGridForTest.Columns[0].Name = "Number"
$DataGridForTest.Columns[0].width = 100
$DataGridForTest.ScrollBars = 'Vertical'
$DataGridForTest.ReadOnly = $True
$DataGridForTest.AllowUserToAddRows = $False
$DataGridForTest.AllowUserToDeleteRows = $False
$DataGridForTest.AllowUserToResizeColumns = $False
$DataGridForTest.AllowUserToResizeRows = $False
$DataGridForTest.SelectionMode = "FullRowSelect"
$DataGridForTest.MultiSelect = $true
#----------------------------------------------
$ArrayNumbers = #("one","two","three","for","five","six","seven","eight","nine","ten")
[system.collections.arraylist]$ArrayWithHeader = #()
ForEach ($object in $ArrayNumbers)
{
$value = [pscustomobject]#{'Number' = $object}
$ArrayWithHeader.Add($value) | Out-Null
$value = $null
}
$ArrayWithHeader | foreach {$DataGridForTest.Rows.Add($_."Number") | Out-Null }
if($bolCheckAllNumberedItems)
{
$chkAllItems.Checked = $true
$DataGridForTest.SelectAll()
}
$chkAllItems.Add_Click({
if($chkAllItems.Checked)
{
$DataGridForTest.SelectAll()
}
else
{
$DataGridForTest.ClearSelection()
}
})
$btnOk.add_Click({$frmTest.Close()})
[void]$frmTest.ShowDialog()
What is wrong with my code? Help is appreciated and with kind regards,
The Sting Pilot
I think the easiest way of doing this is to start your script with a small function you can call inside the checkbox Click() event aswell as in an added form event called Add_Shown()
function Select-Grid ([bool]$selectAll) {
$chkAllItems.Checked = $selectAll
if ($selectAll) {
$DataGridForTest.SelectAll()
}
else {
$DataGridForTest.ClearSelection()
}
}
This way you can remove these lines:
if($bolCheckAllNumberedItems)
{
$chkAllItems.Checked = $true
$DataGridForTest.SelectAll()
}
and instead write
# call the helper function with the Checked value of the $chkAllItems checkbox
$chkAllItems.Add_Click({ Select-Grid $chkAllItems.Checked })
# this is an added event that fires when the form is first shown
$frmTest.Add_Shown( { Select-Grid $bolCheckAllNumberedItems } )
[void]$frmTest.ShowDialog()
# very important! dispose of the form when you are finished with it
$frmTest.Dispose()
An alternative approach is to again remove these lines:
if($bolCheckAllNumberedItems)
{
$chkAllItems.Checked = $true
$DataGridForTest.SelectAll()
}
and change the event $chkAllItems.Add_Click() to use a different event:
$chkAllItems.Add_CheckedChanged({
if($chkAllItems.Checked) {
$DataGridForTest.SelectAll()
}
else {
$DataGridForTest.ClearSelection()
}
})
Activate that event when the form is first shown and finish the code:
$frmTest.Add_Shown( { $chkAllItems.Checked = $bolCheckAllNumberedItems } )
[void]$frmTest.ShowDialog()
# and not forget to get rid of the form from memory
$frmTest.Dispose()
Using the CheckedChanged() method does not need an extra helper function as with the first approach.

Populate ComboBox2 depending on ComboBox1 selection

Few days of searching and trying multiple options, but nothing is working actually. With the previous code posted.
I've imported all my objects from XAML (all set in variables), I don't know if that would be a problem for that. I don't think since everything else seems to work properly.
I just want my Tab2CBB2 to show values depending on the selection of Tab1CBB1. Anyone could help ? (I haven't paste the entire code, neither the paths but you can probably help me with that). Note that those are two of my multiple tries. Thanks
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
[xml]$xaml = #" ...
"#
#Read XAML (Parse it)
$reader=(New-Object System.Xml.XmlNodeReader $XAML)
$Window=[Windows.Markup.XamlReader]::Load( $reader )
$listQA14 = Get-ChildItem $pathQA14 -name
$listQA15 = Get-ChildItem $pathQA15 -name
$listDBQA = Get-ChildItem $pathDBQA -name
$Tab1CBB1_SelectedIndexChanged= {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
Switch ($Tab1CBB1.Text) {
'QA14' {
$ListQA14 | ForEach { $Tab1CBB2.Items.Add($_) }
}
'QA15' {
$ListQA15 | ForEach { $Tab1CBB2.Items.Add($_) }
}
'ARIELDBQA' {
$ListDBQA | ForEach { $Tab1CBB2.Items.Add($_) }
}
}
}
$Tab1CBB1.add_SelectedIndexChanged($Tab1CBB1_SelectedIndexChanged)
#Displays the Window
$Window.ShowDialog() | out-null
Here is another option tried :
#ComboBox1------
$ItemsCBB1 = #('ARIELDBQA','QA14','QA15')
foreach ($Item in $ItemsCBB1) {
$Tab1CBB1.Items.Add($Item)
}
#ComboBox2-------
if ($Tab1CBB1.SelectedItem -eq 'QA14') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listQA14) {
$Tab1CBB2.Items.Add($Serie)
}
}
elseif ($Tab1CBB1.SelectedItem -eq 'QA15') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listQA15) {
$Tab1CBB2.Items.Add($Serie)
}
}
elseif ($Tab1CBB1.SelectedItem -eq 'listDBQA') {
$Tab1CBB2.Items.Clear()
foreach ($Serie in $listDBQA) {
$Tab1CBB2.Items.Add($Serie)
}
}
Tried this also : $Selection = $Tab1CBB1.SelectedItem.ToString()
Variable $selection put after 'if', but not working
Note that when I indicate what the current selection would be, it is working properly. The problem seems to come from 'recording' the selection a the time of clicking... Thnaks !
Basically, all you have to do is inside the $Tab1CBB1_SelectedIndexChanged scriptblock use the various lists with script scoping.
Without that, the variables are unknown inside the script block.
$Tab1CBB1_SelectedIndexChanged = {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
switch ($Tab1CBB1.Text) {
'QA14' { $script:ListQA14 | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
'QA15' { $script:ListQA15 | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
'ARIELDBQA' { $script:ListDBQA | ForEach-Object { $Tab1CBB2.Items.Add($_) } }
}
}
Another method could be to dynamically get the items to enter in the combobox, especially since these are file lists and can change while your form is being used:
$Tab1CBB1_SelectedIndexChanged = {
$Tab1CBB2.Items.Clear() # Clear the list
$Tab1CBB2.Text = $null # Clear the current entry
switch ($Tab1CBB1.Text) {
'QA14' { $path = $script:pathQA14 ; break }
'QA15' { $path = $script:pathQA15 ; break }
'ARIELDBQA' { $path = $script:pathDBQA }
}
Get-ChildItem -Path $path -Name | ForEach-Object { $Tab1CBB2.Items.Add($_) }
}
A simple example of what you seem to be after is something like this.
### Powershell populate ComboBox 2 based on ComboBox 1 Selection
Add-type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$form1 = New-Object 'System.Windows.Forms.Form'
$labelDoubleClickOnAnyItem = New-Object 'System.Windows.Forms.Label'
$listbox2 = New-Object 'System.Windows.Forms.ListBox'
$listbox1 = New-Object 'System.Windows.Forms.ListBox'
$buttonOK = New-Object 'System.Windows.Forms.Button'
$form1_Load={
$items=1..9 |
ForEach-Object {"List item $_"}
$listbox1.Items.AddRange($items)
}
$listbox1_MouseDoubleClick=[System.Windows.Forms.MouseEventHandler]{
$listbox2.Items.Add($listbox1.SelectedItem)
$listbox1.Items.Remove($listbox1.SelectedItem)
}
$listbox2_MouseDoubleClick=[System.Windows.Forms.MouseEventHandler]{
$listbox1.Items.Add($listbox2.SelectedItem)
$listbox2.Items.Remove($listbox2.SelectedItem)
}
$form1.Controls.Add($labelDoubleClickOnAnyItem)
$form1.Controls.Add($listbox2)
$form1.Controls.Add($listbox1)
$form1.Controls.Add($buttonOK)
$form1.AcceptButton = $buttonOK
$form1.ClientSize = '378, 388'
$form1.FormBorderStyle = 'FixedDialog'
$form1.MaximizeBox = $False
$form1.MinimizeBox = $False
$form1.Name = 'form1'
$form1.StartPosition = 'CenterScreen'
$form1.Text = 'Form'
$form1.add_Load($form1_Load)
$labelDoubleClickOnAnyItem.Font = 'Microsoft Sans Serif, 9.75pt, style=Bold, Italic'
$labelDoubleClickOnAnyItem.Location = '13, 13'
$labelDoubleClickOnAnyItem.Name = 'labelDoubleClickOnAnyItem'
$labelDoubleClickOnAnyItem.Size = '353, 41'
$labelDoubleClickOnAnyItem.TabIndex = 3
$labelDoubleClickOnAnyItem.Text = 'Double click on any item to move it from one box to the other.'
$listbox2.FormattingEnabled = $True
$listbox2.Location = '200, 67'
$listbox2.Name = 'listbox2'
$listbox2.Size = '166, 277'
$listbox2.Sorted = $True
$listbox2.TabIndex = 2
$listbox2.add_MouseDoubleClick($listbox2_MouseDoubleClick)
$listbox1.FormattingEnabled = $True
$listbox1.Location = '12, 67'
$listbox1.Name = 'listbox1'
$listbox1.Size = '167, 277'
$listbox1.Sorted = $True
$listbox1.TabIndex = 1
$listbox1.add_MouseDoubleClick($listbox1_MouseDoubleClick)
$buttonOK.Anchor = 'Bottom, Right'
$buttonOK.DialogResult = 'OK'
$buttonOK.Location = '291, 353'
$buttonOK.Name = 'buttonOK'
$buttonOK.Size = '75, 23'
$buttonOK.TabIndex = 0
$buttonOK.Text = '&OK'
$form1.ShowDialog()
Or even this using just the ComboBox elements
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = New-Object System.Drawing.Point(498,459)
$Form.text = 'Form'
$Form.TopMost = $false
$Form.StartPosition = 'CenterScreen'
$ComboBox1 = New-Object system.Windows.Forms.ComboBox
$ComboBox1.text = ''
$ComboBox1.width = 216
$ComboBox1.height = 75
#('ComboxItem1','ComboxItem2','ComboxItem3') |
ForEach-Object {[void] $ComboBox1.Items.Add($_)}
$ComboBox1.location = New-Object System.Drawing.Point(31,41)
$ComboBox1.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$ComboBox2 = New-Object system.Windows.Forms.ComboBox
$ComboBox2.text = ''
$ComboBox2.width = 213
$ComboBox2.height = 38
$ComboBox2.location = New-Object System.Drawing.Point(32,73)
$ComboBox2.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
Function Add-ItemToComboBox2
{
$ComboBox2.Items.Add($ComboBox1.SelectedItem)
$ComboBox2.Refresh()
}
$Form.controls.AddRange(
#(
$ComboBox1,
$ComboBox2
)
)
$ComboBox1.Add_SelectedIndexChanged({ Add-ItemToComboBox2 })
[void]$Form.ShowDialog()
You have not stated what UX/UI tool you are using to design and implement your
GUI. Yet, hard coding is also just a challenge. So consider using one of these for your UX/UI effort. Your UX/UI should just work, whether you have PowerShell code behind it (or any other language). Your PowerShell code should just work, regardless of the UX/UI that may be implemented. Your UX/UI code should be separate from your ops code. You call your ops code from the UX/UI.
Free:
https://poshgui.com
https://www.youtube.com/results?search_query=poshgui
https://visualstudio.microsoft.com/vs/community
Be sure to thoroughly read the licensing agreement for VS.
https://www.youtube.com/results?search_query=vs+2019+community+winforms
Purchase
https://ironmansoftware.com/psscriptpad
https://www.youtube.com/results?search_query=psscriptpad
https://ironmansoftware.com/powershell-pro-tools
https://www.youtube.com/results?search_query=powershell-pro-tools
https://www.sapien.com/software/powershell_studio
https://www.youtube.com/results?search_query=Sapien%27s+powershell+studio++winforms
https://visualstudio.microsoft.com/vs
https://www.youtube.com/results?search_query=vs+2019+community+winforms

How to generate dynamic parameters for script cmdlet by reading an msbuild project?

I'm attempting to create a script cmdlet with dynamic parameters for each of the targets in an MSBuild project file. It's working with one minor annoyance - it only autocompletes the static parameters until I type the first character(s) of a target parameter.
My script with the cmdlet "Make-Project" follows.
What would cause DynamicParam to not return parameters unless the first part of the parameter is entered?
Set-Alias mk Make-Project
function Make-Project
{
[CmdletBinding(DefaultParameterSetName="Build")]
PARAM(
[Parameter(ParameterSetName="Build")]
[Switch]$Build,
[Parameter(ParameterSetName="Clean")]
[Switch]$Clean,
[Parameter(ParameterSetName="Rebuild")]
[Switch]$Rebuild,
[ValidateSet( "q","quiet", "m","minimal", "n","normal", "d","detailed", "diag","diagnostic" )]
[string]$BuildVerbosity,
[Switch]$CertificationBuild,
[Switch]$BuildDebug
)
PROCESS
{
# Defaults
$certBuild = ""
$target = "Usage"
$buildTarget = "Build"
$verbosity = "minimal"
$configuration = "release"
if ( [System.Convert]::ToBoolean( $env:project_build_debug ) )
{ $configuration = "debug" }
foreach( $paramName in $MyInvocation.BoundParameters.Keys )
{
switch -RegEx ( $paramName )
{
"(?i)CertificationBuild" { $certBuild = "cert" }
"(?i)^(Build|Clean|Rebuild)$" { $buildTarget = $paramName }
"(?i)^BuildVerbosity$" { $verbosity = $MyInvocation.BoundParameters[ $paramName ] }
"(?i)^BuildDebug$" { $configuration = "debug" }
default { $target = $paramName }
}
}
$msbuildexe = Get-MSBuildExe
if ( $msbuildexe.Contains( "v4.0" ) )
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /property:CLR4=1 Project.proj" }
else
{ $cmd = "$msbuildexe /v:$verbosity $certBuild /m /property:Configuration=$configuration /property:BuildTarget=$buildTarget /target:$target /tv:3.5 Project.proj" }
Write-Host $cmd
Invoke-Expression $cmd
}
DynamicParam
{
$projFile = '.\Project.Proj'
$projXml = [xml]( Get-Content $projFile )
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$projXml.Project.Target | % {
$paramName = $_.Name
$attribute = New-Object System.Management.Automation.ParameterAttribute
$attribute.ParameterSetName = "__AllParameterSets"
$attribute.Mandatory = $false
$attributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attribute)
$param = New-Object System.Management.Automation.RuntimeDefinedParameter( $paramName, [Switch], $attributeCollection )
$paramDictionary.Add( $paramName, $param )
}
return $paramDictionary
}
}
function Get-MSBuildExe
{
$bitness = ""
if ( $env:PROCESSOR_ARCHITECTURE -eq "AMD64" )
{ $bitness = "64" }
$msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v4.0.30319\MSBuild.exe"
if ( -not (Test-Path $msbuildexe) )
{ $msbuildexe = "$env:SystemRoot\Microsoft.NET\Framework${bitness}\v3.5\MSBuild.exe" }
$msbuildexe
}
I would suggest that you not add a bunch of switch parameters. Instead, add one dynamic parameter named "Target" and add a ValidateSet attribute to it with the list of targets.
This is a helpful article.
http://robertrobelo.wordpress.com/2010/02/12/add-parameter-validation-attributes-to-dynamic-parameters/