Trouble with Dynamic Parameter Positioning in ParameterSets

This topic contains 4 replies, has 4 voices, and was last updated by Profile photo of Marc Rice Marc Rice 1 year, 3 months ago.

  • Author
    Posts
  • #21921
    Profile photo of Chris Koch
    Chris Koch
    Participant

    Thanks again to Rohn Edwards, who helped me with my first DynamicParam problem. Maybe he or someone can help me with what I hope is my last issue...

    Having a hard time positioning an established Dynamic Parameter (Set1C in this example) in the right location. The code at the bottom, which starts with everything in the same ParameterSetName gets me this:

    PS G:\> help ParamTest
    
    NAME
        ParamTest
        
    SYNTAX
        ParamTest [-A] (string)  [-Set1] [-Set2] [-Set1B] (string) [-Set1C] (string) {Option1,Option2,Option3} [-Set2B] (string)  [CommonParameters]
        
    
    ALIASES
        None
        
    
    REMARKS
        None

    I'm trying to modify it so that the syntax shows like this:

    PS G:\> help ParamTest
    
    NAME
        ParamTest
        
    SYNTAX
        ParamTest [-A] (string) [-Set1] [-Set1B] (string) [-Set1C]  {Option1,Option2,Option3} (string)  [CommonParameters]
        
        ParamTest [-A] (string) [-Set2] [-Set2B] (string)  [CommonParameters]
        
    
    ALIASES
        None
        
    
    REMARKS
        None

    Every way I try to do this ends up with the Dynamic Parameter (Set1C) just disappearing from the parameter list...

    The other thing I notice is that, even though the ordering shows Set1C in the 5th position when you use help, when you actually tab through the parameters on the command line, in populates Set1C last, ignoring the position attribute. I assume it's because the DynamicParam section is being evaluated after the Param section, but is there any way around that?

    Here's the code. Thanks to anyone who can offer assistance!

    Function ParamTest {
    [CmdletBinding()]
    Param (
    
            [Parameter(Mandatory=$true,Position=1,ParameterSetName="__AllParameterSets")]
            [string]
            $A,
                    
            [Parameter(Mandatory=$true,Position=2,ParameterSetName="__AllParameterSets")]
            [switch]
            $Set1,
            
            [Parameter(Mandatory=$true,Position=3,ParameterSetName="__AllParameterSets")]
            [switch]
            $Set2,
    
            [Parameter(Mandatory=$true,Position=4,ParameterSetName="__AllParameterSets")]
            [string]
            $Set1B,
    
            [Parameter(Mandatory=$true,Position=6,ParameterSetName="__AllParameterSets")]
            [string]
            $Set2B
    
        )
    DynamicParam {
                # Set the dynamic parameters' name
                $ParameterName = 'Set1C'
                
                # Create the dictionary 
                $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    
                # Create the collection of attributes
                $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
                
                # Create and set the parameters' attributes
                $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
                $ParameterAttribute.Mandatory = $true
                $ParameterAttribute.Position = 5
                $ParameterAttribute.ValuefromPipeline = $true
                $ParameterAttribute.ValueFromPipelineByPropertyName = $true
                $ParameterAttribute.ParameterSetName = "__AllParameterSets"
    
                # Add the attributes to the attributes collection
                $AttributeCollection.Add($ParameterAttribute)
    
                # Generate and set the ValidateSet 
                $arrSet = ("Option1,Option2,Option3")
                $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
    
                # Add the ValidateSet to the attributes collection
                $AttributeCollection.Add($ValidateSetAttribute)
    
                # Create and return the dynamic parameter
                $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
                $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
                return $RuntimeParameterDictionary
            }
    
        }
  • #21922
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Interesting... I modified your code as follows:

    Function ParamTest {
    [CmdletBinding()]
    Param (
     
            [Parameter(Mandatory=$true,Position=1,ParameterSetName="Set1")]
            [Parameter(Mandatory=$true,Position=1,ParameterSetName="Set2")]
            [string]
            $A,
     
            [Parameter(Mandatory=$true,Position=2,ParameterSetName="Set1")]
            [switch]
            $Set1,
     
            [Parameter(Mandatory=$true,Position=3,ParameterSetName="Set2")]
            [switch]
            $Set2,
     
            [Parameter(Mandatory=$true,Position=4,ParameterSetName="Set1")]
            [string]
            $Set1B,
     
            [Parameter(Mandatory=$true,Position=6,ParameterSetName="Set2")]
            [string]
            $Set2B
     
        )
    
        DynamicParam {
                # Set the dynamic parameters' name
                $ParameterName = 'Set1C'
     
                # Create the dictionary 
                $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
     
                # Create the collection of attributes
                $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]
     
                # Create and set the parameters' attributes
                $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
                $ParameterAttribute.Mandatory = $true
                $ParameterAttribute.Position = 5
                $ParameterAttribute.ValuefromPipeline = $true
                $ParameterAttribute.ValueFromPipelineByPropertyName = $true
                $ParameterAttribute.ParameterSetName = "Set1"
     
                # Add the attributes to the attributes collection
                $AttributeCollection.Add($ParameterAttribute)
     
                # Generate and set the ValidateSet 
                $arrSet = ("Option1,Option2,Option3")
                $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet)
     
                # Add the ValidateSet to the attributes collection
                $AttributeCollection.Add($ValidateSetAttribute)
     
                # Create and return the dynamic parameter
                $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
                $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)
                return $RuntimeParameterDictionary
            }
     
        }
    

    Which reproduces the behavior you're describing, of -Set1C disappearing from Get-Command -Syntax. That parameter also doesn't tab complete until you've specified enough parameters to make powerShell certain that "Set1" is in effect. For example, typing [b]ParamTest -A Whatever -{Tab}[/b] will not show Set1C in the list. Once you've typed either -Set1 or -Set1B Something, then it'll show up. I'd call that a bug in PowerShell's handling of dynamic parameters. It should be showing all parameters that could still potentially be valid based on the currently available sets, rather than only showing dynamic parameters once a single parmaeter set is the only valid one remaining. Dynamic parameters assigned to a specific parameter set should also be showing up in Get-Command -Syntax at all times.

    I'm not sure if this is a bug that would also affect compiled cmdlets, or if it's specific to advanced functions. I'll try to test that when I have time. (There may also already be bugs filed for this on the Connect site; haven't checked yet.)

  • #21925
    Profile photo of Chris Koch
    Chris Koch
    Participant

    Two bugs in two days – story of my life lol!

    Yep, that's almost exactly how I was trying to modify it. Glad I'm not missing something obvious. Fingers crossed for a workaround or fix...

    Thanks!

  • #21930
    Profile photo of Rohn Edwards
    Rohn Edwards
    Participant

    I've had issues with getting dynamic parameters to show up in Get-Help and Get-Command -Syntax properly in the past. There's at least one connect bug on it here: https://connect.microsoft.com/PowerShell/feedback/details/397832/dynamicparam-not-returned-when-aliased

    I haven't tried to run your example (maybe I'll have time later), so forgive me if it's not the exact issue you're describing.

  • #29502
    Profile photo of Marc Rice
    Marc Rice
    Participant

    This may be a bit of an old thread but I ran into this issue today while working on some code. I still think it is a bug, but I think I found a work around. If you specify a defaultparametersetname in the cmdletbinding attribute, all the sets seems to show up and will tab complete.

    For example, modifying Dave's code by:

    Function ParamTest {
    [CmdletBinding(DefaultParameterSetName='Set1c')]
    

    seems to be enough to get all parametersets working. Maybe that is a way of forcing the condition that Dave mentions about resolving to a single valid parameter set. Maybe setting defaultparametersetname defaults that condition. I only spent a little time on it, but it worked for my code...

You must be logged in to reply to this topic.