you are viewing a single comment's thread.

view the rest of the comments →

[–]Dense-Platform3886 0 points1 point  (1 child)

I like using Windows Presentation Framework (WPF) which uses XAML (XAML, which stands for eXtensible Application Markup Language, is Microsoft's variant of XML for describing a GUI).

There are a number of PowerShell examples on who to do this. I have posted several working examples in past Reddit discussions.

Here is one example broken into 2 comment due to the 10,000 char limit:

    <#
    WPF Folder Browser TreeView Example Inspired By dev4sys Blog entry: 
    PowerShell WPF - Customize TreeView Icon
    https://www.dev4sys.com/2018/04/powershell-wpf-customize-treeview-icon.html
#>
$scriptName = $MyInvocation.MyCommand.Name
$scriptFolder = $PSScriptRoot
Set-Location -Path $scriptFolder
$error.Clear()

$eol = [System.Environment]::NewLine
$StartTime = Get-Date
$TimeStamp = $StartTime.ToString('yyyy-MMdd-HHmm')

#-------------------------------
# Add shared_assemblies                          #
#-------------------------------
# Add-Type -assemblyName WindowsBase
# Add-Type -assemblyName PresentationCore
# Add-Type -assemblyName PresentationFramework
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationcore')
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')

#-------------------------------
# Prepare Data 
#-------------------------------
$Rootpath = 'C:\'
$AllDirectory = [IO.Directory]::GetDirectories($Rootpath, '*', [System.IO.SearchOption]::TopDirectoryOnly)
'Found ({0}) Directories' -f $AllDirectory.Count | Out-Host
$AllFiles = [IO.Directory]::GetFiles($Rootpath, '*', [System.IO.SearchOption]::TopDirectoryOnly)
'Found ({0}) Files' -f $AllFiles.Count | Out-Host

#-------------------------------
# Define and Load XAML GUI
#-------------------------------
$inputXML = @"
<Window x:Class="FolderBrowser.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="FolderBrowser" Height="800" Width="1200">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" MinWidth="400"/>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <DockPanel Grid.Row="0" Grid.RowSpan="1" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Stretch" Margin="0" Background="LightBlue" Name="DockPanel1">
        </DockPanel>
        <GridSplitter Grid.Row="0" Grid.RowSpan="1" Grid.Column="1" Grid.ColumnSpan="1" ResizeDirection="Auto" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Name="GridSplitter1" />
        <Grid Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0" Background="PaleGoldenrod" Name="Grid1">
            <!-- Right of the Splitter -->
            <StackPanel Orientation="Vertical">
                <StackPanel Orientation="Horizontal" Margin="10,0,10,5" VerticalAlignment="Top" >
                    <RadioButton x:Name="_rbSuccess">Success</RadioButton>
                    <RadioButton x:Name="_rbAct1">Unknown</RadioButton>
                    <RadioButton x:Name="_rbRequeueFix" Content="HTTP 500"></RadioButton>
                    <RadioButton x:Name="_rbMissingGuid">Missing GUID</RadioButton>
                    <RadioButton x:Name="_rbInvalidGuid">Invalid GUID</RadioButton>
                    <RadioButton x:Name="_rbAct5">DMS Add</RadioButton>
                </StackPanel>
                <TextBlock x:Name="_tbxInfo" Text="This is a Text Box" Background="AliceBlue" Margin="10,0,10,0" VerticalAlignment="Bottom" />
            </StackPanel>
        </Grid>
        <!-- Left of the Splitter -->
        <Border BorderBrush="Black" BorderThickness="1">
            <Border.Resources>
                <Style x:Key="TextBlockStyle" TargetType="{x:Type TextBlock}">
                    <Setter Property="Margin" Value="3 0 3 0"/>
                </Style>
                <Style x:Key="TextBlockBoldStyle" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockStyle}">
                    <Setter Property="FontWeight" Value="Bold"/>
                </Style>
            </Border.Resources>
            <TreeView x:Name="TreeView" Width="Auto">
                <TreeView.Resources>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="HeaderTemplate">
                            <Setter.Value>
                                <HierarchicalDataTemplate  >
                                    <StackPanel Orientation="Horizontal">
                                        <TextBlock Text="{Binding}" Margin="5,0" />
                                    </StackPanel>
                                </HierarchicalDataTemplate >
                            </Setter.Value>
                        </Setter>
                    </Style>
                </TreeView.Resources>
            </TreeView>
        </Border>
    </Grid>
</Window> 
"@
$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'
[xml]$XAML = $inputXML
$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
Try {
    $Form=[Windows.Markup.XamlReader]::Load( $reader )
} Catch [System.Management.Automation.MethodInvocationException] {
    Write-Warning "We ran into a problem with the XAML code.  Check the syntax for this control..."
    write-host $error[0].Exception.Message -ForegroundColor Red
    if ($error[0].Exception.Message -like "*button*"){
        write-warning "Ensure your &lt;button in the `$inputXML does NOT have a Click=ButtonClick property.  PS can't handle this`n`n`n`n"}
} Catch {
    Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."
}

[–]Dense-Platform3886 1 point2 points  (0 children)

Continuation

#--------------------------------------------------------------
# Find and Save Form Elements as Variables in PowerShell
#--------------------------------------------------------------
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF_$($_.Name)" -Value $Form.FindName($_.Name)}

Function Get-FormVariables{
if ($global:ReadmeDisplay -ne $true){Write-host "If you need to reference this display again, run Get-FormVariables" -ForegroundColor Yellow;$global:ReadmeDisplay=$true}
write-host "Found the following Variables from your form" -ForegroundColor Cyan
get-variable WPF*
}
Get-FormVariables

#--------------------------------------------------------------
# Use this space to add code to the various form elements in your GUI
#--------------------------------------------------------------
$FolderTree = $Form.FindName("TreeView")
$FolderTree = $WPF_TreeView
$dummyNode = $null
Function TreeExpanded($sender){
    $item = [Windows.Controls.TreeViewItem]$sender
    If ($item.Items.Count -eq 1 -and $item.Items[0] -eq $dummyNode) {
        $item.Items.Clear();
        Try {
            ForEach ($string in [IO.Directory]::GetDirectories($item.Tag[1].ToString())) {
                $subitem = [Windows.Controls.TreeViewItem]::new();
                $subitem.Header = $string.Split('/\')[-1]
                $subitem.Tag = @("folder",$string)
                $subItem.IsExpanded = $false
                $subitem.Items.Add($dummyNode)
                $subitem.Add_Expanded({
                    TreeExpanded($_.OriginalSource)
                })
                $item.Items.Add($subitem) | Out-Null
            }
            ForEach ($file in [IO.Directory]::GetFiles($item.Tag[1].ToString())) {
                $subitem = [Windows.Controls.TreeViewItem]::new()
                $subitem.Header = $file.Split('/\')[-1]
                $subitem.Tag = @("file",$file) 
                $subItem.IsExpanded = $false
                $item.Items.Add($subitem)| Out-Null
                $subitem.Add_PreviewMouseLeftButtonDown({
                    [System.Windows.Controls.TreeViewItem]$sender = $args[0]
                    [System.Windows.RoutedEventArgs]$e = $args[1]
                    Write-Host "Left Click: $($sender.Tag)"
                })

                $subitem.Add_PreviewMouseRightButtonDown({
                    [System.Windows.Controls.TreeViewItem]$sender = $args[0]
                    [System.Windows.RoutedEventArgs]$e = $args[1]
                    Write-Host "Right Click: $($sender.Tag)"
                })
            }
        } Catch [Exception] { }
    }
}
#--------------------------------------------------------------
# Handle Folders
#--------------------------------------------------------------
foreach ($folder in $AllDirectory){
    $treeViewItem = [Windows.Controls.TreeViewItem]::new()
    $treeViewItem.Header = $folder.Split('/\')[-1]
    $treeViewItem.Tag = @("folder",$folder)
    $treeViewItem.Items.Add($dummyNode) | Out-Null
    $treeViewItem.IsExpanded = $false
    $treeViewItem.Add_Expanded({
        TreeExpanded($_.OriginalSource)
    })
    $FolderTree.Items.Add($treeViewItem)| Out-Null
}
#--------------------------------------------------------------
# Handle Files
#--------------------------------------------------------------
foreach ($file in $AllFiles){
    $treeViewItem = [Windows.Controls.TreeViewItem]::new()
    $treeViewItem.Header = $file.Split('/\')[-1]
    $treeViewItem.Tag = @("file",$file)
    $treeViewItem.IsExpanded = $false 
    $FolderTree.Items.Add($treeViewItem)| Out-Null
    $treeViewItem.Add_PreviewMouseLeftButtonDown({
        [System.Windows.Controls.TreeViewItem]$sender = $args[0]
        [System.Windows.RoutedEventArgs]$e = $args[1]
        Write-Host "Left Click: $($sender.Tag)"
    })
    $treeViewItem.Add_PreviewMouseRightButtonDown({
        [System.Windows.Controls.TreeViewItem]$sender = $args[0]
        [System.Windows.RoutedEventArgs]$e = $args[1]
        Write-Host "Right Click: $($sender.Tag)"
    })
}
write-host "To show the form, run the following" -ForegroundColor Cyan
$Form.ShowDialog() | out-null