{"id":74291,"date":"2015-11-02T00:01:00","date_gmt":"2015-11-02T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2015\/11\/02\/creating-pop-ups-by-using-powershell\/"},"modified":"2022-06-20T13:59:03","modified_gmt":"2022-06-20T20:59:03","slug":"creating-pop-ups-by-using-powershell","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/creating-pop-ups-by-using-powershell\/","title":{"rendered":"Creating Pop-ups by Using PowerShell"},"content":{"rendered":"<p><b style=\"font-size:12px\">Summary<\/b><span style=\"font-size:12px\">: Guest blogger and PowerShell MVP, Chrissy <span>LeMaire<\/span>, talks about creating pop-ups with Windows PowerShell.<\/span><\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Welcome today a brand new guest blogger, Chrissy LeMaire.<\/p>\n<p style=\"margin-left:30px\">\n  Chrissy is a systems engineer and PowerShell MVP. Always an avid scripter, she attended the Monad session at the Microsoft Professional Developers Conference in Los Angeles in 2005. She has worked and played with PowerShell ever since. You can follow her on Twitter at <a href=\"https:\/\/na01.safelinks.protection.outlook.com\/?url=http%3a%2f%2ftwitter.com%2fcl&#038;data=01%7c01%7cEDWILS%40exchange.microsoft.com%7ca1009dccb38a433c499508d2da4fbae8%7c72f988bf86f141af91ab2d7cd011db47%7c1&#038;sdata=K%2bzvOvzbGazQkVXYYmhW%2fOY%2fzLL95O5TCMs0yXsBBbU%3d\">@cl<\/a>, and read more about her work with PowerShell on her blog, <a href=\"https:\/\/na01.safelinks.protection.outlook.com\/?url=https%3a%2f%2fblog.netnerds.net&#038;data=01%7c01%7cEDWILS%40exchange.microsoft.com%7ca1009dccb38a433c499508d2da4fbae8%7c72f988bf86f141af91ab2d7cd011db47%7c1&#038;sdata=UyNIH%2b33A%2fxvUznr%2bqCDEA%2f%2fMXUr2PScnvtX9vIFiRo%3d\" target=\"_blank\" rel=\"noopener\">blog.netnerds.net:\\><\/a>.\n<\/p>\n<p><span style=\"font-size:12px\">Here\u2019s Chrissy\u2026<\/span><\/p>\n<p>A few weeks ago, I built a huge SQL Server lab that ended up maxing out my virtual environment\u2019s resources. My resource usage alarms were going off, but I couldn\u2019t see them unless I logged in to the admin client.\u00a0<span style=\"font-size:12px\">I asked myself, \u201cWouldn\u2019t it be nice if I had a little notification icon pop-up that would tell me how much RAM and CPU each of my hosts was using? I bet PowerShell can do that.\u201d And sure enough, it can.<\/span><\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-1.png\" target=\"_blank\" rel=\"noopener\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-1.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<h2>PowerShell GUIs, WPF, and XAML<\/h2>\n<p>Like .NET, there are a variety of ways to create GUIs in PowerShell. Earlier in the year, I learned how to build GUIs in PowerShell from three primary sources: the Hey, Scripting Guy! Blog post, <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/ive-got-a-powershell-secret-adding-a-gui-to-scripts\/\">I&#8217;ve Got a PowerShell Secret: Adding a GUI to Scripts<\/a>, <a href=\"http:\/\/learn-powershell.net\/category\/wpf\/\">Boe Prox<\/a>, and <a href=\"https:\/\/bytecookie.wordpress.com\/2011\/12\/28\/gui-creation-with-powershell-part-2-the-notify-icon-or-how-to-make-your-own-hdd-health-monitor\/\">Denniver Reining<\/a>. With these resources, I learned that I could build a Windows Presentation Framework (WPF) form in Visual Studio, copy the resulting XAML into PowerShell, then manipulate the GUI with variables like <strong>$window<\/strong> and <strong>$listview<\/strong>. Finally, GUI programming was within reach.<\/p>\n<p>This is the first post in a series of two, and it builds heavily on the three previous resources, so if you\u2019re unfamiliar with PowerShell GUIs and XAML, you may want to review those concepts before reading further.<\/p>\n<p>When creating GUIs in PowerShell, I recommend using WPF over alternatives such as Windows Forms (WinForms) because WPF is extremely flexible, and it separates the GUI design from the coding process. This means that you can build the GUI first in Visual Studio, instead of using the command line. WPF forms can be designed by using <a href=\"https:\/\/www.visualstudio.com\/en-us\/products\/visual-studio-community-vs.aspx\" target=\"_blank\" rel=\"noopener\">Visual Studio Community<\/a>, which Microsoft provides free of charge to individual developers, open source projects, educational institutions, and small professional teams.<\/p>\n<h3>Why not use C#?<\/h3>\n<p>\u201cC# is about optimizing computer resources. PowerShell is about optimizing human resources.\u201d<br \/>\n\u00a0\u00a0\u00a0\u00a0 ~ Jeffrey Snover<\/p>\n<p>Although it\u2019s true that C# is great for making GUIs, PowerShell is already in my toolbox. I\u2019m not a developer, and I\u2019ll never get around to learning GUI development in C#. For me, PowerShell is an easier solution and on top of it all, it\u2019s a lot of fun.<\/p>\n<h2>Intro to pop-ups<\/h2>\n<p>In the following tutorial, I\u2019ll be using the term \u201cpop-up\u201d loosely. WPF actually does have a <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.controls.primitives.popup(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener\">Popup class<\/a>, but after some testing, the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.window(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener\">WPF Window<\/a> seems most appropriate. This is because pop-ups are really intended to provide details about UI elements, much like a <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/bb760250(v=vs.85).aspx\" target=\"_blank\" rel=\"noopener\">ToolTip<\/a> or an \u201calt\u201d tag in HTML.<\/p>\n<p>The placement of WPF Windows is also easier and more consistent than a <strong>Popup<\/strong> placement. Consistent placement is especially important when the Window is expected to appear in the lower right part of the screen, as is often the case with notification icon pop-ups.<\/p>\n<h3>\u00a0Sounds good\u2014what are we making?<\/h3>\n<p>There are endless possibilities for what this application can contain. In this post, we\u2019ll keep it simple, but useful, by creating a pop-up that shows your computer\u2019s disk-free space. We\u2019ll also use mouse clicks and a notification icon to make the pop-up window appear and disappear.<\/p>\n<p style=\"margin-left:30px\">\n  <a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-2.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-2.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a>\n<\/p>\n<p>This will set the stage for an even cooler monitoring application that that will alert you if you\u2019re running low on disk space. The monitoring application will be detailed in the second post of this series.<\/p>\n<p>Because I intend for the pop-up to be flexible and visually appealing, there will be a little extra code. In the end, my hope is that you can easily reuse this pop-up without much fuss for your own project.\u00a0 Some of this code will be in image format. For a full text version of the code, please see <a href=\"https:\/\/gallery.technet.microsoft.com\/scriptcenter\/Popup-Toast-WPF-PowerShell-e228e7a3\" target=\"_blank\" rel=\"noopener\">Creating Popup Windows in PowerShell using WPF<\/a> in the Script Center Repository.<\/p>\n<p>Building this pop-up in PowerShell is comprised of ten steps.<\/p>\n<ol start=\"1\">\n<li>\n    Load the required assemblies.\n  <\/li>\n<li>\n    Create and populate the disk-free space PowerShell custom object.\n  <\/li>\n<li>\n    Extract the PowerShell icon to use in the Notification area.\n  <\/li>\n<li>\n    Create a form in Visual Studio, taking careful note to name the required objects.\n  <\/li>\n<li>\n    Copy the resulting XAML, cleaning it up a little, and transforming it into a PowerShell object.\n  <\/li>\n<li>\n    Dynamically populate and draw the ListView control box.\n  <\/li>\n<li>\n    Position the window in the lower-right corner of the primary screen.\n  <\/li>\n<li>\n    Add the notification icon.\n  <\/li>\n<li>\n    Add double -click, lost-focus, and exit events.\n  <\/li>\n<li>\n    Hide the PowerShell host process, and run the script as an application.\n  <\/li>\n<\/ol>\n<h2>Let the coding begin<\/h2>\n<p>First thing\u2019s first. We\u2019ll be using some .NET assemblies, so let\u2019s load them up:<\/p>\n<p style=\"margin-left:30px\">\n  Add-Type -AssemblyName PresentationFramework, System.Drawing, System.Windows.Forms\n<\/p>\n<p>We will use the PresentationFramework assembly for the XAML form, the System.Drawing assembly to extract the PowerShell icon, and the System.Windows.Forms assembly to create the <strong>Notify<\/strong> icon host application. WPF is ideal for creating the pop-up window, but it cannot create the <strong>Notify<\/strong> icon.<\/p>\n<h2>\u00a0Create and populate the disk-free space object<\/h2>\n<p>We already know what we want to see, so it\u2019s time to create the object that displays this information. Boe Prox\u2019s post, <a href=\"http:\/\/learn-powershell.net\/2012\/08\/10\/locating-mount-points-using-powershell\/\" target=\"_blank\" rel=\"noopener\">Locating Mount Points Using PowerShell<\/a>, is my go-to for a good code snippet about this topic.<\/p>\n<p>Let\u2019s populate a PowerShell custom object named <strong>$itemsource<\/strong> with the following disk information values:\u00a0 Name, Label, Total GB, and Free GB.<\/p>\n<p style=\"margin-left:30px\">\n  $localdisks = Get-WmiObject Win32_Volume -Filter &#8220;DriveType=&#8217;3&#8242;&#8221;\n<\/p>\n<p style=\"margin-left:30px\">\n  $itemsource = @()\n<\/p>\n<p style=\"margin-left:30px\">\n  foreach ($disk in ($localdisks| Sort-Object -Property Name)) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 if (!$disk.name.StartsWith(&#8220;\\\\&#8221;)) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $itemsource += [PSCustomObject]@{\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Name = $disk.Name\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Label = $disk.Label\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Total = &#8220;$([Math]::Round($disk.Capacity \/1GB,1)) GB&#8221;\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Free = &#8220;$([Math]::Round($disk.FreeSpace \/1GB,1)) GB&#8221;\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p>Setting the column order will allow us to control how the columns are ordered later in the script:<\/p>\n<p style=\"margin-left:30px\">\n  $columnorder = &#8216;Name&#8217;, &#8216;Label&#8217;, &#8216;Total&#8217;, &#8216;Free&#8217;\n<\/p>\n<h2>Extract the PowerShell icon<\/h2>\n<p>We\u2019ll create the notification icon (notifyicon), which is built into Windows forms. You can use any icon, and in this case, I chose to use the PowerShell icon. So let\u2019s extract the icon from the PowerShell executable:<\/p>\n<p style=\"margin-left:30px\">\n  $icon = [System.Drawing.Icon]::ExtractAssociatedIcon(&#8220;$pshome\\powershell.exe&#8221;)\n<\/p>\n<h2>Create the WPF window<\/h2>\n<p>I mentioned that I learned how to create GUIs in PowerShell by reading WPF articles by the Scripting Guys and Boe Prox. Now that I know how to do this, I can put it into action by using Visual Studio to build my form.<\/p>\n<p>Ultimately, I chose ListView to display my information. When displaying information, it\u2019s generally accepted that using ListView is ideal for read-only data, while DataGrid is ideal for CRUD (create, read, update, delete). For more information, see <a href=\"http:\/\/www.codeproject.com\/Articles\/30905\/WPF-DataGrid-Practical-Examples\" target=\"_blank\" rel=\"noopener\">WPF DataGrid Practical Examples<\/a>.<\/p>\n<p><a href=\"https:\/\/msdnshared.blob.core.windows.net\/media\/TNBlogsFS\/prod.evol.blogs.technet.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/76\/18\/2.5.PNG\"><img decoding=\"async\" src=\"https:\/\/msdnshared.blob.core.windows.net\/media\/TNBlogsFS\/prod.evol.blogs.technet.com\/CommunityServer.Blogs.Components.WeblogFiles\/00\/00\/00\/76\/18\/2.5.PNG\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<p>I found ListView to be rather flexible, because different types of objects can be used to populate it, including (but not limited to) PowerShell custom objects, data tables, and hash tables. In this case, we\u2019ll be using a custom object.<\/p>\n<p>One major drawback with a ListView control box is that customizing it can be a bit painful, especially if you want it to have a sleek, chromeless appearance. The code to modify the header row (<strong>GridViewColumnHeader<\/strong>) alone takes up about 50% of the XAML code that is used in this post.<\/p>\n<p>Designing a WPF form in Visual Studio is surprisingly simple. I won\u2019t go over it in its entirety, but basically, it\u2019s:<\/p>\n<p style=\"margin-left:30px\">\n  Start > Visual Studio 2015 > New Project > Visual C# > Windows > Classic Desktop > WPF Application\n<\/p>\n<p>The resulting XAML from the default form can be converted into usable PowerShell code with a few changes.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-3.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-3.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<h3>The XAML<\/h3>\n<p>Here is the resulting XAML from the form that I built:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-4.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-4.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p style=\"margin-left:30px\">\n  <strong>Note<\/strong> \u00a0To copy this code, see <a href=\"https:\/\/gallery.technet.microsoft.com\/scriptcenter\/Popup-Toast-WPF-PowerShell-e228e7a3\" target=\"_blank\" rel=\"noopener\">Creating Popup Windows in PowerShell using WPF<\/a>, which contains the code in plain text.\n<\/p>\n<p>This form would be rather straightforward were it not for the GridViewColumnHeader style. This necessary chunk of code ensures that the form looks appealing. Otherwise, the column header would be distracting, as can be seen here:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-5.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-5.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<h2>Transform XAML to PowerShell variables<\/h2>\n<p>Take note of any place in the XAML that says <strong>Name<\/strong>. We can convert these nodes (Window, Grid, and ListView) to PowerShell variables by parsing the XML object and creating a variable for each result, as shown in the following code:<\/p>\n<p style=\"margin-left:30px\">\n  $window = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))<br \/> $xaml.SelectNodes(&#8220;\/\/*[@Name]&#8221;) | ForEach-Object { Set-Variable -Name ($_.Name) \u2013Value $window.FindName($_.Name) -Scope Script }\n<\/p>\n<p>If you\u2019re interested in a breakdown of how this works, you can read more in this post: <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/i-39-ve-got-a-powershell-secret-adding-a-gui-to-scripts\/\" target=\"_blank\" rel=\"noopener\">I&#8217;ve Got a PowerShell Secret: Adding a GUI to Scripts<\/a>.<\/p>\n<h2>\u00a0Dynamically populate the ListView control box<\/h2>\n<p>Now that we have access to the <strong>$window<\/strong>, <strong>$grid<\/strong>, and <strong>$listview<\/strong> variables, we can populate <strong>$listview<\/strong> with the <strong>$itemsource<\/strong> object we created earlier. Then, we\u2019ll set the width of <strong>$listview<\/strong> to take up 90% of <strong>$grid<\/strong>.<\/p>\n<p style=\"margin-left:30px\">\n  $listview.ItemsSource = $itemsource\n<\/p>\n<p style=\"margin-left:30px\">\n  $listview.Width = $grid.width*.9\n<\/p>\n<p>At this point, we\u2019ve only set the <strong>ItemSource<\/strong> object, but the layout must still be built using GridView. If you recall, I wanted this to be as dynamic as possible, so we\u2019ll add the GridView for the ListView by using PowerShell. This technique is akin to using C#\u2019s &#8220;code-behind&#8221; (see <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/vstudio\/ms743596(v=vs.100).aspx\" target=\"_blank\" rel=\"noopener\">How to: Add an Event Handler Using Code<\/a>).<\/p>\n<p style=\"margin-left:30px\">\n  $gridview = New-Object System.Windows.Controls.GridView\n<\/p>\n<p style=\"margin-left:30px\">\n  foreach ($column in $columnorder) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridcolumn = New-Object System.Windows.Controls.GridViewColumn\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridcolumn.Header = $column\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridcolumn.Width = $grid.width*.20\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridbinding = New-Object System.Windows.Data.Binding $column\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridcolumn.DisplayMemberBinding = $gridbinding\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0 $gridview.AddChild($gridcolumn)\n<\/p>\n<p style=\"margin-left:30px\">\n  }\n<\/p>\n<p style=\"margin-left:30px\">\n  $listview.view = $gridview\n<\/p>\n<p>The previous section of code creates the GridView, adds columns (Name, Label, Total, Free) to GridView, sets the width of each column, then associates the column with the actual data contained within $listview.ItemSource ($itemsource) on a column name by column name basis.<\/p>\n<p>Here is a visual representation of the pop-up, which is now populated by a ListView control box.<\/p>\n<p style=\"margin-left:30px\">\n  <a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-6.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-6.png\" alt=\"Image of pop-up\" title=\"Image of pop-up\" \/><\/a>\n<\/p>\n<p><span style=\"font-size:12px\">\u2026well, almost. We still need to tell the pop-up window to appear in the lower-right corner. More on that shortly.<\/span><\/p>\n<h2 style=\"margin-left:30px\">\n  Create notification icon and add right-click\n<\/h2>\n<p>NotifyIcon is the icon that you\u2019ll interact with in your task bar. To add a right-click (Exit), we will create a ContextMenu, then populate <strong>$contex<\/strong>tmenu with a <strong>$menuitem<\/strong> that reads <strong>Exit<\/strong> and responds to clicks.<\/p>\n<p style=\"margin-left:30px\">\n  <span style=\"font-size:12px\">$notifyicon = New-Object System.Windows.Forms.NotifyIcon<\/span>\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.Text = &#8220;Disk Usage&#8221;\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.Icon = $icon\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.Visible = $true\n<\/p>\n<p style=\"margin-left:30px\"> $menuitem = New-Object System.Windows.Forms.MenuItem\n<\/p>\n<p style=\"margin-left:30px\">\n  $menuitem.Text = &#8220;Exit&#8221;\n<\/p>\n<p>\u00a0<\/p>\n<p style=\"margin-left:30px\">\n  $contextmenu = New-Object System.Windows.Forms.ContextMenu\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.ContextMenu = $contextmenu\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.contextMenu.MenuItems.AddRange($menuitem)\n<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1385.hsg-11-2-15-7.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1385.hsg-11-2-15-7.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<h2>Add double-click and exit events<\/h2>\n<p>Now that we have all of our objects ready, we will add event handlers. Events are members of an object, such as properties and methods. Event handling is basically \u201cdo this when that happens,\u201d for instance, \u201cSet the pop-up window to visible when the notification icon is clicked.\u201d<\/p>\n<p>How do you know what events an object supports? Pipe the object to <strong>Get-Member -MemberType Event<\/strong>:<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon | Get-Member -MemberType Event\n<\/p>\n<p>Alternatively, you can search the web for phrases such as <strong>msdn notifyicon events<\/strong>, and you\u2019ll find an easy-to-read webpage with a list of all of the events for any object that supports events (see <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.windows.forms.notifyicon_events(v=vs.110).aspx\" target=\"_blank\" rel=\"noopener\">NotifyIcon Events<\/a>). If you\u2019re interested in learning more about events, check out this article by PowerShell MVP, June Blender: <a href=\"https:\/\/www.sapien.com\/blog\/2015\/08\/31\/the-methods-that-register-events\/\" target=\"_blank\" rel=\"noopener\">The Methods that Register Events<\/a>.<\/p>\n<p style=\"margin-left:30px\">\n  # Close the pop-up window if it&#8217;s double clicked\n<\/p>\n<p style=\"margin-left:30px\">\n  $window.Add_MouseDoubleClick({\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Hide()\n<\/p>\n<p style=\"margin-left:30px\">\n  })\n<\/p>\n<p style=\"margin-left:30px\">\n  # Close the pop-up window if any other window is clicked\n<\/p>\n<p style=\"margin-left:30px\">\n  $window.Add_Deactivated({\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Hide()\n<\/p>\n<p style=\"margin-left:30px\">\n  })\n<\/p>\n<p style=\"margin-left:30px\">\n  # When Exit is clicked, close everything and kill the PowerShell process\n<\/p>\n<p style=\"margin-left:30px\">\n  $menuitem.add_Click({\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $notifyicon.Visible = $false\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Close()\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Stop-Process $pid\n<\/p>\n<p style=\"margin-left:30px\">\n  })\n<\/p>\n<p style=\"margin-left:30px\">\n  # Show window when the notifyicon is clicked with the left mouse button\n<\/p>\n<p style=\"margin-left:30px\">\n  # Recall that the right mouse button brings up the contextmenu\n<\/p>\n<p style=\"margin-left:30px\">\n  $notifyicon.add_Click({\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0 if ($_.Button -eq [Windows.Forms.MouseButtons]::Left) {\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 # reposition each time, in case the resolution changes\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Left = $([System.Windows.SystemParameters]::WorkArea.Width-$window.Width)\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Top = $([System.Windows.SystemParameters]::WorkArea.Height-$window.Height)\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Show()\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $window.Activate()\n<\/p>\n<p style=\"margin-left:30px\">\n  \u00a0\u00a0 }<br \/> })\n<\/p>\n<p>Ensuring that the pop-up window always appears above the notification icon can be tricky, especially when you\u2019re working with multiple monitors. To handle this placement, the previous code determines the proper coordinates each time the notification icon is clicked. This ensures that the pop-up will appear where it\u2019s expected, even if the display resolution changes after the application is first launched.<\/p>\n<h2>Hide PowerShell host process and run the application<\/h2>\n<p>Because this is a long-running process, we don\u2019t want the PowerShell window to stay in our task bar. To hide the PowerShell process, we\u2019ll use a technique from Microsoft that calls some C# code (see <a href=\"http:\/\/blogs.msdn.com\/b\/powershell\/archive\/2008\/06\/03\/show-powershell-hide-powershell.aspx\" target=\"_blank\" rel=\"noopener\">Show-PowerShell\/Hide-PowerShell<\/a>).<\/p>\n<p style=\"margin-left:30px\">\n  $windowcode = &#8216;[DllImport(&#8220;user32.dll&#8221;)] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);&#8217;\n<\/p>\n<p style=\"margin-left:30px\">\n  $asyncwindow = Add-Type -MemberDefinition $windowcode -name Win32ShowWindowAsync -namespace Win32Functions -PassThru\n<\/p>\n<p style=\"margin-left:30px\">\n  $null = $asyncwindow::ShowWindowAsync((Get-Process -PID $pid).MainWindowHandle, 0)\n<\/p>\n<p>When the PowerShell process is hidden, we can do a little garbage collection, then launch the actual application. Forcing a quick garbage collection is not required, but it can release unused resources so that the application starts with less RAM usage.<\/p>\n<p style=\"margin-left:30px\">\n  [System.GC]::Collect()\n<\/p>\n<p>Next, we\u2019ll create a Windows Form object called ApplicationContext to \u201ccontain\u201d the entire application (see <a href=\"http:\/\/www.codeproject.com\/Tips\/627796\/Doing-a-NotifyIcon-program-the-right-way\" target=\"_blank\" rel=\"noopener\">Doing a NotifyIcon program the right way<\/a>). This is the proper way to run windowless NotifyIcon tasks and it makes the application more responsive, especially the Exit action for NotifyIcon.<\/p>\n<p style=\"margin-left:30px\">\n  $appContext = New-Object System.Windows.Forms.ApplicationContext\n<\/p>\n<p style=\"margin-left:30px\">\n  [void][System.Windows.Forms.Application]::Run($appContext)\n<\/p>\n<p>And now we\u2019re done! Want to see this in action? Copy and paste the entire script from the Script Center Repository (see <a href=\"https:\/\/gallery.technet.microsoft.com\/scriptcenter\/Popup-Toast-WPF-PowerShell-e228e7a3\" target=\"_blank\" rel=\"noopener\">Creating Popup Windows in PowerShell using WPF<\/a>) or download the .ps1 and execute.<\/p>\n<h2>A couple more things<\/h2>\n<p>Initially, when PowerShell starts, you might notice that it will be using a bit of RAM (35-100\u00a0MB). Over time, however, this number is drastically reduced, usually to about 5 or 6\u00a0MB.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-8.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-8.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<p>Also, if you find that the icon disappears, you can customize the Notification area to show it all the time. In <strong>Taskbar and Start Menu<\/strong> <strong>Properties<\/strong>, click <strong>Customize<\/strong>, and setting the icon to <strong>On<\/strong>.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-9.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-11-2-15-9.png\" alt=\"Image of menu\" title=\"Image of menu\" \/><\/a><\/p>\n<h2>Now, you\u2019re set!<\/h2>\n<p>Imagine all of the things you can see with a quick glance now that you know how to do it so easily using PowerShell. DBAs can quickly see if important jobs have failed, Hyper-V admins can get an overview of host resource usage, and storage admins can easily see if disk space is running low or if a disk has failed.<\/p>\n<p>In the next post of this series, I\u2019ll explore monitors and alerts by examining a real-world example that polls for low disk space and triggers an alert when the disk capacity dips below 20%.<\/p>\n<p>~ Chrissy<\/p>\n<p>Thanks Chrissy. I&#8217;m looking forward to the next blog post.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\" rel=\"noopener\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\" rel=\"noopener\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\" rel=\"noopener\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\" rel=\"noopener\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><strong>Ed Wilson, Microsoft Scripting Guy<\/strong><span style=\"font-size:12px\">\u00a0<\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger and PowerShell MVP, Chrissy LeMaire, talks about creating pop-ups with Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, is here. Welcome today a brand new guest blogger, Chrissy LeMaire. Chrissy is a systems engineer and PowerShell MVP. Always an avid scripter, she attended the Monad session at the Microsoft Professional Developers Conference in [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[643,71,56,3,4,45],"class_list":["post-74291","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-chrissy-lemaire","tag-graphical","tag-guest-blogger","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger and PowerShell MVP, Chrissy LeMaire, talks about creating pop-ups with Windows PowerShell. Microsoft Scripting Guy, Ed Wilson, is here. Welcome today a brand new guest blogger, Chrissy LeMaire. Chrissy is a systems engineer and PowerShell MVP. Always an avid scripter, she attended the Monad session at the Microsoft Professional Developers Conference in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/74291","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=74291"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/74291\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=74291"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=74291"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=74291"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}