Enabling tab completion for the properties of a custom object

This topic contains 4 replies, has 3 voices, and was last updated by  Hugh Martin 6 months, 2 weeks ago.

  • Author
    Posts
  • #65803

    Hugh Martin
    Participant

    Although I'm not new to PowerShell, I am new to working with modules. In creating my first module, I am trying to get tab completion to work with the properties contained in one of the functions.

    Example: Get-MyFunction | Select-Object MyOb[tab key] should result in Get-MyFunctin | Select-Object MyObjectProperty

    In the function, I am creating an array to hold data from the pscustomobject. It is the array that is returned by the function.

    [System.Collections.ArrayList]$arrMyArray = @()

    Next, I loop through the relevant information, adding them to a pscustomobject:

    $MyObject = [PSCustomObject]@{
    			MyObjectProperty = $strValue1
    			MyOtherObjectProperty = $strValue2
    

    It is these properties that I want to enable tab completion.

    At the end of each loop, I add the information to the array object:

    $arrMyArray.Add($MyObject) | Out-Null

    Once all loops are completed, I return the the array:

    Return $arrMyArray

    Following the information in this post, I was able to get it to work, but even adding this code within my function will cause the properties to appear in the tab completion of all functions in the module that have an OutputType of 'array'.

    Any help or suggestions will be greatly appreciated.

  • #65805

    Daniel Krebs
    Moderator

    You'll need to roll your own class to get tab completion on properties.

    Example (PowerShell v5 and later)

    class ForumItemData {
        [String] $Name
        [String] $Status
    }
    
    function Get-ItemData {
    
        [CmdletBinding()]
        [OutputType('ForumItemData')]
        param ()
    
        $obj = [ForumItemData]::new()
        $obj.Name = 'Testing 123'
        $obj.Status = 'Active'
        $obj
    }
    
    Get-ItemData | Select-Object -Property Name, Status
    

    Example (PowerShell v4 and earlier)

    Add-Type -TypeDefinition @'
    using System;
    
    namespace PowerShellOrg.Forum
    {
        public class ItemData
        {
            public string Name { get; set; }
            public string Status { get; set; }
        }
    }
    '@ -ErrorAction SilentlyContinue
    
    function Get-ItemData {
    
        [CmdletBinding()]
        [OutputType('PowerShellOrg.Forum.ItemData')]
        param ()
    
        $obj = New-Object -TypeName 'PowerShellOrg.Forum.ItemData'
        $obj.Name = 'Testing 123'
        $obj.Status = 'Active'
        $obj
    }
    
    Get-ItemData | Select-Object -Property Name, Status
    
    • #65844

      Hugh Martin
      Participant

      Daniel,

      Thanks for the quick and thorough response. I will definitely incorporate this into my script.

  • #65806

    Don Jones
    Keymaster

    This is hard to do in a script.

    .NET uses Reflection to figure out what members a type has; in your case, your type is a PSCustomObject, which does not have any members. At least, none defined in its base, which is all .NET can use.

    For one, you're building your function very contrary to PowerShell patterns in terms of accumulating objects in an array. This blocks the pipeline, which is a Bad Thing. See https://devopscollective.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/accumulating-output-in-a-function.html. The Return keyword is misleading, and in a function you probably should avoid it. In a function, it's an alias to Write-Output, which is preferred. Continually adding objects to an array is also a decent performance hit as the array grows.

    What you'd need to do, in order for this to work correctly, is register a type (e.g., Add-Type) that has the members you want, and then output objects of that type, and then modify your function declaration to indicate it was outputting objects of that type. It's honestly a lot of work, and it's kind of overkill in terms of a "script cmdlet." You should also be outputting objects one at a time to the pipeline so that PowerShell can stream objects instead of dealing with giant collections.

    • #65845

      Hugh Martin
      Participant

      Thanks Don. It appears I have a lot more homework to do. Thanks for the feedback and tips.

You must be logged in to reply to this topic.