├── .gitignore ├── PSScriptMenuGui ├── examples │ ├── example_text_file.txt │ ├── example_target.cmd │ ├── pwsh7.ico │ ├── example_target.ps1 │ ├── PSScriptMenuGui.ps1 │ ├── PSScriptMenuGui_all_options.ps1 │ └── example_data.csv ├── xaml │ ├── end.xaml │ ├── heading.xaml │ ├── item.xaml │ └── start.xaml ├── PSScriptMenuGui.psm1 ├── PSScriptMenuGui.psd1 ├── public │ └── functions.ps1 └── private │ └── functions.ps1 ├── demo.gif ├── demo.mp4 ├── excel.png ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/example_text_file.txt: -------------------------------------------------------------------------------- 1 | hello world -------------------------------------------------------------------------------- /PSScriptMenuGui/xaml/end.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-osull/PowerShell-Script-Menu-Gui/HEAD/demo.gif -------------------------------------------------------------------------------- /demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-osull/PowerShell-Script-Menu-Gui/HEAD/demo.mp4 -------------------------------------------------------------------------------- /excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-osull/PowerShell-Script-Menu-Gui/HEAD/excel.png -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/example_target.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo CMD script called from PSScriptMenuGui.ps1 3 | pause -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/pwsh7.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dan-osull/PowerShell-Script-Menu-Gui/HEAD/PSScriptMenuGui/examples/pwsh7.ico -------------------------------------------------------------------------------- /PSScriptMenuGui/xaml/heading.xaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/example_target.ps1: -------------------------------------------------------------------------------- 1 | param ($message) 2 | "PowerShell script called from PSScriptMenuGui.ps1" 3 | if ($message) { 4 | "`$message = $message" 5 | } 6 | $PSVersionTable 7 | Read-Host "Press Enter to continue" -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/PSScriptMenuGui.ps1: -------------------------------------------------------------------------------- 1 | #region Setup 2 | Set-Location $PSScriptRoot 3 | Remove-Module PSScriptMenuGui -ErrorAction SilentlyContinue 4 | try { 5 | Import-Module PSScriptMenuGui -ErrorAction Stop 6 | } 7 | catch { 8 | Write-Warning $_ 9 | Write-Verbose 'Attempting to import from parent directory...' -Verbose 10 | Import-Module '..\' 11 | } 12 | #endregion 13 | 14 | Show-ScriptMenuGui -csvPath '.\example_data.csv' -Verbose -------------------------------------------------------------------------------- /PSScriptMenuGui/xaml/item.xaml: -------------------------------------------------------------------------------- 1 | 4 | INSERT_DESCRIPTION -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/PSScriptMenuGui_all_options.ps1: -------------------------------------------------------------------------------- 1 | #region Setup 2 | Set-Location $PSScriptRoot 3 | Remove-Module PSScriptMenuGui -ErrorAction SilentlyContinue 4 | try { 5 | Import-Module PSScriptMenuGui -ErrorAction Stop 6 | } 7 | catch { 8 | Write-Warning $_ 9 | Write-Verbose 'Attempting to import from parent directory...' -Verbose 10 | Import-Module '..\' 11 | } 12 | #endregion 13 | 14 | $params = @{ 15 | csvPath = '.\example_data.csv' 16 | windowTitle = 'Example with all options' 17 | buttonForegroundColor = 'Azure' 18 | buttonBackgroundColor = '#C00077' 19 | iconPath = '.\pwsh7.ico' 20 | hideConsole = $true 21 | noExit = $true 22 | Verbose = $true 23 | } 24 | Show-ScriptMenuGui @params -------------------------------------------------------------------------------- /PSScriptMenuGui/PSScriptMenuGui.psm1: -------------------------------------------------------------------------------- 1 | if ($PSEdition -eq 'Core') { 2 | if (-not $IsWindows) { 3 | throw 'This module only runs on Windows' 4 | } 5 | if ($PSVersionTable.PSVersion.Major -eq 6) { 6 | throw 'This module is not compatible with PowerShell Core 6' 7 | } 8 | } 9 | 10 | # Get public and private function definition files 11 | # Based on: https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psm1 12 | $scripts = @() 13 | $scripts += Get-ChildItem -Path $PSScriptRoot\public\*.ps1 -ErrorAction SilentlyContinue 14 | $scripts += Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue 15 | 16 | # Dot source the files 17 | ForEach ($script in $scripts) { 18 | try { 19 | . $script.FullName 20 | } 21 | catch { 22 | throw 23 | } 24 | } 25 | 26 | # Used to get files from xaml and examples subfolders 27 | $moduleRoot = $PSScriptRoot -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dan O'Sullivan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PSScriptMenuGui/examples/example_data.csv: -------------------------------------------------------------------------------- 1 | Section,Method,Command,Arguments,Name,Description 2 | Old school,cmd,.\example_target.cmd,,Example 1:cmd,.cmd file 3 | Old school,cmd,taskmgr.exe,,Example 2:cmd,External executable 4 | Old school,cmd,notepad.exe,example_text_file,Example 3:cmd,External executable with arguments 5 | Less old,powershell_file,.\example_target.ps1,,Example 4:powershell_file,.ps1 file called with powershell.exe 6 | Less old,powershell_inline,"""Inline script"";$PSVersionTable;Read-Host ""Press Enter to continue""",,Example 5:powershell_inline,Inline script called with powershell.exe 7 | Less old,powershell_inline,$PSVersionTable, -NoExit -WindowStyle Maximized,Example 6:powershell_inline,Additional powershell.exe arguments 8 | The future,pwsh_file,.\example_target.ps1,,Example 7:pwsh_file,.ps1 file called with pwsh.exe 9 | The future,pwsh_inline,"""Inline script"";$PSVersionTable;Read-Host ""Press Enter to continue""",,Example 8:pwsh_inline,Inline script called with pwsh.exe 10 | The future,pwsh_inline,"& .\example_target.ps1 -Message ""passed in via param""",,Example 9:pwsh_inline,.ps1 file called with parameter 11 | -------------------------------------------------------------------------------- /PSScriptMenuGui/xaml/start.xaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PSScriptMenuGui/PSScriptMenuGui.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSScriptMenuGui' 3 | # 4 | # Generated by: Dan O'Sullivan 5 | # 6 | # Generated on: 10/7/2019 7 | # 8 | 9 | @{ 10 | 11 | # Script module or binary module file associated with this manifest. 12 | RootModule = 'PSScriptMenuGui.psm1' 13 | 14 | # Version number of this module. 15 | ModuleVersion = '1.0.1' 16 | 17 | # Supported PSEditions 18 | # CompatiblePSEditions = @() 19 | 20 | # ID used to uniquely identify this module 21 | GUID = '103460b8-4f8b-471c-8f70-41e493070656' 22 | 23 | # Author of this module 24 | Author = "Dan O'Sullivan" 25 | 26 | # Company or vendor of this module 27 | # CompanyName = '' 28 | 29 | # Copyright statement for this module 30 | Copyright = "(c) Dan O'Sullivan. Released under the MIT License." 31 | 32 | # Description of the functionality provided by this module 33 | Description = 'Use a CSV file to make a graphical menu of PowerShell scripts. Easy to customise and fast to launch. 34 | 35 | You can also add Windows programs and files to the menu. 36 | 37 | See the Project Site on GitHub for full documentation.' 38 | 39 | # Minimum version of the PowerShell engine required by this module 40 | PowerShellVersion = '5.1' 41 | 42 | # Name of the PowerShell host required by this module 43 | # PowerShellHostName = '' 44 | 45 | # Minimum version of the PowerShell host required by this module 46 | # PowerShellHostVersion = '' 47 | 48 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 49 | # DotNetFrameworkVersion = '' 50 | 51 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 52 | # CLRVersion = '' 53 | 54 | # Processor architecture (None, X86, Amd64) required by this module 55 | # ProcessorArchitecture = '' 56 | 57 | # Modules that must be imported into the global environment prior to importing this module 58 | # RequiredModules = @() 59 | 60 | # Assemblies that must be loaded prior to importing this module 61 | # RequiredAssemblies = @() 62 | 63 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 64 | # ScriptsToProcess = @() 65 | 66 | # Type files (.ps1xml) to be loaded when importing this module 67 | # TypesToProcess = @() 68 | 69 | # Format files (.ps1xml) to be loaded when importing this module 70 | # FormatsToProcess = @() 71 | 72 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 73 | # NestedModules = @() 74 | 75 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 76 | FunctionsToExport = @( 77 | 'Show-ScriptMenuGui', 78 | 'New-ScriptMenuGuiExample' 79 | ) 80 | 81 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 82 | CmdletsToExport = @() 83 | 84 | # Variables to export from this module 85 | VariablesToExport = '*' 86 | 87 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 88 | AliasesToExport = @() 89 | 90 | # DSC resources to export from this module 91 | # DscResourcesToExport = @() 92 | 93 | # List of all modules packaged with this module 94 | # ModuleList = @() 95 | 96 | # List of all files packaged with this module 97 | # FileList = @() 98 | 99 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 100 | PrivateData = @{ 101 | 102 | PSData = @{ 103 | 104 | # Tags applied to this module. These help with module discovery in online galleries. 105 | Tags = @('PSEdition_Desktop','PSEdition_Core','Windows') 106 | 107 | # A URL to the license for this module. 108 | LicenseUri = 'https://github.com/weebsnore/PowerShell-Script-Menu-Gui/blob/master/LICENSE' 109 | 110 | # A URL to the main website for this project. 111 | ProjectUri = 'https://github.com/weebsnore/PowerShell-Script-Menu-Gui' 112 | 113 | # A URL to an icon representing this module. 114 | # IconUri = '' 115 | 116 | # ReleaseNotes of this module 117 | # ReleaseNotes = '' 118 | 119 | # Prerelease string of this module 120 | # Prerelease = '' 121 | 122 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 123 | RequireLicenseAcceptance = $false 124 | 125 | # External dependent modules of this module 126 | # ExternalModuleDependencies = @() 127 | 128 | } # End of PSData hashtable 129 | 130 | } # End of PrivateData hashtable 131 | 132 | # HelpInfo URI of this module 133 | # HelpInfoURI = '' 134 | 135 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 136 | # DefaultCommandPrefix = '' 137 | 138 | } 139 | 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PSScriptMenuGui 2 | 3 | [![PSGallery Version](https://img.shields.io/powershellgallery/v/PSScriptMenuGui.png?style=for-the-badge&logo=powershell&label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) [![PSGallery Downloads](https://img.shields.io/powershellgallery/dt/PSScriptMenuGui.png?style=for-the-badge&label=Downloads)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) [![PSGallery Platform](https://img.shields.io/powershellgallery/p/PSScriptMenuGui.png?style=for-the-badge&label=Platform)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) 4 | 5 | Do you have favourite scripts that go forgotten? 6 | 7 | Does your organisation have scripts that would be useful to frontline staff who are not comfortable with the command line? 8 | 9 | This module uses a CSV file to make a graphical menu of PowerShell scripts. 10 | 11 | It's easy to customise and fast to launch. 12 | 13 | You can also add Windows programs and files to the menu. 14 | 15 | Just a few minutes to setup and - *click! click!* - you're away! 16 | 17 | ![](demo.gif) 18 | 19 | ## Try it out 20 | 21 | ### Tutorial 22 | 23 | Looking for a step-by-step introduction? [**See this blog post.**](https://blog.osull.com/2019/11/06/tutorial-use-a-csv-file-to-make-a-graphical-menu-of-powershell-scripts/) 24 | 25 | ### Install from the PowerShell Gallery 26 | 27 | Install-Module PSScriptMenuGui -Scope CurrentUser 28 | New-ScriptMenuGuiExample 29 | cd PSScriptMenuGui_example 30 | .\PSScriptMenuGui.ps1 31 | 32 | ### Clone from GitHub 33 | 34 | git clone https://github.com/weebsnore/PowerShell-Script-Menu-Gui 35 | cd PowerShell-Script-Menu-Gui\PSScriptMenuGui\examples 36 | .\PSScriptMenuGui.ps1 37 | 38 | ## Problems and feedback 39 | 40 | How are you finding the module? Is it useful? *(Please share a screenshot!)* 41 | 42 | Are you stuck? Do you want a feature? 43 | 44 | Please [open a GitHub issue](https://github.com/weebsnore/PowerShell-Script-Menu-Gui/issues) or get in touch. 45 | 46 | [@dan_osull.com](https://twitter.com/dan_osull_com/) *- follow me for updates!* 47 | https://blog.osull.com 48 | powershell@osull.com 49 | 50 | ## Compatibility 51 | 52 | Tested with **PowerShell 5.1 x64** and **PowerShell 7 x64** on Windows 10. 53 | 54 | ## Basic usage 55 | 56 | Show-ScriptMenuGui -csvPath '.\example_data.csv' -Verbose 57 | 58 | ## Show-ScriptMenuGui options 59 | 60 | Parameter | What is it? 61 | :--- |:--- 62 | `-csvPath` | Path to CSV file that defines the menu. See [CSV reference](#csv-reference), below. 63 | `-windowTitle` *(optional)* | Custom title for the menu window 64 | `-buttonForegroundColor` *(optional)* | Custom button foreground (text) color. Hex codes (e.g. `#C00077`) and color names (e.g. `Azure`) are valid. See [.NET Color Class](https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.colors). 65 | `-buttonBackgroundColor` *(optional)* | Custom button background color 66 | `-iconPath` *(optional)* | Path to .ico file for use in menu 67 | `-hideConsole` *(optional)* | Hide the PowerShell console that the menu is called from. **Note:** This means you won't be able to see any errors from button clicks. If things aren't working, this should be the first thing you stop using. 68 | `-noExit` *(optional)* | Start all PowerShell instances with `-NoExit` *("Does not exit after running startup commands.")*. **Note:** You can set `-NoExit` on individual menu items by using the *Arguments* column. See [CSV reference](#csv-reference), below. 69 | 70 | See [`PSScriptMenuGui_all_options.ps1`](PSScriptMenuGui/examples/PSScriptMenuGui_all_options.ps1) for an example using every option. 71 | 72 | ## CSV reference 73 | 74 | This table details how to lay out the CSV file for your menu. 75 | 76 | The top row of your CSV should contain the column headers. Each row after this defines a menu item. 77 | 78 | Column header | What is it? 79 | :--- |:--- 80 | Section *(optional)* | Text for heading 81 | Method | What happens when you click the button? Valid options: `cmd` \| `powershell_file` \| `powershell_inline` \| `pwsh_file` \| `pwsh_inline` 82 | Command | Path to target script/executable (`cmd` or `_file` methods) ***or*** PowerShell commands (`_inline` methods) 83 | Arguments *(optional)* | Arguments to pass to target executable (`cmd` method) ***or*** to the PowerShell exe (other methods) 84 | Name | Text for button 85 | Description *(optional)* | Text for description 86 | 87 | ### Some examples 88 | 89 | Section | Method | Command | Arguments | Name | Description 90 | :---|:---|:---|:---|:---|:--- 91 | Old school | `cmd` | `taskmgr.exe` | | Example 2: cmd | External executable 92 | Old school | `cmd` | `notepad.exe` | `example_text_file` | Example 3: cmd | External executable with arguments 93 | Less old | `powershell_file` | `example_target.ps1` | | Example 4: powershell_file | .ps1 file called with powershell.exe 94 | Less old | `powershell_inline` | `$PSVersionTable` | `-NoExit -WindowStyle Maximized` | Example 6: powershell_inline | Additional powershell.exe arguments 95 | The future | `pwsh_file` | `example_target.ps1` | | Example 7: pwsh_file | .ps1 file called with pwsh.exe 96 | The future | `pwsh_inline` | `& .\example_target.ps1 -Message "passed in via param"` | |Example 9: pwsh_inline | .ps1 file called with parameter 97 | 98 | See [`example_data.csv`](PSScriptMenuGui/examples/example_data.csv) for further examples in CSV format. 99 | 100 | ### Tips 101 | 102 | - Relative paths, network paths and paths in your environment should work. 103 | - `` is supported in text fields. 104 | - You can add multiple `_inline` commands by separating with a semi-colon (`;`) 105 | - Excel makes a good editor! 106 | - But watch out for Excel turning e.g. `-NoExit` into a formula. Best workaround is to prefix with a space. 107 | 108 | ![](excel.png) 109 | -------------------------------------------------------------------------------- /PSScriptMenuGui/public/functions.ps1: -------------------------------------------------------------------------------- 1 | Function Show-ScriptMenuGui { 2 | <# 3 | .SYNOPSIS 4 | Use a CSV file to make a graphical menu of PowerShell scripts. Easy to customise and fast to launch. 5 | .DESCRIPTION 6 | Do you have favourite scripts that go forgotten? 7 | 8 | Does your organisation have scripts that would be useful to frontline staff who are not comfortable with the command line? 9 | 10 | This module uses a CSV file to make a graphical menu of PowerShell scripts. 11 | 12 | You can also add Windows programs and files to the menu. 13 | .PARAMETER csvPath 14 | Path to CSV file that defines the menu. 15 | 16 | See CSV reference: https://github.com/weebsnore/PowerShell-Script-Menu-Gui 17 | .PARAMETER windowTitle 18 | Custom title for the menu window. 19 | .PARAMETER buttonForegroundColor 20 | Custom button foreground (text) color. 21 | 22 | Hex codes (e.g. #C00077) and color names (e.g. Azure) are valid. 23 | 24 | See .NET Color Class: https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.colors 25 | .PARAMETER buttonBackgroundColor 26 | Custom button background color. 27 | .PARAMETER iconPath 28 | Path to .ico file for use in menu. 29 | .PARAMETER hideConsole 30 | Hide the PowerShell console that the menu is called from. 31 | 32 | Note: This means you won't be able to see any errors from button clicks. If things aren't working, this should be the first thing you stop using. 33 | .PARAMETER noExit 34 | Start all PowerShell instances with -NoExit ("Does not exit after running startup commands.") 35 | 36 | Note: You can set -NoExit on individual menu items by using the Arguments column. 37 | 38 | See CSV reference: https://github.com/weebsnore/PowerShell-Script-Menu-Gui 39 | .EXAMPLE 40 | Show-ScriptMenuGui -csvPath '.\example_data.csv' -Verbose 41 | .NOTES 42 | Run New-ScriptMenuGuiExample to get some example files 43 | .LINK 44 | https://github.com/weebsnore/PowerShell-Script-Menu-Gui 45 | #> 46 | [CmdletBinding()] 47 | param( 48 | [string][Parameter(Mandatory)]$csvPath, 49 | [string]$windowTitle = 'PowerShell Script Menu', 50 | [string]$buttonForegroundColor = 'White', 51 | [string]$buttonBackgroundColor = '#366EE8', 52 | [string]$iconPath, 53 | [switch]$hideConsole, 54 | [switch]$noExit 55 | ) 56 | Write-Verbose 'Show-ScriptMenuGui started' 57 | 58 | # -Verbose value, to pass to select cmdlets 59 | $verbose = $false 60 | try { 61 | if ($PSBoundParameters['Verbose'].ToString() -eq 'True') { 62 | $verbose = $true 63 | } 64 | } 65 | catch {} 66 | 67 | $csvData = Import-CSV -Path $csvPath -ErrorAction Stop 68 | Write-Verbose "Got $($csvData.Count) CSV rows" 69 | 70 | # Add unique Reference to each item 71 | # Used as x:Name of button and to look up action on click 72 | $i = 0 73 | $csvData | ForEach-Object { 74 | $_ | Add-Member -Name Reference -MemberType NoteProperty -Value "button$i" 75 | $i++ 76 | } 77 | 78 | # Begin constructing XAML 79 | $xaml = Get-Content "$moduleRoot\xaml\start.xaml" 80 | $xaml = $xaml.Replace('INSERT_WINDOW_TITLE',$windowTitle) 81 | if ($iconPath) { 82 | # TODO: change taskbar icon? 83 | # WPF wants the absolute path 84 | $iconPath = (Resolve-Path $iconPath).Path 85 | $xaml = $xaml.Replace('INSERT_ICON_PATH',$iconPath) 86 | } 87 | else { 88 | # No icon specified 89 | $xaml = $xaml.Replace('Icon="INSERT_ICON_PATH" ','') 90 | } 91 | 92 | # Add CSV data to XAML 93 | # Row counter 94 | $script:row = 0 95 | # Not using Group-Object as PS7-preview4 does not preserve original order 96 | $sections = $csvData.Section | Where-Object {-not [string]::IsNullOrEmpty($_) } | Get-Unique 97 | # Generate GUI rows 98 | ForEach ($section in $sections) { 99 | Write-Verbose "Adding GUI Section: $section..." 100 | # Section Heading 101 | $xaml += New-GuiHeading $section 102 | $csvData | Where-Object {$_.Section -eq $section} | ForEach-Object { 103 | # Add items 104 | $xaml += New-GuiRow $_ 105 | } 106 | } 107 | Write-Verbose 'Adding any items with blank Section...' 108 | $csvData | Where-Object { [string]::IsNullOrEmpty($_.Section) } | ForEach-Object { 109 | $xaml += New-GuiRow $_ 110 | # TODO: spacing at top of window is untidy with no Sections (minor) 111 | } 112 | Write-Verbose "Added $($row) GUI rows" 113 | 114 | # Finish constructing XAML 115 | $xaml += Get-Content "$moduleRoot\xaml\end.xaml" 116 | 117 | Write-Verbose 'Creating XAML objects...' 118 | $form = New-GuiForm -inputXml $xaml 119 | 120 | Write-Verbose "Found $($buttons.Count) buttons" 121 | Write-Verbose 'Adding click actions...' 122 | ForEach ($button in $buttons) { 123 | $button.Add_Click( { 124 | # Use object in pipeline to identify script to run 125 | Invoke-ButtonAction $_.Source.Name 126 | } ) 127 | } 128 | 129 | if ($hideConsole) { 130 | if ($global:error[0].Exception.CommandInvocation.MyCommand.ModuleName -ne 'PSScriptMenuGui') { 131 | # Do not hide console if there have been errors 132 | Hide-Console | Out-Null 133 | } 134 | } 135 | 136 | Write-Verbose 'Showing dialog...' 137 | $Form.ShowDialog() | Out-Null 138 | } 139 | 140 | Function New-ScriptMenuGuiExample { 141 | <# 142 | .SYNOPSIS 143 | Creates an example set of files for PSScriptMenuGui 144 | .PARAMETER path 145 | Path of output folder 146 | .EXAMPLE 147 | New-ScriptMenuGuiExample -path 'PSScriptMenuGui_example' 148 | .LINK 149 | https://github.com/weebsnore/PowerShell-Script-Menu-Gui 150 | #> 151 | [CmdletBinding()] 152 | param ( 153 | [string]$path = 'PSScriptMenuGui_example' 154 | ) 155 | 156 | # Ensure folder exists 157 | if (-not (Test-Path -Path $path -PathType Container) ) { 158 | New-Item -Path $path -ItemType 'directory' -Verbose | Out-Null 159 | } 160 | 161 | Write-Verbose "Copying example files to $path..." -Verbose 162 | Copy-Item -Path "$moduleRoot\examples\*" -Destination $path 163 | } -------------------------------------------------------------------------------- /PSScriptMenuGui/private/functions.ps1: -------------------------------------------------------------------------------- 1 | function Hide-Console { 2 | Write-Verbose 'Hiding PowerShell console...' 3 | # .NET method for hiding the PowerShell console window 4 | # https://stackoverflow.com/questions/40617800/opening-powershell-script-and-hide-command-prompt-but-not-the-gui 5 | Add-Type -Name Window -Namespace Console -MemberDefinition ' 6 | [DllImport("Kernel32.dll")] 7 | public static extern IntPtr GetConsoleWindow(); 8 | 9 | [DllImport("user32.dll")] 10 | public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow); 11 | ' 12 | $consolePtr = [Console.Window]::GetConsoleWindow() 13 | [Console.Window]::ShowWindow($consolePtr, 0) # 0 = hide 14 | } 15 | 16 | Function New-GuiHeading { 17 | param( 18 | [Parameter(Mandatory)][string]$name 19 | ) 20 | $string = Get-Content "$moduleRoot\xaml\heading.xaml" 21 | $string = $string.Replace('INSERT_SECTION_HEADING',(Get-XamlSafeString $name) ) 22 | $string = $string.Replace('INSERT_ROW',$row) 23 | $script:row++ 24 | 25 | return $string 26 | } 27 | 28 | Function New-GuiRow { 29 | [CmdletBinding()] 30 | param( 31 | [Parameter(Mandatory)][PSCustomObject]$item 32 | ) 33 | Write-Verbose $item 34 | 35 | $string = Get-Content "$moduleRoot\xaml\item.xaml" 36 | $string = $string.Replace('INSERT_BACKGROUND_COLOR',$buttonBackgroundColor) 37 | $string = $string.Replace('INSERT_FOREGROUND_COLOR',$buttonForegroundColor) 38 | $string = $string.Replace('INSERT_BUTTON_TEXT',(Get-XamlSafeString $item.Name) ) 39 | # Description is optional 40 | if ($item.Description) { 41 | $string = $string.Replace('INSERT_DESCRIPTION',(Get-XamlSafeString $item.Description) ) 42 | } 43 | else { 44 | $string = $string.Replace('INSERT_DESCRIPTION','') 45 | } 46 | $string = $string.Replace('INSERT_BUTTON_NAME',$item.Reference) 47 | $string = $string.Replace('INSERT_ROW',$row) 48 | $script:row++ 49 | 50 | return $string 51 | } 52 | 53 | Function Get-XamlSafeString { 54 | param( 55 | [Parameter(Mandatory)][string]$string 56 | ) 57 | # https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-use-special-characters-in-xaml 58 | # Order matters: & first 59 | $string = $string.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') 60 | # Restore line breaks 61 | $string = $string -replace '<\s*?LineBreak\s*?\/\s*?>','' 62 | 63 | return $string 64 | } 65 | 66 | Function New-GuiForm { 67 | # Based on: https://foxdeploy.com/2015/05/14/part-iii-using-advanced-gui-elements-in-powershell/ 68 | param ( 69 | [Parameter(Mandatory)][array]$inputXml # XML has not been converted to object yet 70 | ) 71 | # Process raw XML 72 | $inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^