Show-Object1.ps1

#[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
#[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
add-type -AssemblyName System.Windows.Forms -Verbose
add-type -AssemblyName System.Drawing -verbose
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 



    class Form3 : System.Windows.Forms.Form
    {
        # <summary>
        # Required designer variable.
        # </summary>
        [System.ComponentModel.IContainer]$components;

#region Windows Form Designer generated code

        # <summary>
        # Required method for Designer support - do not modify
        # the contents of $this method with the code editor.
        # </summary>
        [void] InitializeComponent()
        {
            $this.ObjectPath = [System.Windows.Forms.TextBox]::new();
            $this.menuStrip1 = [System.Windows.Forms.MenuStrip]::New();
            $this.splitContainer1 = [System.Windows.Forms.SplitContainer]::New();
            $this.splitContainer2 = [System.Windows.Forms.SplitContainer]::New();
            $this.treeView1 = [System.Windows.Forms.TreeView]::New();
            Add-Member -InputObject $this.treeView1 -MemberType NoteProperty -Name Form -Value $this
            $this.TypeDetailsTextBox = [System.Windows.Forms.TextBox]::new();
            $this.PropertyGrid = [System.Windows.Forms.DataGridView]::New();
            $this.findToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New();
            $this.exitToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New();
            $this.Edit = [System.Windows.Forms.ToolStripMenuItem]::New();
            $this.findToolStripMenuItem1 = [System.Windows.Forms.ToolStripMenuItem]::New();
            $this.findNextToolStripMenuItem = [System.Windows.Forms.ToolStripMenuItem]::New();
            $this.PropertyDataTable = [System.Data.DataTable]::New();
            $this.menuStrip1.SuspendLayout();
            ([System.ComponentModel.ISupportInitialize]($this.splitContainer1)).BeginInit();
            $this.splitContainer1.Panel1.SuspendLayout();
            $this.splitContainer1.Panel2.SuspendLayout();
            $this.splitContainer1.SuspendLayout();
            ([System.ComponentModel.ISupportInitialize]($this.splitContainer2)).BeginInit();
            $this.splitContainer2.Panel1.SuspendLayout();
            $this.splitContainer2.Panel2.SuspendLayout();
            $this.splitContainer2.SuspendLayout();
            ([System.ComponentModel.ISupportInitialize]($this.PropertyGrid)).BeginInit();
            $this.SuspendLayout();
            #
            # ObjectPath
            #
            $this.ObjectPath.Dock = "Bottom";
            $this.ObjectPath.Location = [System.Drawing.Point]::New(0, 430);
            $this.ObjectPath.Name = "ObjectPath";
            $this.ObjectPath.Size = [System.Drawing.Size]::New(889, 20);
            $this.ObjectPath.Font = $this.CurrentFont;
            $this.ObjectPath.TabIndex = 0;
            #
            # menuStrip1
            #

            $this.menuStrip1.Items.Add($this.findToolStripMenuItem)
            $this.menuStrip1.Items.Add($this.Edit)

            

            $this.menuStrip1.Location = [System.Drawing.Point]::New(0, 0);
            $this.menuStrip1.Name = "menuStrip1";
            $this.menuStrip1.Size = [System.Drawing.Size]::New(889, 24);
            $this.menuStrip1.TabIndex = 1;
            $this.menuStrip1.Text = "menuStrip1";
            #
            # splitContainer1
            #
            $this.splitContainer1.Dock = "Fill";
            $this.splitContainer1.Location = [System.Drawing.Point]::New(0, 24);
            $this.splitContainer1.Name = "splitContainer1";
            $this.splitContainer1.Orientation = "Horizontal";
            #
            # splitContainer1.Panel1
            #
            $this.splitContainer1.Panel1.Controls.Add($this.splitContainer2);
            #
            # splitContainer1.Panel2
            #
            $this.splitContainer1.Panel2.Controls.Add($this.TypeDetailsTextBox);
            $this.splitContainer1.Size = [System.Drawing.Size]::New(889, 406);
            $this.splitContainer1.SplitterDistance = 296;
            $this.splitContainer1.TabIndex = 2;
            #
            # splitContainer2
            #
            $this.splitContainer2.Dock = "Fill";
            $this.splitContainer2.Location = [System.Drawing.Point]::New(0, 0);
            $this.splitContainer2.Name = "splitContainer2";
            #
            # splitContainer2.Panel1
            #
            $this.splitContainer2.Panel1.Controls.Add($this.treeView1);
            #
            # splitContainer2.Panel2
            #
            $this.splitContainer2.Panel2.Controls.Add($this.PropertyGrid);
            $this.splitContainer2.Size = [System.Drawing.Size]::New(889, 296);
            $this.splitContainer2.SplitterDistance = 445;
            $this.splitContainer2.TabIndex = 0;
            #
            # treeView1
            #
            $this.treeView1.Dock = "Fill";
            $this.treeView1.Location = [System.Drawing.Point]::New(0, 0);
            $this.treeView1.Name = "treeView1";
            $this.treeView1.Size = [System.Drawing.Size]::New(445, 296);
            $this.treeView1.TabIndex = 0;
            $this.treeView1.Font = $this.currentFont;
            #
            # TypeDetailsTextBox
            #
            $this.TypeDetailsTextBox.Dock = "Fill";
            $this.TypeDetailsTextBox.Location = [System.Drawing.Point]::New(0, 0);
            $this.TypeDetailsTextBox.Multiline = $true;
            $this.TypeDetailsTextBox.Name = "TypeDetailsTextBox";
            $this.TypeDetailsTextBox.ScrollBars = "Both"
            $this.TypeDetailsTextBox.Size = [System.Drawing.Size]::New(889, 106);
            $this.TypeDetailsTextBox.TabIndex = 0;
            $this.TypeDetailsTextBox.Font = $this.currentFont;
            #
            # PropertyGrid
            #
            $this.PropertyGrid.ColumnHeadersHeightSizeMode = "AutoSize";
            $this.PropertyGrid.Dock = "Fill";
            $this.PropertyGrid.Location = [System.Drawing.Point]::New(0, 0);
            $this.PropertyGrid.Name = "PropertyGrid";
            $this.PropertyGrid.SelectionMode = "CellSelect";
            $this.PropertyGrid.Size = [System.Drawing.Size]::New(440, 296);
            $this.PropertyGrid.TabIndex = 0;
            $this.PropertyGrid.AutoSizeColumnsMode = "Fill";
            $this.PropertyGrid.Font = $this.currentFont;
            $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Type",[string]));
            $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Name",[string]));
            $this.PropertyDataTable.Columns.Add([System.Data.DataColumn]::new("Value",[Object]));
            $this.PropertyGrid.DataSource = $this.PropertyDataTable;
            #
            # findToolStripMenuItem
            #
            
            $this.findToolStripMenuItem.DropDownItems.Add($this.exitToolStripMenuItem);
            
            
            $this.findToolStripMenuItem.Name = "findToolStripMenuItem";
            $this.findToolStripMenuItem.Size = [System.Drawing.Size]::New(37, 20);
            $this.findToolStripMenuItem.Text = "File";
            #
            # exitToolStripMenuItem
            #
            $this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
            $this.exitToolStripMenuItem.Size = [System.Drawing.Size]::New(152, 22);
            $this.exitToolStripMenuItem.Text = "Exit";
            #
            # Edit
            #
            $this.Edit.DropDownItems.Add($this.findToolStripMenuItem1);
            $this.Edit.DropDownItems.Add($this.findNextToolStripMenuItem);
            $this.Edit.Name = "Edit";
            $this.Edit.Size = [System.Drawing.Size]::New(39, 20);
            $this.Edit.Text = "Edit";
            #
            # findToolStripMenuItem1
            #
            
            $this.findToolStripMenuItem1.Name = "findToolStripMenuItem1";
            $this.findToolStripMenuItem1.ShortcutKeys = ([System.Windows.Forms.Keys]"Control" -bor [System.Windows.Forms.Keys]"F");
            $this.findToolStripMenuItem1.Size = [System.Drawing.Size]::New(152, 22);
            $this.findToolStripMenuItem1.Text = "Find...";
            #
            # findNextToolStripMenuItem
            #
            $this.findNextToolStripMenuItem.Name = "findNextToolStripMenuItem";
            $this.findNextToolStripMenuItem.ShortcutKeys = [System.Windows.Forms.Keys]"F3";
            $this.findNextToolStripMenuItem.Size = [System.Drawing.Size]::New(152, 22);
            $this.findNextToolStripMenuItem.Text = "Find Next";
            #
            # Form3
            #
            $this.AutoScaleDimensions = [System.Drawing.SizeF]::New(6, 13);
            $this.AutoScaleMode = "Font";
            $this.ClientSize = [System.Drawing.Size]::New(889, 450);
            $this.Controls.Add($this.splitContainer1);
            $this.Controls.Add($this.ObjectPath);
            $this.Controls.Add($this.menuStrip1);
            $this.MainMenuStrip = $this.menuStrip1;
            $this.Name = "Form3";
            $this.Text = "Show-Object";
            $this.menuStrip1.ResumeLayout($false);
            $this.menuStrip1.PerformLayout();
            $this.splitContainer1.Panel1.ResumeLayout($false);
            $this.splitContainer1.Panel2.ResumeLayout($false);
            $this.splitContainer1.Panel2.PerformLayout();
            ([System.ComponentModel.ISupportInitialize]($this.splitContainer1)).EndInit();
            $this.splitContainer1.ResumeLayout($false);
            $this.splitContainer2.Panel1.ResumeLayout($false);
            $this.splitContainer2.Panel2.ResumeLayout($false);
            ([System.ComponentModel.ISupportInitialize]($this.splitContainer2)).EndInit();
            $this.splitContainer2.ResumeLayout($false);
            ([System.ComponentModel.ISupportInitialize]($this.PropertyGrid)).EndInit();
            $this.ResumeLayout($false);
            $this.PerformLayout();

            $this.treeView1.PathSeparator = "."
            $this.treeView1.ShowNodeToolTips = $true
            $this.treeView1.Add_AfterSelect( {$this.Form.OnAfterSelect($args[0], $args[1]) } )
            $this.treeView1.Add_BeforeExpand( { $this.Form.OnBeforeExpand($args[0], $args[1]) } )
            $this.treeView1.Add_KeyPress( {$this.Form.OnTreeViewKeyPress($args[0], $args[1])} )

            $this.Add_MouseWheel( { $this.OnMouseWheel($args[0], $args[1]) } )
            $this.Add_KeyUp( { $this.OnKeyUp($args[0], $args[1]) } )

        }

#endregion

        [System.Windows.Forms.TextBox]$ObjectPath;
        [System.Windows.Forms.MenuStrip]$menuStrip1;
        [System.Windows.Forms.SplitContainer]$splitContainer1;
        [System.Windows.Forms.ToolStripMenuItem]$findToolStripMenuItem;
        [System.Windows.Forms.ToolStripMenuItem]$exitToolStripMenuItem;
        [System.Windows.Forms.ToolStripMenuItem]$Edit;
        [System.Windows.Forms.ToolStripMenuItem]$findToolStripMenuItem1;
        [System.Windows.Forms.ToolStripMenuItem]$findNextToolStripMenuItem;
        [System.Windows.Forms.SplitContainer]$splitContainer2;
        [System.Windows.Forms.TreeView]$treeView1;
        [System.Windows.Forms.DataGridView]$PropertyGrid;
        [System.Data.DataTable]$PropertyDataTable;
        [System.Windows.Forms.TextBox]$TypeDetailsTextBox;
        [int]$currentFontSize = 12;
        [System.Drawing.Font]$currentFont = [System.Drawing.Font]::new("Consolas", $this.CurrentFontSize);

        [String]GetValueString($o)
        {
            if ($o -ne $null)
            {
                switch ($o.GetType().Fullname)
                {
                    "System.String" {return "`"$($o.tostring())`""}
                    "System.Boolean"{
                        if ($o) {return '$True'}
                        else    {return '$False'}
                    }
                    default { return $o.ToString()}
                }
            }
            return ""
        }

        ## A function to add a placeholder if required to a node.
        ## If there are any properties or children for this object, make a temporary
        ## node with the text "..." so that the node shows a '+' sign to be
        ## expanded.
        [void]AddPlaceholderIfRequired($node, $object)
        {
            if(-not $object -or $object -is [String]) { return }    
            if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or
                @($object.PSObject.Properties))
            {
                $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") )
            }
        }
    
        ## A function invoked when a node is selected.
        [void]OnAfterSelect($Sender, [System.Windows.Forms.TreeViewEventArgs]$TreeViewEventArgs)
        {   
            [System.Windows.Forms.TreeNode]$nodeSelected = $treeViewEventArgs.Node
    
            ## Walk through its parents, creating the virtual
            ## PowerShell syntax to access this property.
            [String]$nodePath = $this.GetPathForNode($nodeSelected)
    
            ## Now, invoke that PowerShell syntax to retrieve
            ## the value of the property.
            $resultObject = Invoke-Expression $nodePath
            $this.ObjectPath.Text = $nodePath
    
            ## If we got some output, put the object's member
            ## information in the text box.
            if($resultObject)
            {
                $this.TypeDetailsTextBox.Text = Get-Member -InputObject $resultObject | sort-object MemberType,Name | Out-String
            }
            $this.UpdatePropertyGrid($resultObject)
        }
    
        ## A function invoked when a node is selected.
        [void]UpdatePropertyGrid([Object]$Item)
        {   
            [System.Windows.Forms.DataGridView]$grid = $this.PropertyGrid;
            [System.Data.DataTable]$datatable = $this.PropertyDataTable;
            $datatable.Rows.Clear()
            
            if ($item -ne $null)
            {
                foreach ($p in Get-Member -input $item -MemberType Properties |sort Name)
                {
                    $row = $datatable.NewRow();
                    $row["Name"] = $p.name;
                    $value = $item.$($p.Name);
                    $row["Value"] = $value;
                    if ($value -ne $null)
                    {
                        $row["Type"] = $value.GetType().Fullname;
                    }
                    $datatable.Rows.Add($row)
                }
            }            
        }

        
        ## A function invoked when the user is about to expand a node
        [void]OnBeforeExpand($Sender, $TreeViewCancelEventArgs)
        {
            ## Determine the selected node
            $selectedNode = $TreeViewCancelEventArgs.Node
    
            ## If it has a child node that is the placeholder, clear
            ## the placehoder node.
            if($selectedNode.FirstNode -and
                ($selectedNode.FirstNode.Text -eq "..."))
            {
                $selectedNode.Nodes.Clear()
            }
            else
            {
                return
            }
    
            ## Walk through its parents, creating the virtual
            ## PowerShell syntax to access this property.
            $nodePath = $this.GetPathForNode($selectedNode)
            $resultObject = $null
            ## Now, invoke that PowerShell syntax to retrieve
            ## the value of the property.
            Invoke-Expression "`$resultObject = $nodePath"
    
            ## And populate the node with the result object.
            $this.PopulateNode($selectedNode, $resultObject)
        }
    
        ## A function to handle key presses on the tree view.
        ## In this case, we capture ^C to copy the path of
        ## the object property that we're currently viewing.
        [void]OnTreeViewKeyPress($Sender, [System.Windows.Forms.KeyEventArgs]$KeyPressEventArgs)
        {
            ## [Char] 3 = Control-C
            if($KeyPressEventArgs.KeyChar -eq 3)
            {
                $KeyPressEventArgs.Handled = $true
    
                ## Get the object path, and set it on the clipboard
                $node = $Sender.SelectedNode
                $nodePath = $this.GetPathForNode($node)
                [System.Windows.Forms.Clipboard]::SetText($nodePath)
    
                $this.Close()
            }
            elseif([System.Windows.Forms.Control]::ModifierKeys -eq "Control")
            {
                if($KeyPressEventArgs.KeyChar -eq '+')
                {
                    $this.CurrentFontSize++
                    $this.UpdateFonts()
            
                    $KeyPressEventArgs.Handled = $true
                }
                elseif($KeyPressEventArgs.KeyChar -eq '-')
                {
                    $this.CurrentFontSize--
                    if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 }
                    $this.UpdateFonts()
            
                    $KeyPressEventArgs.Handled = $true
                }
            }
        }

        ## A function to handle key presses on the form.
        ## In this case, we handle Ctrl-Plus and Ctrl-Minus
        ## to adjust font size.
        [void]OnKeyUp($Sender, [System.Windows.Forms.KeyEventArgs]$KeyUpEventArgs)
        {
            if([System.Windows.Forms.Control]::ModifierKeys -eq "Control")
            {
                if($KeyUpEventArgs.KeyCode -in 'Add','OemPlus')
                {
                    $this.CurrentFontSize++
                    $this.UpdateFonts()
            
                    $KeyUpEventArgs.Handled = $true
                }
                elseif($KeyUpEventArgs.KeyCode -in 'Subtract','OemMinus')
                {
                    $this.CurrentFontSize--
                    if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 }
                    $this.UpdateFonts()
            
                    $KeyUpEventArgs.Handled = $true
                }
                elseif($KeyUpEventArgs.KeyCode -eq 'D0')
                {
                    $this.CurrentFontSize = 12
                    $this.UpdateFonts()
            
                    $KeyUpEventArgs.Handled = $true
                }
            }
        }

        ## A function to handle mouse wheel scrolling.
        ## In this case, we translate Ctrl-Wheel to zoom.
        [void]OnMouseWheel($Sender, [System.Windows.Forms.MouseEventArgs]$MouseEventArgs)
        {
            if(
                ([System.Windows.Forms.Control]::ModifierKeys -eq "Control") -and
                ($MouseEventArgs.Delta -ne 0))
            {
                $this.CurrentFontSize += ($MouseEventArgs.Delta / 120)
                if($this.CurrentFontSize -lt 1) { $this.CurrentFontSize = 1 }

                $this.UpdateFonts()
                $MouseEventArgs.Handled = $true
            }
        }


        [void]OnClick($Sender, [System.Windows.Forms.MouseEventArgs]$MouseClickEventArgs)
        {
            if($MouseClickEventArgs.Button -eq "Right")
            {
                $this.menuStrip1.show()
            }
        }
    
    
        ## A function to walk through the parents of a node,
        ## creating virtual PowerShell syntax to access this property.
        [String]GetPathForNode([System.Windows.Forms.TreeNode]$Node)
        {
            $nodeElements = @()
    
            ## Go through all the parents, adding them so that
            ## $nodeElements is in order.
            while($Node)
            {
                $nodeElements = ,$Node + $nodeElements
                $Node = $Node.Parent
            }
    
            ## Now go through the node elements
            $nodePath = ""
            foreach($Node in $nodeElements)
            {
                $nodeName = $Node.Name
    
                ## If it was a node that PowerShell is able to enumerate
                ## (but not index), wrap it in the array cast operator.
                if($nodeName.StartsWith('@'))
                {
                    $nodeName = $nodeName.Substring(1)
                    $nodePath = "@(" + $nodePath + ")"
                }
                elseif($nodeName.StartsWith('['))
                {
                    ## If it's a child index, we don't need to
                    ## add the dot for property access
                }
                elseif($nodePath)
                {
                    ## Otherwise, we're accessing a property. Add a dot.
                    $nodePath += "."
                }
    
                ## Append the node name to the path
                $nodePath += $nodeName
            }
    
            ## And return the result
            return $nodePath
        }

        [Void]UpdateFonts()
        {
            $this.Currentfont = [System.Drawing.Font]::new("Consolas", $this.currentFontSize)
            $this.treeView1.Font = `
            $this.TypeDetailsTextBox.Font = `
            $this.PropertyGrid.Font = `
            $this.ObjectPath.Font = $this.Currentfont;
        }
    

        [void]ShowObject($inputObject)
        {
            ## Figure out the variable name to use when displaying the
            ## object navigation syntax. To do this, we look through all
            ## of the variables for the one with the same object identifier.
            $rootVariableName = dir variable:\* -Exclude InputObject,Args |
                Where-Object {
                    $_.Value -and
                    ($_.Value.GetType() -eq $InputObject.GetType()) -and
                    ($_.Value.GetHashCode() -eq $InputObject.GetHashCode())
            }
    
            ## If we got multiple, pick the first
            $rootVariableName = $rootVariableName| % Name | Select -First 1
    
            ## If we didn't find one, use a default name
            if(-not $rootVariableName)
            {
                $rootVariableName = "InputObject"
            }
    

            ## Create the root node, which represents the object
            ## we are trying to show.
            $root = New-Object Windows.Forms.TreeNode
            $root.ToolTipText = "[" + $InputObject.GetType() + "]"
            $root.Text = $InputObject
            $root.Name = '$' + $rootVariableName
            $root.Expand()
            $null = $this.treeView1.Nodes.Add($root)            
            $this.PopulateNode($root, $InputObject)

        }
        ## A function to add an object to the display tree
        [void]PopulateNode($node, $object)
        {
            ## If we've been asked to add a NULL object, just return
            if(-not $object) { return }
    
            ## If the object is a collection, then we need to add multiple
            ## children to the node
            if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object))
            {
                ## Some very rare collections don't support indexing (i.e.: $foo[0]).
                ## In this situation, PowerShell returns the parent object back when you
                ## try to access the [0] property.
                $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode()
    
                ## Go through all the items
                $count = 0
                foreach($childObjectValue in $object)
                {
                    ## Create the new node to add, with the node text of the item and
                    ## value, along with its type
                   $newChildNode = New-Object Windows.Forms.TreeNode
                    $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue"
                    $newChildNode.ToolTipText = "[" + $childObjectValue.GetType() + "]"
                   
                    ## Use the node name to keep track of the actual property name
                    ## and syntax to access that property.
                    ## If we can't use the index operator to access children, add
                    ## a special tag that we'll handle specially when displaying
                    ## the node names.
                    if($isOnlyEnumerable)
                    {
                        $newChildNode.Name = "@"
                    }
    
                    $newChildNode.Name += "[$count]"
                   $null = $node.Nodes.Add($newChildNode)               
    
                    ## If this node has children or properties, add a placeholder
                    ## node underneath so that the node shows a '+' sign to be
                    ## expanded.
                    $this.AddPlaceholderIfRequired($newChildNode, $childObjectValue)
    
                    $count++
                }
            }
            else
            {
                ## If the item was not a collection, then go through its
                ## properties
                foreach($child in $object.PSObject.Properties |Sort-Object Name)
                {
                    ## Figure out the value of the property, along with
                    ## its type.
                   $childObject = $child.Value
                    $childObjectType = $null
                    if($childObject -ne $null)
                    {
                        $childObjectType = $childObject.GetType()
                    }else
                    {
                        $childObjectType = $Object.gettype().getproperty($($child.Name)).PropertyType.Fullname
                    }
    
                    ## Create the new node to add, with the node text of the item and
                    ## value, along with its type
                   $childNode = New-Object Windows.Forms.TreeNode
                    $childNode.Text = $child.Name + " = $($this.GetValueString($childObject))"
                    $childNode.ToolTipText = "[" + $childObjectType + "]"
                    if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($childObject))
                    {
                        $childNode.ToolTipText += "[]"
                    }
                
                    $childNode.Name = $child.Name
                   $null = $node.Nodes.Add($childNode)
    
                    ## If this node has children or properties, add a placeholder
                    ## node underneath so that the node shows a '+' sign to be
                    ## expanded.
                    $this.AddPlaceholderIfRequired($childNode, $childObject)
                }
            }
        }

    }

    $f = [form3]::new()
    $f.InitializeComponent();
    $x = $args[0]
    $f.ShowObject($x)
    $null = $f.ShowDialog()
    $f.Dispose()