Latest Free Tool: ConfigMgr PXE Boot Log

onfigMgr Add2Collection is a free tool that allows IT administrators and support staff to add resources to collections in ConfigMgr independently of the ConfigMgr console. It honors role-based access control (RBAC) to limit visibility of collections where appropriate. It can be used either on the Site Server or a remote workstation using PS remoting.

A Customisable WPF MessageBox for PowerShell

Update 2017-08-25 | Changed the way the output from a button click is handled, saving it to a variable instead.

The humble VBScript message box has been around for a while now and is still a useful way of providing a simple pop-up notification in Windows. I’ve used it in my PowerShell scripts as well as my UI tools and even in my ConfigMgr task sequences for providing feedback to the end user.

Both WinForms and WPF also have a message box but they have similar style and functionality to the VBScript version. In this age of modern design languages like Metro and Material Design however, this message box is starting to look a little dated. So I decided to create my own customizable message box in WPF as a PowerShell function that can be used in my scripts and UI tools.

Enter New-WPFMessageBox. My goal was to create a good-looking, fully-functional message box window that is highly customizable in the WPF format, and can even be used as a window host to display custom WPF content. The message box has a basic default design, but if you’re the creative type then you can customize away until you get an appearance you like.

Examples

Let’s have a look at some ways we can use this function.

To create a simple message box, all you need to do is pass a string to the Content parameter:

New-WPFMessageBox -Content "I'm a WPF Object!"

The Content parameter is the only mandatory parameter, and can take either a simple text string or your own custom WPF content (which we’ll look at later).

The title bar is a color block, so it can be used to create a nice contrast with the main content, for example. Use the TitleBackground parameter to change the color. This parameter is a dynamic parameter and will allow you to select from the list of available colors in the .Net palette.

You can change the fonts, font weights and font sizes, as well as the background and foreground colors to create some nice combinations. The FontFamily parameter is also dynamic and allows you to select from fonts available in .Net.

The Content… parameters allow you to change the main content section, and the Title… parameters allow you to change the title.

When using several parameters, it can be easier to ‘splat’ them like so:

The default button is the OK button, but you can also use other buttons or button combinations such as Yes/No, Retry/Cancel, Abort/Retry/Ignore etc. Simply select an option using the ButtonType parameter.

You can create Information, Warning or Error message boxes using your own style. If you define the parameters once in your script, then you can simply call them everytime you want to display that message type.

In this one I’ve added the Sound parameter. This is a dynamic parameter allowing you to select from available Windows sounds in your local media library. The sound will be played when the message box is displayed.

Maybe you prefer a little more drama in your error message box. How about this:

If you prefer square corners and no window shadow effect for a more basic look, try this. Here I have set the CornerRadius, ShadowDepth and BlurRadius to 0, as well as setting the BorderThickness and BorderBrush parameters to create a simple border.

Timeout is a useful parameter which allows you to automatically close the message box after a number of seconds. You might want to use this in a script where you choose to continue if no response was received after a period of time. You can also use this to simply display a message without any buttons – just for information, and no response is required.

I’ve also added a couple of parameters that allow you to assign code to the Loaded or Closed events on the message box window. This means you can run code when the message box opens, or after it has closed. Simply pass a code block to the OnLoaded or OnClosed parameters. Bear in mind that any code assigned to the Loaded event should not be long running as it will block the thread until it has completed, and the message box will only display afterwards.

In this example, I have used an async method from the SpeechSynthesizer class so that the text will be spoken at the same time the window opens because it is not running in the same thread.

Adding Custom WPF Content

As well as a simple text string, the Content parameter can accept a WPF UI element. When you pass a text string, the function simply creates a textblock element and adds the text. But if you know a little about creating WPF objects in PowerShell, you can create your own elements and add them as content to the message box. This opens up a lot of possibilities and transforms the message box into a kind of easy-to-create window host for any WPF content.

For example, instead of some text, how about displaying a picture instead?

To set the height and width I have simply halved the dimensions of the source image file.

Maybe you want to add a text description of the picture too. In that case, create a textblock and add both the image and textblock to a layout control like a stackpanel, and pass the stackpanel as content.

You can also ask the user for input and return the results to a variable in the containing script. For example, here I am simply asking the user to select an option from a drop-down list. The selection will be saved to a variable that can be used later in the script.

If your script returns data in table format, how about using a WPF datagrid to display it in a nicer way? You can populate a datagrid from an array, but I recommend converting to a datatable object as it’s a type that fully supports the features of the datagrid.
In this example, I’m simply displaying services on my system from the Get-Service cmdlet.

If you want to use the message box function in your own WPF UI tool, then you can use the WindowHost parameter to pass the host window object. This will make the host window owner of the message box window, and means your message box will always display centered in the host window, no matter the size or location of the window.

In this example I have created a simple host window with a big “Click Me” button. When clicked, a message box will display.

My final example is a bit more advanced, but it demonstrates how the message box could be used in a more dynamic way to display output from code running in a background job.

I’ve created a stackpanel from XAML code containing a textbox and a button. These are all added to a synchronized hash table which gets passed, together with a code block, to a new PowerShell instance which runs in the background. The code block simply pings a list of computers in succession and updates the textbox in real time with the result of each ping.

62 thoughts on “A Customisable WPF MessageBox for PowerShell”

Wauw, cool message boxes, thank you so much. Only minor suggestion to include default icons for information,warning and error messages would be nice, so I don’t need to get an external image somewhere..but now I’m just whining. Keep up your great posts, much appreciated!

That’s interesting, the $this.activate() line should take care of that. Another way to do it is to set the topmost property of the window, so set $window.topmost = $true just after the window object is created, then set it back to false in the $window.Add_loaded event methods otherwise the window will stay permanently on top of all other windows.

Are you using the latest version of the script? This was an issue with the first release but I corrected it to include loading the required libraries in the PS console. The current Gist is working fine for me.

Hey Sam, that’s not the function that’s the issue, but your additional code. You need to add the required assemblies for WPF before you create any WPF objects, ie Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase

Hey, thanks for the fast reply! I’ve got everything working thanks to your help, and I made a post on the PDQ Deploy forum on how you enabled me to accomplish what I needed to accomplish. I gave you credit, and referenced back to this page so others can gain from this awesome tool. Thanks again! -Sam

Probably bad wording, I mean a combo box with a list of variables where the user selects one and then the result is set as the variable. eg. I have a ts server and want users to select the location they’re at.

Will this work within a PXE environment during a task sequence? The boot image does have Powershell and .net 4.7 loaded. I can get the messages to work no problem on my desktop but they don’t appear during the my task sequence. Running the script doesn’t create any errors in the TS environment, it just pauses for a bit then moves on as if it ran fine without anything appearing.

I wanted to add, I did discover if I just run New-WPFMessageBox -Content “I’m a WPF Object!” the message box pops up without issue in PXE. However if I add anything else at all to it, even just doing New-WPFMessageBox -Content “I’m a WPF Object!” -Title “Custom Message Box” it fails to display.

Great function, was looking for this for a while :)…just one issue untill now…I can’t seem to get one or more new lines in the messagebox…it keeps putting it on one line. Even with `n…any thoughts on this?

Can anyone tell me why i get this error when running this code? Basically if i add multiple different type of objects such as textbox, listbox etc together, i can only run the popup once, if i run it again i get this error. I’ve tried adding the line: $Window.FindName(‘ContentHost’).Children.Clear() above line 386 with no luck.