I have an application I am making with multiple text boxes and I am trying to clean it up by making a text box function. However the name parameter itself needs to return a variable name and I am just not quite sure how to do that. I tried giving the parameter the
[psvariable] type but that does not seem to be working.
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.StartPosition = "CenterScreen"
$Form.Size = New-Object System.Drawing.Point(415,838)
$Form.text = "Test Form"
$Form.TopMost = $false
$Form.MaximumSize = $Form.Size
$Form.MinimumSize = $Form.Size
function textBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[psvariable]$name,
[Parameter (Mandatory = $True)]
[string]$ml,
[Parameter (Mandatory = $True)]
[int]$lx,
[Parameter (Mandatory = $True)]
[int]$ly,
[Parameter (Mandatory = $True)]
[string]$text
)
$name = New-Object system.Windows.Forms.TextBox
$name.multiline = $ml
$name.Size = New-Object System.Drawing.Size(300,60)
$name.location = New-Object System.Drawing.Point($lx,$ly)
$name.Font = New-Object System.Drawing.Font('Microsoft Sans Serif',10)
$name.Text = $text
}
textBox -name '$source' -ml $True -lx 15 -ly 100 -text "test it"
$Form.controls.AddRange(#($source))
[void]$Form.ShowDialog()
exit
It's unclear why is the $name parameter there in your function to begin with, I don't see any use for it. The other problem is that your function is not returning anything and your function invocation is not capturing anything either.
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
# Form code here
function textBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[bool] $ml,
[Parameter (Mandatory = $True)]
[int] $lx,
[Parameter (Mandatory = $True)]
[int] $ly,
[Parameter (Mandatory = $True)]
[string] $text
)
[Windows.Forms.TextBox]#{
Multiline = $ml
Size = [Drawing.Size]::new(300,60)
Location = [Drawing.Point]::new($lx, $ly)
Font = [Drawing.Font]::new('Microsoft Sans Serif', 10)
Text = $text
}
}
# Capture here
$txtBox = textBox -ml $True -lx 15 -ly 100 -text "test it"
$Form.Controls.AddRange(#($txtBox))
[void] $Form.ShowDialog()
To clarify further, the $Name Parameter was constraint to be a PSVariable:
[psvariable] $Name = $null
Hence when, in your function's body, you try to assign an instance different than a PSVariable:
$name = New-Object System.Windows.Forms.TextBox
You would receive this error, which is basically telling you that PowerShell cannot convert an instance of TextBox to the constraint type:
Cannot convert the "System.Windows.Forms.TextBox, Text: " value of type "System.Windows.Forms.TextBox" to type "System.Management.Automation.PSVariable".
Related
I have the following code that I can run outside of a function and it works just fine.
I am trying to make a form that will be displaying multiple images so I need to format it into a function.
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.StartPosition = "CenterScreen"
$Form.Size = New-Object System.Drawing.Point(800,800)
$Form.text = "Example Form"
$Form.TopMost = $false
$Form.MaximumSize = $Form.Size
$Form.MinimumSize = $Form.Size
$Picture = (get-item ("C:\Users\User\Desktop\t.png"))
$img = [System.Drawing.Image]::Fromfile($Picture)
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Width = $img.Size.Width
$pictureBox.Height = $img.Size.Height
$pictureBox.Image = $img
$pictureBox.Location = [Drawing.Point]::new(15, 15)
$Form.controls.add($pictureBox)
$Form.ShowDialog()
but when I tried to assemble this as a function it is not working
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.StartPosition = "CenterScreen"
$Form.Size = New-Object System.Drawing.Point(800,800)
$Form.text = "Example Form"
$Form.TopMost = $false
$Form.MaximumSize = $Form.Size
$Form.MinimumSize = $Form.Size
function pictureBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[string] $p,
[Parameter (Mandatory = $True)]
[int] $lx,
[Parameter (Mandatory = $True)]
[int] $ly
)
$Picture = (get-item ($p))
$img = [System.Drawing.Image]::Fromfile($Picture)
$pictureBox = new-object Windows.Forms.PictureBox
$pictureBox.Size = [Drawing.Size]::new($img.Size.Width,$img.Size.Height)
$pictureBox.Location = [Drawing.Point]::new($lx, $ly)
$pictureBox.Image = $img
}
$v1 = pictureBox -p "C:\Users\User\Desktop\t.png" -lx 15 -ly 15
$Form.controls.add($v1)
$Form.ShowDialog()
What am I doing wrong?
Your function pictureBox is not returning anything, it creates a new PictureBox instance, updates it's properties but then that object is never outputted, hence $v1 gets assigned AutomationNull.Value.
# Same code for creating the Form here
function pictureBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[string] $p,
[Parameter (Mandatory = $True)]
[int] $lx,
[Parameter (Mandatory = $True)]
[int] $ly
)
$img = [Drawing.Image]::Fromfile($PSCmdlet.GetUnresolvedProviderPathFromPSPath($p))
[Windows.Forms.PictureBox]#{
Size = [Drawing.Size]::new($img.Size.Width, $img.Size.Height)
Location = [Drawing.Point]::new($lx, $ly)
Image = $img
}
}
$v1 = pictureBox -p "C:\Users\User\Desktop\t.png" -lx 15 -ly 15
$Form.controls.add($v1)
$Form.ShowDialog()
I am tring to make a form with several images that will open a URL.
Is it possible to add the click event to the function itself or do I have to add it after the function has been called?
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.StartPosition = "CenterScreen"
$Form.Size = New-Object System.Drawing.Point(800,800)
$Form.text = "Example Form"
$Form.TopMost = $false
$Form.MaximumSize = $Form.Size
$Form.MinimumSize = $Form.Size
$Form.BackColor = "#000000"
function pictureBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[string] $p,
[Parameter (Mandatory = $True)]
[int] $lx,
[Parameter (Mandatory = $True)]
[int] $ly,
[Parameter (Mandatory = $True)]
[string] $url
)
$img = [Drawing.Image]::Fromfile($PSCmdlet.GetUnresolvedProviderPathFromPSPath($p))
[Windows.Forms.PictureBox]#{
Size = [Drawing.Size]::new($img.Size.Width, $img.Size.Height)
Location = [Drawing.Point]::new($lx, $ly)
Image = $img
}
Add_Click ( { start-process $url } )
}
$v1 = pictureBox -p "C:\Users\User\Desktop\t.png" -lx 20 -ly 65 -url **THE-URL**
$v1.Add_Click( { start-process **THE-URL** } )
It's possible to do it from the function itself, first you need to instantiate the PictureBox, in the example below, the object is stored in $pbx. After that you can add the Click event to it.
As you may note there is also a GetNewClosure() method invocation, in this case it is required because the ScriptBlock needs to "remember" the value of $url.
function pictureBox {
[CmdletBinding()]
param (
[Parameter (Mandatory = $True)]
[string] $p,
[Parameter (Mandatory = $True)]
[int] $lx,
[Parameter (Mandatory = $True)]
[int] $ly,
[Parameter (Mandatory = $True)]
[string] $url
)
$img = [Drawing.Image]::Fromfile($PSCmdlet.GetUnresolvedProviderPathFromPSPath($p))
$pbx = [Windows.Forms.PictureBox]#{
Size = [Drawing.Size]::new($img.Size.Width, $img.Size.Height)
Location = [Drawing.Point]::new($lx, $ly)
Image = $img
}
$pbx.Add_Click({ Start-Process $url }.GetNewClosure())
$pbx
}
$v1 = pictureBox -p "C:\Users\User\Desktop\t.png" -lx 20 -ly 65 -url "https://stackoverflow.com/"
$Form.Controls.Add($v1)
$Form.ShowDialog()
Is there a way to bring a PoweR Shell popup to the front of the screen?
i use this command to show the popup
$wshell = New-Object -ComObject Wscript.Shell
$Output = $wshell.Popup("text" ,0,"header",0+64)
but when i use form and a button in the form bis supposed to bring up the popup it shows in the back behind the form
the form itself opens in the center and not bringing to front as shows here
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '1370,720'
$Form.text = "Chame Wizard"
$Form.TopMost = $true
$Form.icon = "c:\script\chame.ico"
$FormImage = [system.drawing.image]::FromFile("c:\script\back2.jpg")
$Form.BackgroundImage = $FormImage
$Form.StartPosition = "CenterScreen"
i know i can use balloon popup but i want the user to press OK before the script continues.
Thanks :-)
You can also use one of the overloaded methods of [System.Windows.Forms.MessageBox]::Show() which allows you to add the owner window in order to have the messagebox be topmost to that.
By using $null there, your messagebox will be topmost to all opened windows:
function Show-MessageBox {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false)]
[string]$Title = 'MessageBox in PowerShell',
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[ValidateSet('OK', 'OKCancel', 'AbortRetryIgnore', 'YesNoCancel', 'YesNo', 'RetryCancel')]
[string]$Buttons = 'OKCancel',
[Parameter(Mandatory = $false)]
[ValidateSet('Error', 'Warning', 'Information', 'None', 'Question')]
[string]$Icon = 'Information',
[Parameter(Mandatory = $false)]
[ValidateRange(1,3)]
[int]$DefaultButton = 1
)
# determine the possible default button
if ($Buttons -eq 'OK') {
$Default = 'Button1'
}
elseif (#('AbortRetryIgnore', 'YesNoCancel') -contains $Buttons) {
$Default = 'Button{0}' -f [math]::Max([math]::Min($DefaultButton, 3), 1)
}
else {
$Default = 'Button{0}' -f [math]::Max([math]::Min($DefaultButton, 2), 1)
}
Add-Type -AssemblyName System.Windows.Forms
# added from tip by [Ste](https://stackoverflow.com/users/8262102/ste) so the
# button gets highlighted when the mouse hovers over it.
[void][System.Windows.Forms.Application]::EnableVisualStyles()
# Setting the first parameter 'owner' to $null lets he messagebox become topmost
[System.Windows.Forms.MessageBox]::Show($null, $Message, $Title,
[Windows.Forms.MessageBoxButtons]::$Buttons,
[Windows.Forms.MessageBoxIcon]::$Icon,
[Windows.Forms.MessageBoxDefaultButton]::$Default)
}
With this function in place, you call it like:
Show-MessageBox -Title 'Important message' -Message 'Hi there!' -Icon Information -Buttons OK
Edit
As asked by Ste, the above function shows the messagebox TopMost. That however does not mean it is Modal. It only means the box is shown on top when first displayed, but can be pushed to the background by activating other windows.
For a real Modal messagebox that cannot be pushed to the background, I use this:
function Show-MessageBox {
[CmdletBinding()]
param(
[parameter(Mandatory = $true, Position = 0)]
[string]$Message,
[parameter(Mandatory = $false)]
[string]$Title = 'MessageBox in PowerShell',
[ValidateSet("OKOnly", "OKCancel", "AbortRetryIgnore", "YesNoCancel", "YesNo", "RetryCancel")]
[string]$Buttons = "OKCancel",
[ValidateSet("Critical", "Question", "Exclamation", "Information")]
[string]$Icon = "Information"
)
Add-Type -AssemblyName Microsoft.VisualBasic
[Microsoft.VisualBasic.Interaction]::MsgBox($Message, "$Buttons,SystemModal,$Icon", $Title)
}
Show-MessageBox -Title 'Important message' -Message 'Hi there!' -Icon Information -Buttons OKOnly
Theos' answer is great but if you want the MessageBox to be modal and not allow any more interaction with the $Form then this is the way to go about it.
I've updated this to show how to show a modal messagebox with a $Form.Add_Load event handler should you need a modal message box on execution of the script.
Comments in the code on how to achieve this:
# I've added this as an answer here:
# Edit added the $Form.Add_Load event handler.
# https://stackoverflow.com/questions/59371640/powershell-popup-in-form/67368911#67368911
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '444,55'
$Form.StartPosition = "CenterScreen"
$Form.Text = "..."
$Form.TopMost = $true
$Btn = New-Object System.Windows.Forms.Button
$Btn.Height = $Form.Height-39
$Btn.Text = "DO NOT PRESS..."
$Btn.Width = $Form.Width-15
$Form.Controls.Add($Btn)
$Btn.Add_Click({
# To have a modal messagebox you need to have it called from a form and use: $this.ActiveForm as shown below.
[System.Windows.Forms.MessageBox]::Show($this.ActiveForm, 'Not to!!', 'I Told You..', [Windows.Forms.MessageBoxButtons]::"OK", [Windows.Forms.MessageBoxIcon]::"Warning")
})
# Here you can do an if statement and then fire the message box when the Form loads which will be modal also.
$Form.Add_Load({
[System.Windows.Forms.MessageBox]::Show($this.ActiveForm, 'You can add the message box as an event handler to show a message when the form is loaded...', 'A Message on Form Load Event!!', [Windows.Forms.MessageBoxButtons]::"OK", [Windows.Forms.MessageBoxIcon]::"Warning")
})
# This below needs to be added to focus the dialog when it opens after the $Form.
$Form.Add_Shown({$Form.Activate(); $Btn.Focus()})
$Form.ShowDialog()
Theos great function modified to accept a -Modal switch.
# Mod of Theos' answer here: https://stackoverflow.com/a/59378301/8262102
# Edit by Ste: Can now be shown modal to the parent form with the usage of the
# -Modal switch.
# Mod of Theos' answer here: https://stackoverflow.com/a/59378301/8262102
# Edit by Ste: Can now be shown modal to the parent form with the usage of the
# -Modal switch.
Function Show-MessageBox {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $false)]
[string]$Title = 'MessageBox in PowerShell',
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter(Mandatory = $false)]
[ValidateSet('OK', 'OKCancel', 'AbortRetryIgnore', 'YesNoCancel', 'YesNo', 'RetryCancel')]
[string]$Buttons = 'OKCancel',
[Parameter(Mandatory = $false)]
[ValidateSet('Error', 'Warning', 'Information', 'None', 'Question')]
[string]$Icon = 'Information',
[Parameter(Mandatory = $false)]
[ValidateRange(1,3)]
[int]$DefaultButton = 1,
[Parameter(Mandatory = $false)]
[Switch]$Modal = $false
)
# Determine the possible default button.
if ($Buttons -eq 'OK') {
$Default = 'Button1'
}
elseif (#('AbortRetryIgnore', 'YesNoCancel') -contains $Buttons) {
$Default = 'Button{0}' -f [math]::Max([math]::Min($DefaultButton, 3), 1)
}
else {
$Default = 'Button{0}' -f [math]::Max([math]::Min($DefaultButton, 2), 1)
}
# Create a new form to hold the object and set it properties for the main
# FolderBrowserDialog form.
Add-Type -AssemblyName System.Windows.Forms
$MessageBoxParentForm = New-Object System.Windows.Forms.Form
$MessageBoxParentForm.TopMost = $Modal
Add-Type -AssemblyName System.Windows.Forms
[void][System.Windows.Forms.Application]::EnableVisualStyles()
[System.Windows.Forms.MessageBox]::Show(($MessageBoxParentForm), $Message, $Title,
[Windows.Forms.MessageBoxButtons]::$Buttons,
[Windows.Forms.MessageBoxIcon]::$Icon,
[Windows.Forms.MessageBoxDefaultButton]::$Default)
}
# Non-modal example.
# Show-MessageBox -Title 'Important message' -Message 'Hi there!' -Icon None -Buttons OKCancel
# Modal example.
Show-MessageBox -Modal -Title 'Important message' -Message 'Hi there!' -Icon Warning -Buttons OK
I've been reading a ton of these articles that say to use Get-SPWeb, but I've never been able to get those functions working due to authentication errors. I have build my own little functions to do what I need but I'm struggling to figure out what I'm doing wrong for my update function. Below are the functions I've built, and all of them work:
If (!$cred) {$cred = get-credential -UserName "$ENV:Username#$ENV:UserDomain.com" -Message "Enter your office 365 login"}
function Get-AuthenticationCookie($context)
{
$sharePointUri = New-Object System.Uri($context.site.Url)
$authCookie = $context.Credentials.GetAuthenticationCookie($sharePointUri)
if ($? -eq $false) #https://ss64.com/ps/syntax-automatic-variables.html
{
return $null
}
$fedAuthString = $authCookie.TrimStart("SPOIDCRL=".ToCharArray())
$cookieContainer = New-Object System.Net.CookieContainer
$cookieContainer.Add($sharePointUri, (New-Object System.Net.Cookie("SPOIDCRL", $fedAuthString)))
return $cookieContainer
}
function Get-SharepointContext
{
Param(
[Parameter(Mandatory = $true)]
$siteUrl,
[Parameter(Mandatory = $false)]
$cred)
If (!$cred) {$cred = get-credential -UserName "$ENV:Username#$env:USERDNSDOMAIN" -Message "Login"}
[string]$username = $cred.UserName
$securePassword = $cred.Password
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.ClientContext")
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
$ctx.RequestTimeOut = 1000 * 60 * 10;
$ctx.AuthenticationMode = [Microsoft.SharePoint.Client.ClientAuthenticationMode]::Default
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $securePassword)
$ctx.Credentials = $credentials
Return $ctx
}
function Add-SharepointListEntry
{
#example
#Add-SharepointListEntry -cred $cred -clientName $DestinationPages
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:Userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName,
$SharepointData
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
# Get List
$List = $Context.Web.Lists.GetByTitle($ListName)
$Context.Load($List)
$Context.ExecuteQuery()
# Create Single List Item
$ListItemCreationInformation = New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
$NewListItem = $List.AddItem($ListItemCreationInformation)
#construct the entry to insert
$NewListItem["Title"] = $SharepointData.Title #Client Name
$NewListItem["Description"] = $SharepointData.Title
#These objects should pass right through
$NewListItem["Client"] = $SharepointData.Client
$NewListItem["Author"] = $SharepointData.Author
$NewListItem["Initials"] = $SharepointData.Author
$NewListItem["Created"] = $SharepointData.Created
$NewListItem.Update()
$Context.ExecuteQuery()
}
Function Get-SharepointListData
{
#example
#Get-SharepointListData -cred $cred
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:Userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
#Retrive the List
$List = $Context.web.Lists.GetByTitle($ListName)
#Get All List Items
#reference https://gallery.technet.microsoft.com/office/How-to-do-a-CAML-Query-6f5260cf
$Query = New-Object Microsoft.SharePoint.Client.CamlQuery
$ListItems = $List.GetItems($Query)
$context.Load($ListItems)
$context.ExecuteQuery()
# Turn item into a catch array
$ListItemCollection = #()
ForEach ($item in $ListItems)
{
$propertiesValues = New-Object PSObject
$currentItem = $item
$item.FieldValues.Keys | Where {$_ -ne "MetaInfo"} | ForEach {Add-Member -InputObject $propertiesValues -MemberType NoteProperty -Name $_ -Value $currentItem[$_]}
$ListItemCollection += $propertiesValues
Remove-Variable propertiesValues
}
Return $ListItemCollection
}
Now I'm building a new function and trying to use one list (which is querying a sharepoint folder) to update a sharepoint list. I query the directory with the get-sharepointlistdata, then loop through the results to add new entries if something is missing. This whole process works without issue. I'm trying to add a step in to update for any changes, but the function keeps failing on $list.Items.GetByID($index) throwing the error "You cannot call a method on a null-valued expression.":
Function Set-SharepointListData
{
Param(
[Parameter(Mandatory = $true)]
$cred,
[Parameter(Mandatory = $true)]
$sitename,
$siteUrl = "https://$env:userdomain.sharepoint.com/$sitename",
[Parameter(Mandatory = $true)]
$ListName,
[Parameter(Mandatory = $true)]
[int]$Index,
[Parameter(Mandatory = $true)]
$Time
)
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
# Bind to site collection
$Context = Get-SharepointContext -siteUrl $siteUrl -cred $cred
# Get List
$List = $Context.Web.Lists.GetByTitle($ListName)
$Context.Load($List)
$Context.ExecuteQuery()
# Select Single List Item
$ListItem = $List.Items.GetById($index)
$ListItem["Created"] = $time
$ListItem.Update();
$Context.ExecuteQuery();
}
I'm certain I'm overlooking something obvious here... anyone have any ideas?
The $Context.Web.Lists.GetByTitle($ListName) doesn't return the Items of the list. You have to load the Items... normally done via caml query. See here - Although the sample is in C# it should get you started.
Actually I rather suggest you to use PnPPowershell, there are plenty of cmdlets to work with Sharepoint.
Let's say I have a function like:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
[Parameter(ParameterSetName='Set1')][string] $Username,
[Parameter(ParameterSetName='Set1')][string] $Password
)
..
}
And I would like to make the parameter set to be mandatory when $AuthenticationType = 'UsernameAndPassword' but also so that it cannot be used if $AuthenticationType = 'WindowsAuthentication'.
It this even possible in PowerShell?
Using the link from Tim Ferrill's answer, I created the following function to help create dynamic parameters:
function New-DynamicParameter
{
[CmdletBinding(DefaultParameterSetName = 'Core')]
param
(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][string] $Name,
[Parameter(Mandatory = $true, ParameterSetName = 'Core')][Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][type] $Type,
[Parameter(Mandatory = $false)][string] $ParameterSetName = '__AllParameterSets',
[Parameter(Mandatory = $false)][bool] $Mandatory = $false,
[Parameter(Mandatory = $false)][int] $Position,
[Parameter(Mandatory = $false)][bool] $ValueFromPipelineByPropertyName = $false,
[Parameter(Mandatory = $false)][string] $HelpMessage,
[Parameter(Mandatory = $true, ParameterSetName = 'ValidateSet')][string[]] $ValidateSet,
[Parameter(Mandatory = $false, ParameterSetName = 'ValidateSet')][bool] $IgnoreCase = $true
)
process
{
# Define Parameter Attributes
$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.ParameterSetName = $ParameterSetName
$ParameterAttribute.Mandatory = $Mandatory
$ParameterAttribute.Position = $Position
$ParameterAttribute.ValueFromPipelineByPropertyName = $ValueFromPipelineByPropertyName
$ParameterAttribute.HelpMessage = $HelpMessage
# Define Parameter Validation Options if ValidateSet set was used
if ($PSCmdlet.ParameterSetName -eq 'ValidateSet')
{
$ParameterValidateSet = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $ValidateSet -Strict (!$IgnoreCase)
}
# Add Parameter Attributes and ValidateSet to an Attribute Collection
$AttributeCollection = New-Object Collections.ObjectModel.Collection[System.Attribute]
$AttributeCollection.Add($ParameterAttribute)
$AttributeCollection.Add($ParameterValidateSet)
# Add parameter to parameter list
$Parameter = New-Object System.Management.Automation.RuntimeDefinedParameter -ArgumentList #($Name, $Type, $AttributeCollection)
# Expose parameter to the namespace
$ParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$ParameterDictionary.Add($Name, $Parameter)
return $ParameterDictionary
}
}
And solved my particular problem in the following way:
function Authenticate
{
param
(
[ValidateSet('WindowsAuthentication','UsernameAndPassword')][string] $AuthenticationType,
)
DynamicParam
{
if ($AuthenticationType -eq 'UsernameAndPassword')
{
New-DynamicParameter Username [string] -Mandatory $true
New-DynamicParameter Password [string] -Mandatory $true
}
}
...
}
It became unneeded to have a parameter set when using Dynamic Parameter so I removed the parameter set.
You can do this using DynamicParam. I saw a decent post on this recently here.
DynamicParam {
if ($AuthenticationType -eq 'UsernameAndPassword') {
#create ParameterAttribute Objects for the username and password
$unAttribute = New-Object System.Management.Automation.ParameterAttribute
$unAttribute.Mandatory = $true
$unAttribute.HelpMessage = "Please enter your username:"
$pwAttribute = New-Object System.Management.Automation.ParameterAttribute
$pwAttribute.Mandatory = $true
$pwAttribute.HelpMessage = "Please enter a password:"
#create an attributecollection object for the attributes we just created.
$attributeCollection = new-object System.Collections.ObjectModel.Collection[System.Attribute]
#add our custom attributes
$attributeCollection.Add($unAttribute)
$attributeCollection.Add($pwAttribute)
#add our paramater specifying the attribute collection
$unParam = New-Object System.Management.Automation.RuntimeDefinedParameter('username', [string], $attributeCollection)
$pwParam = New-Object System.Management.Automation.RuntimeDefinedParameter('password', [string], $attributeCollection)
#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('username', $unParam)
$paramDictionary.Add('password', $pwParam)
return $paramDictionary
}
}
Process {
$PSBoundParameters.username
$PSBoundParameters.password
}