Add-member scriptmethod dynamic validate set

Copper Contributor

Hi all,

 

I have a script that build an array of all supported screen resolutions on a computer.  WORKS !

I use this code to build the validateset of a dynamic parameter in another function.    WORKS !

I use the code to create and argument completer for a parameter in another function WORKS !

 

Now I create a pscustomobject , I add a note property named resolution to it               WORKS.

I create a scriptmethod and add it to the custom object to define resolution note property values  WORKS.

 

When I try to dynamic parameter or argument completion to build the possiblevalues for the script method none of them works.

 

 

 

SCRIPT ------------------------------

 

$Monitors = gwmi -N "root\wmi" -Class WmiMonitorListedSupportedSourceModes
$PossibleModes = new-object -TypeName system.collections.arraylist
foreach ($LoopInMonitors in $Monitors)
{
foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
{
$CurrentResolution = "$($LoopInPossibleResolution.HorizontalActivePixels) X $($LoopInPossibleResolution.VerticalActivePixels)"
$PossibleModes.add($CurrentResolution) | out-null
} # END foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
} # END foreach ($LoopInMonitors in $Monitors)
$PossibleModes

 

----------------------------------------------------------------------

 

I can use this code part in other scripts to have either an argument completion or a validateset in other scripts : 

 

ARGUMENT COMPLETER : ********************

Function set-favoriteresolution
{

[CmdletBinding()]
Param
(
[ArgumentCompleter( {
param ( $commandName,
$parameterName,
$wordToComplete,
$commandAst,
$fakeBoundParameters )


$Monitors = gwmi -N "root\wmi" -Class WmiMonitorListedSupportedSourceModes
$PossibleModes = new-object -TypeName system.collections.arraylist
foreach ($LoopInMonitors in $Monitors)
{
foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
{
$CurrentResolution = "$($LoopInPossibleResolution.HorizontalActivePixels) X $($LoopInPossibleResolution.VerticalActivePixels)"
$PossibleModes.add($CurrentResolution) | out-null
} # END foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
} # END foreach ($LoopInMonitors in $Monitors)
$PossibleModes


} )]
[string]$Resolution

)

write-host "$resolution" -ForegroundColor Green -BackgroundColor White


} # END Function set-favoriteresolution

 

**********************************************************

 

DynamicParameter

 

function Set-Resolution
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(

)

Dynamicparam {

$ResolutionAttribute = New-Object System.Management.Automation.ParameterAttribute
$ResolutionAttribute.Position = 0
$ResolutionAttribute.Mandatory = $true
$ResolutionAttribute.HelpMessage = "Select Screen resolution"

$Monitors = gwmi -N "root\wmi" -Class WmiMonitorListedSupportedSourceModes
$PossibleModes = new-object -TypeName system.collections.arraylist
foreach ($LoopInMonitors in $Monitors)
{
foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
{
$CurrentResolution = "$($LoopInPossibleResolution.HorizontalActivePixels) X $($LoopInPossibleResolution.VerticalActivePixels)"
$PossibleModes.add($CurrentResolution) | out-null
} # END foreach ($LoopInPossibleResolution in $LoopInMonitors.MonitorSourceModes)
} # END foreach ($LoopInMonitors in $Monitors)

$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($PossibleModes)

#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($ResolutionAttribute)
$attributeCollection.add($ValidateSetAttribute)

#add our paramater specifying the attribute collection
$ResolutionParameter = New-Object System.Management.Automation.RuntimeDefinedParameter('ScreenResolution', [string], $attributeCollection)

#expose the name of our parameter
$paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('ScreenResolution', $ResolutionParameter)
return $paramDictionary


}


Process
{
$PSBoundParameters
$SpecifiedResolution = $PSBoundParameters["ScreenResolution"]

write-host $SpecifiedResolution -ForegroundColor green

}

 


} # END function Verb-Noun

 

3 Replies

@Stephaneschaumburg 

 

Good question! I've never had a need to do this before, so I've learnt something new for reading up on it.

 

I used the official documentation linked below and had no issues getting the tab completion to work, though I only approached it from a very basic perspective. This was on PowerShell 5.1, for reference.

 

About functions argument completion - PowerShell | Microsoft Docs

 

[cmdletbinding()]
param(
    [parameter()][ArgumentCompleter(
        {
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters);

            $Values = [string[]]((Get-CimInstance -ClassName WmiMonitorListedSupportedSourceModes -Namespace root\wmi).MonitorSourceModes | Select-Object @{n="Resolution"; e={ "$($_.HorizontalActivePixels)x$($_.VerticalActivePixels)" }}).Resolution;

            if ($fakeBoundParameters.ContainsKey("Mode"))
            {
                $Values.Where({ $_.StartsWith($wordToComplete) });
            }
            else
            {
                $Values;
            }
        }
    )]$Mode
)

$Mode;

 

Cheers,

Lain

@LainRobertson 

 

Yes.

 

As I said....argument completers and dynamic parameters both works in functions

I just cannot get them to work in a add-member scriptmethod .

Thanks though.

 

Regards

@Stephaneschaumburg 

 

Sorry, I've re-read the question now.

 

Functions and methods are two different concepts and I don't believe PowerShell currently is designed to attempt parameter inspection on methods.

 

For example, if I called the code I dropped in above "Get-ScreenResolution" and then added it to a PSCustomObject like this:

 

$o = [PSCustomObject] @{ Name = "Fred"; }
$o | Add-Member -MemberType ScriptMethod -Name GetScreenResolution -Value { Param($Mode); Get-ScreenResolution -Mode $Mode; }

 

And then finally inspected the difference using Get-Member, like this (for both the function and then the scriptmethod):

 

${function:Get-ScreenResolution} | gm;
$o.GetScreenResolution | gm;

 

Then you get back a [System.Management.Automation.ScriptBlock] for the function (line 1) and a [System.Management.Automation.PSScriptMethod] for the scriptmethod (line 2) - and that difference is expected, and also why you're not likely to get tab completion, since you're not looking at the same kind of thing.

 

Maybe if PowerShell walked the chain and further inspected the "Script" attribute of the [System.Management.Automation.PSScriptMethod], it could probably do it, but I haven't read or seen anything to suggest it's possible to make it do so, and certainly not in a user-interactive manner.

 

So, when you call a function, you're calling it directly, hence tab completion is acknowledged. When you call a function added as a scriptmethod, you're calling the function indirectly via the PSScriptMethod instance.

 

Cheers,

Lain