Close

WPF/XAML-based Context Menu - Part 1: NotifyIcon

A project log for PowerShell Quick Tray

Get a notification-tray-located palette for running powershell commands

nicholas-jacksonNicholas Jackson 10/09/2022 at 17:540 Comments

The code for this project is gonna be based on the notification app made by Trevor Jones on his blog SMSAgent.

The first thing that needs to be done is to give PowerShell support for XAML (PresentationFramework) and NotifyIcon/ApplicationContext (System.Windows.Forms):

Add-TypeAssemblyName System.Windows.Forms,PresentationFramework

To learn more about Application Context and what it does in C#, take a look at this article on creating C# Splash Screens.

We will use Application Context to keep the script running, and prevent NotifyIcon artifacts (If we don't run an application context, notifyicon may persist to run even once the powershell script exits)

To create the App Context, initiate it as an object (we will invoke the .run() method at the end of the script):

$appContext = New-Object System.Windows.Forms.ApplicationContext

 In Trevor Jones's article, he used a Base64 string to create the notification tray icon. An alternative to this is to extract one from a preexisting favicon. To do that we'll pull the method used by Damien Van Robaeys in his article on PowerShell Systray applications:

$executable = "$env:SystemRoot\system32\WindowsPowerShell\v1.0\powershell.exe"
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon( $executable )

 **if needed add System.Drawing to the Add-Type command, if it is missing

Here, we pull the icon from Windows PowerShell. Don't have to maintain an icon for the app, if we just pull a pre-existing one, right?

Now, let's create the systray icon:

$QuickTray = New-Object System.Windows.Forms.NotifyIcon
$QuickTray.Icon = $icon 
$QuickTray.Text = "PowerShell Quick Tray"

In Trevor's article, he created the systray icon under the "$Script:" scope, but since this app is gonna serve as a powershell command palette, why not leave it in the global scope for automation?

Now, to make the icon right-clickable:

$QuickTray.Add_MouseDown({
    if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Right)
    {
        <# run some code here... #>
    }
}) 

Now, if you only want a single powershell script to run replace the script with your own code. If not, we will cover creating the WPF/XAML portion of this app in Part 2.

For now, to test that it works you can replace the comment with this:

[System.Windows.MessageBox]::Show("Hello World!") 

Now, let's go ahead and see what this looks like by starting the app with these lines:

$QuickTray.Visible = $true

[void][System.Windows.Forms.Application]::Run($appContext)

 To get a copy of the script, check it out on Gist

Discussions