Part I – Creating PowerShell GUIs in Minutes using Visual Studio – A New Hope

series_PowerShellGUI

This post is part of the Learning GUI Toolmaking Series, here on FoxDeploy. Click the banner to return to the series jump page!


“I’ll never do it the old way again. EVER. ”
-me

If you’ve been following my blog for any time, you’ll know that I love making tools using PowerShell.

I’ve written on the topic previously, and the approach I took then were based off of using the .net System.Windows.Forms class which really meant that you had to painfully, agonizingly create a GUI one element at a time.   I knew there were other methods, using something scary called XAML, and I’d even seen other’s tutorials, but frankly thought it looked too hard.

So I stuck with the pulling teeth method for years.  Until one day, I had some spare time and thought I’d take a look at the other GUI design method again…

I wish I had sooner, because it is so EASY!

What You’ll Need:

  • Visual Studio.  I’m using Community Tech Preview 2015, but you can do this with Visual Studio Express too. Get it here 
  • A bit of helper PowerShell code, inspired by the awesome Chris Conte in this guest post on The Scripting Guy blog.  I’ll provide the snippet we need, when we need it.

Getting Visual Studio is easy, just download either the CTP edition or the trial of Ultimate.  There are ways of getting it for free too, via DreamSpark, MSDN, MVP Access or you can always use Express.  Assuming you’ve been able to install Visual Studio…

How this is going to work

A bit of history first on how this differs from what we did before.

Released with .net 3.0 back in 2006, Windows Presentation Foundation sought to rearchitect the way Windows Developers wrote their programs.  Instead of WinForm and the incredibly verbose style of configuring each element line by line, Microsoft brought a more civilized, CSS inspired design pattern to the world of Windows UX Design.

C# could be used as the code-behind (the actual payload of making things work), while the UI could be designed and themed in XAML, Microsoft’s new Extensible Application Markup Language format (inspired by XML), and the whole thing could easily be themed much like a website with CSS.

Since PowerShell is also based on .net, we can use Visual Studio to draw and design a GUI, then easily import it into PowerShell to use to make our own GUIS, but with our favorite scripting language used as the engine (instead of C#).  And the cool part is that anywhere that PowerShell runs and a GUI is available, our code will work there too!

(Sorry Server Core users of the World, Server Core does not have the WPF assemblies needed to run GUIs)

Interested?  Let’s begin!

Preparing Visual Studio

Start by launching Visual Studio

00-VisualStudio image
it’s so purple and creepy! Like a Gore Magala!

gore_magala

Not a Monster Hunter fan? Ok…moving on

You’ll want to click New Project on the left side of your screen

00-VisualStudio_newProject

Next, follow the numbers in the image below as you will 1. type WPF in the search box 2. click WPF Application in the main window, and then 3. customize the project name (if you’d like) and click OK.

00-VisualStudio_makeaWPF

This is what you’ll see, the fully blown Visual Studio UI.

VisualStudio
There is a lot of cruft we can disable though, so lets hide some elements.  For now, we don’t need Solution Explorer, which holds all of the files related to our project (since we won’t actually be publishing this project, just building its UI here) and we won’t need Properties until we add some items.

You can bring these items back by hitting F4, F6 or choosing them from the Alt-View menu up top
You can bring these items back by hitting F4, F6 or choosing them from the Alt-View menu up top

Now, we will want to display and then pin the toolbox on the left side, to give us our goods we can drag and move around.

00-VisualStudio_PinStuff

Alright, there we go, much less noise, and now we’re able to get started.

Making a GUI in Visual Studio

The center area now holds our form.  You’re looking at what the GUI will be for your script or tool. You can resize the window, or drag and drop items into our form.

Two Tool making tips to remember

Before we dig in deeper, you should remember these tennants:

  • Give it a name – Every good tool needs a name, without a name, it’s harder to understand what something is for non-enthusiasts and you might end up with something being called ‘the Dan Script’ or ‘The Stephen Tool’
  • Wait till its ready – Always add an image and put a small amount of polish on your tools before you release them, or you’ll never get over that first bad impression

Let’s name this bad boy: click the title bar of your GUI, then hit F4 to show Properties

You could also just click the text and hit F2
You could also just click the text and hit F2

Let’s start with our GUI by adding an image first, then some text. The elements we want to grab from the tool box are Image, and a TextBlock object.

00-VisualStudio_toolbox_image

In the Toolbox, click Image, then draw the area you want the image to take on your form.

Now’s a good time to hit F4 to bring back some of the stuff we hid earlier, because we want to add some properties to this image (namely, the file source).

Go to explorer, find an image you like, and then copy its path and paste it here (pro-tip, don’t use quotes).

Put the file path in the Source field. Pro-tip: no quotes
Put the file path in the Source field. Pro-tip: no quotes

00-VisualStudio_toolbox_textblockNow we’ll do the same again for a TextBlock.

I created mine right next to my MS-DOS FoxDeploy icon, and just added a bit of flavor text.

If you’re feeling really uninspired and just want to copy my text, then go ahead and enter the following:

“Use this tool to find out all sorts of useful disk information, and also to get rich input from your scripts and tools ”

If you really want to, you change the Font as well, by clicking in the Properties box on the right, and look towards the bottom for the Text area

Provided you’ve accomplished the above, you should now be looking at a pretty sweet field like this guy.

00-VisualStudio_lookingGood

But it doesn’t do anything yet…

One thing every good tool should have is a button!  Let’s add an OK button.  Go back to the toolbox, find the Button Control and drag and drop it.  I’ll put mine in the lower right hand corner.

A button shouldn’t say ‘Button’.  When you’ve clicked on the button, you can Hit F2 to quickly type in some new text for the face of the button, or edit this value in the properties as well.

00-VisualStudio_lookingGoodNowWithButton

I wasn’t satisfied with the miniscule font VS likes to use, so I decided to change it; you can change the size or font of pretty much anything by clicking it in the drafting pane, and then using the Properties window on the right (or Hit F4 to show it if properties is hiding) to change the Font Size.  I pumped the font up to 12 or 14.

I’d say if we add a text box (place where users can enter stuff) and a label (non-editable, it’s where we add a description to a field, we’ll have enough for now.  For both of these, go back to the toolbox and then grab one of each and draw them on your form.  I went agead and changed Label to say ‘UserName’, because I’ve started thinking about how to make this useful.

00-VisualStudio_done

 Coming Up…

In this guide, we installed Visual Studio and spent some time creating a WPF Powered GUI, which uses XAML as the language to describe the GUI.  All well and good, but how do we get this working in PowerShell??

Read on, intrepid reader, to find out.

Series Home page 
Part II – Deploying PowerShell GUIs in Minutes using Visual Studio

Advertisements

28 thoughts on “Part I – Creating PowerShell GUIs in Minutes using Visual Studio – A New Hope

  1. svangulick April 10, 2015 / 12:32 pm

    Good introduction Stephen. I also prefere to use visual studio in order to be able to use XAML to generate a GUI for a PowerShell application since I think XAML apps look way nicer then WPF. The main issue I had though, when I compared it to WPF (using Sapien PowerShell Studio) is the problem with multithreading. Basically, it ‘difficult’ to make your GUI responsive to user interaction.For instance, a task that would execute for some time in the back ground would make the application hang until that task in the background is finished. I didn’t face these issues with WPF/Sapien powershell Studio. Have you found an (easy way) to make the GUI not hang in these type of cases? Looking forward to the next post 🙂

  2. Shane April 10, 2015 / 8:16 pm

    I agree with you that creating a new object every single time you want a label or text box is tedious at best not to mention the new objects for location and size. I devised a way so that every object I use text boxes, labels, list views, Group boxes etc. is put into a generic function. Each object’s function has the basic properties of x and y location, w and h size, and the object you will be adding your new object to. After all the initial debugging, this works beautifully andlll I am able to call a new item, pass in a few coordinates and such, and spend more time worrying about logic rather than where the stupid button should go. Ive learned so much about powershell in such a short time im able to even use runspacepools to multithread a few items. 🙂 keep in mind I’m essentially developing an application for our network team to use, to manager several aspect of servers, Active Directory, Exchange, and even Citrix. 🙂 Also future updates/suggestion/addons are a breeze using these methods.

    • FoxDeploy April 10, 2015 / 9:50 pm

      Cool! I’d love to see what you’ve made. Do you have a code sample or post somewhere I might see it?

      • Shane Thompson April 11, 2015 / 7:42 pm

        Well I can’t share the code I use for work, so I’ll give you an example of how I set this up.

        First, for editors, I recommend using PowerGUI unless that is MSVS is capable… (Not Sure)

        Once installed, create a new document/script and you must first make sure you “import” the “classes” that allow PowerShell to use Window’s “Framework” to create forms.

        Commands: ~Add-Type -AssemblyName System.Windows.Forms

        These two commands are so your GUI doesn’t look like Windows 98! Unless you’re into that kind of thing. =] : ~Add-Type -AssemblyName System.Drawing ~[System.Windows.Forms.Application]::EnableVisualStyles()

        I’m assuming you are familiar with how to create a form and how to add items to it so just for simplicity, I will just explain some functions for example.

        *** I highly recommend keeping your code neat, formatted consistently, and commented, with the use of #Region and #EndRegion. This allows you to collapse your code so you no longer need to scroll 200+ lines just to find the other function. You can name them too!

        EX:

        #Region – Beginning of Some_Function

        Function Some_Function () {

        > Function Code – – – – – – “>” = Is just to denote tab spacing for this article. (Formatting!!!!!!)

        }

        #EndRegion – End of Some_Function

        So lets create a function that allows us to call a new form whenever you’d like. Before we do that we need to know the bare minimum requirements for a form to be loaded.

        Things that are required: We need to create a new object to a variable Title of the form. Specify location of the object Specify size of the object (Other objects like buttons and text boxes, you will need to know what object to add the new object to.)

        Title,Location, and Size will be our Parameters.

        Function New_Form {

        >Param (

        >>[String] $Form_Title, >>[Int] $Form_X, >>[int] $Form_y >>[int] $Form_W >>[int] $Form_H

        >)

        }

        Well, we now have the beginning shell of our function! =] Hurray!

        Next we create generic objects that create the form, apply its required properties, and then return the object from the function.

        Function New_Form {

        >Param (

        >>[String] $Form_Title, >>[Int] $Form_X, >>[int] $Form_y >>[int] $Form_W >>[int] $Form_H

        >)

        >$Object_Form = New-Object System.Windows.Forms.Form

        >$Object_Form.Title = $Form_Title

        >$System_Drawing_Point = New-Object System.Drawing.Point >$System_Drawing_Point.X = $Form_X >$System_Drawing_Point.Y = $Form_Y >$Object_Form.Location = System_Drawing_Point

        >$System_Drawing_Size = New-Object System.Drawing.Size >$System_Drawing_Size.Width = $Form_W >$System_Drawing_Size.Height = $Form_H > $Object_Form.Size = $System_Drawing_Size

        >Return $Object_Form

        }

        Getting Closer!!! Now all we need to do is call the function and set it to a variable!

        $Form_MainApplication = New_Form “Title of Form” 200 200 800 500

        Yay! We are done! … but wait wheres my form? Ohhhhh yea…

        $Form_MainApplication.ShowDialog() | Out-Null

        There’s you’re blank form!

        Now let’s say you want a function that will allow you to add a button to this… Okay follow all the same guideline except you need to pass in the object you are adding the button to as one of your parameters. Like so…

        Function New_Button {

        >Param (

        >>[String] $Button_Text, >>[Int] $Button_X, >>[int] $Button_y >>[int] $Button_W >>[int] $Button_H

        >>[Object] $Button_Holder

        >)

        >$Object_Button = New-Object System.Windows.Forms.Button

        >$Object_Button.Text = $Button_Text

        >$System_Drawing_Point = New-Object System.Drawing.Point >$System_Drawing_Point.X = $Button_X >$System_Drawing_Point.Y = $Button_y >$Object_Button.Location = System_Drawing_Point

        >$System_Drawing_Size = New-Object System.Drawing.Size >$System_Drawing_Size.Width = $Button_W >$System_Drawing_Size.Height = $Button_H > $Object_Button.Size = $System_Drawing_Size

        >$Button_Holder.Controls.Add(Object_Button)

        >Return $Object_Button

        }

        Now lets create a button!

        $Button_MyFirstButton = New_Button “Click Me!” 50 50 200 200 $Form_Main

        *** $Object_Holder *** This is crucial! When you decide to define group boxes for organizing your visuals, you will more than likely add your group boxes to the form and then the button to the group boxes… and to note the coordinate system is pixel based and 0,0 is at the top left of the $object_Holder.

        Another Note*** Make sure $Form_MainApplication.ShowDialog() is the last line of your script =] HeeHee

        Almost all objects will work with this “Syntax” for “Object Functioning”; text boxes, buttons, labels, group boxes, popup boxes, etc. I will say that list views work a little bit differently, the function is the same, but it returns as an array vs an object!!! You just have to perform an extra step after you call/create the list view object, but i’ll leave that for you to figure out!! =]

        You can take these functions a step further and add a few if statements for certain situations and format some coloring to standardize your form throughout, just be careful and test everything!!!

        One last recommendation:

        Define one region for all your “Object Functions” to sit at the top of your script (After your assemblies duh!!!), it will help with code organization and prevent any execution issues.

        You are going to love this! I would love to hear what GUIs anyone creates using this, and tell me how simple it was after getting it all set up!

        Welp, this is my example, and I really hope you find this information useful. I know it has made a difference in my work environment!

      • Shane April 12, 2015 / 7:41 am

        Well I can’t share the code I use for work, so I’ll give you an example of how I set this up.

        First, for editors, I recommend using PowerGUI unless that is MSVS is capable… (Not Sure)

        Once installed, create a new document/script and you must first make sure you “import” the “classes” that allow PowerShell to use Window’s “Framework” to create forms.

        Commands:
        ~Add-Type -AssemblyName System.Windows.Forms

        These two commands are so your GUI doesn’t look like Windows 98! Unless you’re into that kind of thing. =] :
        ~Add-Type -AssemblyName System.Drawing
        ~[System.Windows.Forms.Application]::EnableVisualStyles()

        I’m assuming you are familiar with how to create a form and how to add items to it so just for simplicity, I will just explain some functions for example.

        *** I highly recommend keeping your code neat, formatted consistently, and commented, with the use of
        #Region
        and
        #EndRegion.
        This allows you to collapse your code so you no longer need to scroll 200+ lines just to find the other function. You can name them too!

        EX:

        #Region – Beginning of Some_Function

        Function Some_Function () {

        > Function Code – – – – – – “>” = Is just to denote tab spacing for this article. (Formatting!!!!!!)

        }

        #EndRegion – End of Some_Function

        So lets create a function that allows us to call a new form whenever you’d like.
        Before we do that we need to know the bare minimum requirements for a form to be loaded.

        Things that are required:
        We need to create a new object to a variable
        Title of the form.
        Specify location of the object
        Specify size of the object
        (Other objects like buttons and text boxes, you will need to know what object to add the new object to.)

        Title,Location, and Size will be our Parameters.

        Function New_Form {

        >Param (

        >>[String] $Form_Title,
        >>[Int] $Form_X,
        >>[int] $Form_y
        >>[int] $Form_W
        >>[int] $Form_H

        >)

        }

        Well, we now have the beginning shell of our function! =] Hurray!

        Next we create generic objects that create the form, apply its required properties, and then return the object from the function.

        Function New_Form {

        >Param (

        >>[String] $Form_Title,
        >>[Int] $Form_X,
        >>[int] $Form_y
        >>[int] $Form_W
        >>[int] $Form_H

        >)

        >$Object_Form = New-Object System.Windows.Forms.Form

        >$Object_Form.Title = $Form_Title

        >$System_Drawing_Point = New-Object System.Drawing.Point
        >$System_Drawing_Point.X = $Form_X
        >$System_Drawing_Point.Y = $Form_Y
        >$Object_Form.Location = System_Drawing_Point

        >$System_Drawing_Size = New-Object System.Drawing.Size
        >$System_Drawing_Size.Width = $Form_W
        >$System_Drawing_Size.Height = $Form_H
        > $Object_Form.Size = $System_Drawing_Size

        >Return $Object_Form

        }

        Getting Closer!!! Now all we need to do is call the function and set it to a variable!

        $Form_MainApplication = New_Form “Title of Form” 200 200 800 500

        Yay! We are done! … but wait wheres my form? Ohhhhh yea…

        $Form_MainApplication.ShowDialog() | Out-Null

        There’s you’re blank form!

        Now let’s say you want a function that will allow you to add a button to this… Okay follow all the same guideline except you need to pass in the object you are adding the button to as one of your parameters. Like so…

        Function New_Button {

        >Param (

        >>[String] $Button_Text,
        >>[Int] $Button_X,
        >>[int] $Button_y
        >>[int] $Button_W
        >>[int] $Button_H

        >>[Object] $Button_Holder

        >)

        >$Object_Button = New-Object System.Windows.Forms.Button

        >$Object_Button.Text = $Button_Text

        >$System_Drawing_Point = New-Object System.Drawing.Point
        >$System_Drawing_Point.X = $Button_X
        >$System_Drawing_Point.Y = $Button_y
        >$Object_Button.Location = System_Drawing_Point

        >$System_Drawing_Size = New-Object System.Drawing.Size
        >$System_Drawing_Size.Width = $Button_W
        >$System_Drawing_Size.Height = $Button_H
        > $Object_Button.Size = $System_Drawing_Size

        >$Button_Holder.Controls.Add(Object_Button)

        >Return $Object_Button

        }

        Now lets create a button!

        $Button_MyFirstButton = New_Button “Click Me!” 50 50 200 200 $Form_Main

        *** $Object_Holder *** This is crucial! When you decide to define group boxes for organizing your visuals, you will more than likely add your group boxes to the form and then the button to the group boxes… and to note the coordinate system is pixel based and 0,0 is at the top left of the $object_Holder.

        Another Note*** Make sure $Form_MainApplication.ShowDialog() is the last line of your script =] HeeHee

        Almost all objects will work with this “Syntax” for “Object Functioning”; text boxes, buttons, labels, group boxes, popup boxes, etc. I will say that list views work a little bit differently, the function is the same, but it returns as an array vs an object!!! You just have to perform an extra step after you call/create the list view object, but i’ll leave that for you to figure out!! =]

        You can take these functions a step further and add a few if statements for certain situations and format some coloring to standardize your form throughout, just be careful and test everything!!!

        One last recommendation:

        Define one region for all your “Object Functions” to sit at the top of your script (After your assemblies duh!!!), it will help with code organization and prevent any execution issues.

        You are going to love this! I would love to hear what GUIs anyone creates using this, and tell me how simple it was after getting it all set up!

        Welp, this is my example, and I really hope you find this information useful. I know it has made a difference in my work environment!

  3. Shane April 10, 2015 / 8:16 pm

    I would like email notifications thats why im typing this XD

  4. shadsterwolf August 5, 2015 / 10:11 am

    I’m a monster hunter fan! Great stuff 😀

    I also enjoy scripting simple PS/VBA/Batch coding.

  5. Tom Alessi September 11, 2015 / 12:24 pm

    So this is simply excellent. I really appreciate it! So long ASCII menus!
    BUT! I’m getting the following error. Crashes PS or ISE. Crashes on multiple machines. Thing is, it’s not at ALL consistent. Errors out 1 in 20 times maybe. Almost always the 1st run of the day (weather still logged in or not). Any help would be GREAT! The error is:

    Problem signature:
    Problem Event Name: PowerShell
    NameOfExe: PowerShell_ISE.exe
    FileVersionOfSystemManagementAutomation: 6.3.9600.17090
    InnermostExceptionType: System.ComponentModel.Win32Exception
    OutermostExceptionType: System.ComponentModel.Win32Exception
    DeepestPowerShellFrame: unknown
    DeepestFrame: MS.Win32.UnsafeNativeMethods.PostMessage
    ThreadName: Pipeli..ution Thread
    OS Version: 6.3.9600.2.0.0.256.48
    Locale ID: 1033

    Read our privacy statement online:
    http://go.microsoft.com/fwlink/?linkid=280262

    If the online privacy statement is not available, please read our privacy statement offline:
    C:\WINDOWS\system32\en-US\erofflps.txt

  6. sunshineknox October 16, 2015 / 12:59 pm

    I’m looking to shadow users that are on windows 2012R2 using powershell that’s a gui for helpdesk. They would need to see a list of users to choose from. It can be executed on the RDS server too. This will be for a citrix server running xenapp 7.6.

  7. Syparon March 3, 2016 / 1:14 pm

    great ! 😀

  8. Noel March 21, 2016 / 11:02 am

    Hi,

    I’ve just installed visual studio 2015 express but cannot find “WPF Addplication” when i try to search.

    What should i take ?

    My goal is just to provide a simple visual interface to a few of my powershell scripts.

    Thank you.

  9. Morry May 6, 2016 / 5:55 pm

    Part I is fine Part II doesn’t work there seems to be a syntax error
    You cannot call a method on a null-valued expression.
    At line:51 char:93
    + $xaml.SelectNodes(“//*[@Name]”) | %{Set-Variable -Name “WPF$($_.Name)” -Value $Form.FindName <<<< ($_.Name)}
    + CategoryInfo : InvalidOperation: (FindName:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    • FoxDeploy May 6, 2016 / 6:03 pm

      Hi Morry, thanks for dropping by. If you post your code as a gist (gist.github.com), and paste the link here, I can help find the issue.

  10. Morry May 20, 2016 / 10:27 pm

    I gave up on the Visual Studio but I have a problem maybe you have seen. I created a ps script and it works fine on my full windows laptop but when I create a USB PE stick and run the script it partially works. Here is the issue I have a button that calls $dialog which is an OpenFileDialog so I call dialog.ShowDialog() I get HRESULT 80040111 error something about COMException error. Any clues Like I say it works fine under full windows but not PE

    • FoxDeploy May 20, 2016 / 11:12 pm

      Winpe is a different subsystem and doesnt include powershell support vg default. You have to add powershell support to your winpe image then it will work.

  11. Morry May 21, 2016 / 11:52 am

    I have added the Powershell package
    & $DISM_Path\dism.exe /Image:$WinPE_MountFolder /Add-Package /PackagePath:$WinPE_OCs_Path\WinPE-PowerShell.cab
    & $DISM_Path\dism.exe /Image:$WinPE_MountFolder /Add-Package /PackagePath:$WinPE_OCs_Path\en-us\WinPE-PowerShell_en-us.cab

    • FoxDeploy May 21, 2016 / 1:50 pm

      I would use mdt to do this.way easier than messing with dism. With dism, proceed at your own peril.

  12. Nick October 3, 2017 / 7:56 am

    What tool can I use to package up not only my powershell script file, but other resource files as well into a single EXE? I’d like to hand this EXE over to other admins to use where all they have to do is double click on the file and it will run my powershell script to start the gui and have all supporting files as well for things like icon files, pictures, other supporting script files, etc.

    • FoxDeploy October 3, 2017 / 9:13 am

      PowerShell studio can do this natively for you. It’s not free, but it’s very affordable

Have a code issue? Share your code by going to Gist.github.com and pasting your code there, then post the link here!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s