private/implicitwindow/Get-UiBoundBlockParameter.ps1

function Get-UiBoundBlockParameter {
    <#
    .SYNOPSIS
        Works out which parameter a block binds to, by the name written in front of it when
        there is one and by position otherwise.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [System.Management.Automation.Language.CommandAst]$Command,

        [Parameter(Mandatory)]
        [System.Management.Automation.Language.ScriptBlockExpressionAst]$Block
    )

    $name = Get-UiPlainCommandName -Command $Command
    if (!$name) { return $null }

    if (!$script:uiBlockParameterFacts) {
        $script:uiBlockParameterFacts = [System.Collections.Generic.Dictionary[string, object]]::new([StringComparer]::OrdinalIgnoreCase)
    }

    $facts = $null
    if (!$script:uiBlockParameterFacts.TryGetValue($name, [ref]$facts)) {
        $facts    = @{ Positional = $null; Switches = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) }
        $resolved = Get-Command -Name $name -Module 'PsUi' -ErrorAction SilentlyContinue

        if ($resolved) {
            $lowest = [int]::MaxValue
            foreach ($parameter in $resolved.Parameters.Values) {
                if ($parameter.SwitchParameter) { [void]$facts.Switches.Add($parameter.Name) }
                if ($parameter.ParameterType -ne [scriptblock]) { continue }
                foreach ($attribute in $parameter.Attributes) {
                    if ($attribute -isnot [System.Management.Automation.ParameterAttribute]) { continue }
                    if ($attribute.Position -lt 0 -or $attribute.Position -ge $lowest) { continue }
                    $lowest           = $attribute.Position
                    $facts.Positional = $parameter.Name
                }
            }
        }
        $script:uiBlockParameterFacts[$name] = $facts
    }

    # -Content { } arrives as the parameter element then the block, so the element in front is the parameter it binds to. A switch in front takes no value, and a block behind one is really binding by position.
    # Names are read as written, so an abbreviated -Cont or -Full misses both tests and the block falls through as deferred. The cost is a control left out, which is prolly on the safe side.
    $index = $Command.CommandElements.IndexOf($Block)
    if ($index -gt 0) {
        $previous = $Command.CommandElements[$index - 1] -as [System.Management.Automation.Language.CommandParameterAst]
        if ($previous -and !$previous.Argument -and !$facts.Switches.Contains($previous.ParameterName)) { return $previous.ParameterName }
    }

    # New-UiPanel { } binds by position, and no PsUi command takes two blocks that way.
    return $facts.Positional
}