In this short tutorial, you'll learn how to create a new Windows Presentation Foundation (WPF) app with Visual Studio. Once the initial app has been generated, you'll learn how to add controls and how to handle events. By the end of this tutorial, you'll have a simple app that adds names to a list box.
Important
- The XAML code editor in the Visual Studio IDE includes all the tools you need to create WPF and UWP apps for the Windows platform, and for Xamarin.Forms. This article outlines both the role the code editor plays when you develop XAML-based apps, and the features that are unique to the XAML code editor in Visual Studio 2019.
- Start Visual Studio (Express / Community / Professional) and open a “ New Project. Visual Studio – New Project. Select “ WPF Appplication ” and confirm with “ OK “. Visual Studio – WPF Application. Result: The result is an empty WPF form that will be extended within the next steps. Visual Studio – Workspace.
Visual Studio 2019 will be the release to support building.NET Core 3 applications. Windows Desktop Frameworks are now Open Source and the first wave of source code is already available on GitHub. You can create new.NET Core projects for WPF and Windows Forms from the command line.
The Desktop Guide documentation for .NET 5 (and .NET Core) is under construction.
In this tutorial, you learn how to:
- Create a new WPF app
- Add controls to a form
- Handle control events to provide app functionality
- Run the app
Here's a preview of the app you'll build while following this tutorial:
Prerequisites
- Visual Studio 2019 version 16.9 or later versions
- Select the Visual Studio Desktop workload
- Select the .NET 5 individual component
Windows Presentation Foundation Examples
Create a WPF app
The first step to creating a new app is opening Visual Studio and generating the app from a template.
Open Visual Studio.
Select Create a new project.
In the Search for templates box, type wpf, and then press Enter.
In the code language dropdown, choose C# or Visual Basic.
In the templates list, select WPF Application and then select Next.
Important
Don't select the WPF Application (.NET Framework) template.
In the Configure your new project window, do the following:
- In the Project name box, enter Names.
- Select the Place solution and project in the same directory check box.
- Optionally, choose a different Location to save your code.
- Select the Create button.
Once the app is generated, Visual Studio should open the XAML designer pane for the default window, MainWindow. If the designer isn't visible, double-click on the MainWindow.xaml file in the Solution Explorer pane to open the designer.
Important parts of Visual Studio
Support for WPF in Visual Studio has five important components that you'll interact with as you create an app:
Solution Explorer
All of your project files, code, windows, resources, will appear in this pane.
Properties
This pane shows property settings you can configure based on the item selected. For example, if you select an item from Solution Explorer, you'll see property settings related to the file. If you select an object in the Designer, you'll see settings for that item.
Toolbox
The toolbox contains all of the controls you can add to a form. To add a control to the current form, double-click a control or drag-and-drop the control.
XAML designer
This is the designer for a XAML document. It's interactive and you can drag-and-drop objects from the Toolbox. By selecting and moving items in the designer, you can visually compose the user interface (UI) for your app.
When both the designer and editor are visible, changes to one is reflected in the other. When you select items in the designer, the Properties pane displays the properties and attributes about that object.
XAML code editor
This is the XAML code editor for a XAML document. The XAML code editor is a way to craft your UI by hand without a designer. The designer may infer the values of properties on a control when the control is added in the designer. The XAML code editor gives you a lot more control.
When both the designer and editor are visible, changes to one is reflected in the other. As you navigate the text caret in the code editor, the Properties pane displays the properties and attributes about that object.
Target .NET 5.0
After you create your project, change the target framework to .NET 5.0. Double-click on the Names project file in the Solution Explorer. This opens up the project file for editing. Set the <TargetFramework>
element to net5.0-windows
:
Save the project file and then close the editor tab.
Examine the XAML
After your project is created, the XAML code editor is visible with a minimal amount of XAML code to display the window. If the editor isn't open, double-click the MainWindow.xaml item in the Solution Explorer. You should see XAML similar to the following:
Let's break down this XAML code to understand it better. XAML is simply XML that can be processed by the compilers that WPF uses. It describes the WPF UI and interacts with .NET code. To understand XAML, you should, at a minimum, be familiar with the basics of XML.
The document root <Window>
represents the type of object being described by the XAML file. There are eight attributes declared, and they generally belong to three categories:
Namespaces
An XML namespace provides structure to the XML, determining what is or isn't allowed to be declared in the file.
The main
xmlns
attribute imports the XML namespace for the entire file, and in this case, maps to the types declared by WPF. The other XML namespaces declare a prefix and import other types and objects for the XAML file. For example, thexmlns:local
namespace declares thelocal
prefix and maps to the objects declared by your project, the ones declared in theNames
code namespace.x:Class
attributeThis attribute maps the
<Window>
to the type defined by your code: the MainWindow.xaml.cs or MainWindow.xaml.vb file, which is theNames.MainWindow
class.Title
attributeAny normal attribute declared on the XAML object sets a property of that object. In this case the
Title
attribute sets theWindow.Title
property.
Change the window
First, let's run the project and see the default output. You'll see that a window that pops up, without any controls, and a title of MainWindow:
For our example app, this window is too large, and the title bar isn't descriptive. Change the title and size of the window by changing the appropriate attributes in the XAML to the following values:
Prepare the layout
WPF provides a powerful layout system with many different layout controls. Layout controls help place and size child controls, and can even do so automatically. The default layout control provided to you in this XAML is the <Grid>
control.
The Grid
control lets you define rows and columns, much like a table, and place controls within the bounds of a specific row and column combination. You can have any number of child controls or other layout controls added to the Grid
. For example, you can place another Grid
control in a specific row and column combination, and that new Grid
can then define more rows and columns and have its own children.
The <Grid>
control defines rows and columns in which your controls will be. A grid always has a single row and column declared, meaning, the grid by default is a single cell. That doesn't really give you much flexibility in placing controls.
Before we add the new rows and columns, add a new attribute to the <Grid>
element: Margin='10'
. This insets the grid from the window and makes it look a little nicer.
Next, define two rows and two columns, dividing the grid into four cells:
Select the grid in either the XAML code editor or XAML designer, you'll see that the XAML designer shows each row and column:
Add the first control
Now that the grid has been created, we can start adding controls to it. First, start with the label control. Create a new <Label>
element inside the <Grid>
element, after the row and column definitions, and give it a string value of Names
:
The <Label>Names</Label>
defines the content Names
. Some controls understand how to handle content, others don't. The content of a control maps to the Content
property. Setting the content through XAML attribute syntax, you would use this format: <Label />
. Both ways accomplish the same thing, setting the content of the label to display the text Names
.
We have a problem though, the label takes up half the window as it was automatically assigned to the first row and column of the grid. For our first row, we don't need that much space because we're only going to use that row for the label. Change the Height
attribute of the first <RowDefinition>
from *
to Auto
. The Auto
value automatically sizes the grid row to the size of its contents, in this case, the label control.
Notice that the designer now shows the label occupying a small amount of the available height. There's now more room for the next row to occupy. Most controls define some sort of height and width value that they should occupy that looks best for them. In the case of the label control, it has a height value that ensures that you can read it.
Control placement
Let's talk about control placement. The label created in the section above was automatically placed in row 0 and column 0 of the grid. The numbering for rows and columns starts at 0 and increments by 1 for each new row or column. The control doesn't know anything about the grid, and the control doesn't define any properties to control its placement within the grid. The control could have even been placed within some other layout control which has its own set of rules defining how to place controls.
How do you tell a control to use a different row or column when the control has no knowledge of the grid? Attached properties! The grid takes advantage of the powerful property system provided by WPF. The grid defines new properties that the child controls can declare and use. The properties don't actually exist on the control itself, they're attached by the grid when the control is added to the grid.
The grid defines two properties to determine the row and column placement of a child control: Grid.Row
and Grid.Column
. If these properties are omitted from the control, it's implied that they have the default values of 0, so, the control is placed in row 0
and column 0
of the grid. Try changing the placement of the <Label>
control by setting the Grid.Column
attribute to 1
:
Notice how your label now moved to the second column. You can use the Grid.Row
and Grid.Column
attached properties to place the next controls we'll create. For now though, restore the label to row 0.
Create the name list box
Now that the grid is correctly sized and the label created, add a list box control on the row below the label. The list box will be in row 1
and column 0
. We'll also give this control the name of lstNames
. Once a control is named, it can be referenced in the code behind. The name is assigned to the control with the x:Name
attribute.
Add the remaining controls
The last two controls we'll add are a text box and a button, which the user will use to enter a name to add to the list box. However, instead of trying to create more rows and columns for the grid, we'll put these controls into the <StackPanel>
layout control.
The stack panel differs from the grid in how the controls are placed. While you tell the grid where you want the controls to be with the Grid.Row
and Grid.Column
attached properties, the stack panel works automatically by placing the first control, then placing the next control after it, continuing until all controls have been placed. It 'stacks' each control below the other.
Create the <StackPanel>
control after the list box and put it in grid row 1
column 1
. Add another attribute named Margin
with a value of 5,0,0,0
:
The Margin
attribute was previously used on the grid, but we only put in a single value, 10
. Now we've used a value of 5,0,0,0
on the stack panel. The margin is a Thickness
type and can interpret both values. A thickness defines the space around each side of a rectangular frame, left, top, right, bottom, respectively. If the value for the margin is a single value, it uses that value for all four sides.
Next, create a <TextBox>
and <Button>
control in the <StackPanel>
.
The layout for the window is complete. However, our app doesn't have any logic in it to actually be functional. Next, we need to hook up the control events to code and get the app to actually do something.
Add code for the Click event
The <Button>
we created has a Click
event that is raised when the user presses the button. You can subscribe to this event and add code to add a name to the list box. Just like you set a property on a control by adding a XAML attribute, you can use a XAML attribute to subscribe to an event. Set the Click
attribute to ButtonAddName_Click
Now you need to generate the handler code. Right-click on ButtonAddName_Click
and select Go To Definition. This will generate a method in the code behind for you that matches the handler name you've entered.
Next, add the following code to do these three steps:
- Make sure that the text box contains a name.
- Validate that the name entered in the text box doesn't already exist.
- Add the name to the list box.
Run the app
Now that the event has been coded, you can run the app by pressing the F5 key or by selecting Debug > Start Debugging from the menu. The window is displayed and you can enter a name in the textbox and then add it by clicking the button.
Next steps
PowerShell is a command-line tool but did you know it can also be used as a base for graphical interfaces? Sometimes command-line isn’t the best kind of interface for a particular instance. Building a PowerShell GUI for for your service desk is a great example. This is one of those times when it is more appropriate to build graphical tools instead.
Quit with helpdesk tickets. Expedite user password resets and unlocks with Specops’ Secure Service Desk: no helpdesk calls, secure process, and happy end-users. Try for free today!
PowerShell can use and expose .NET functionality and features. As a result, it is possible to write GUI front ends for the scripts you create. Building PowerShell GUIs may seem complicated, especially if you are a beginner.
But if you have basic experience with PowerShell scripting then there’s no reason for you not to learn and adapt the practice of creating GUI for your scripts.
In this post, you will learn how to create a PowerShell GUI using the Windows Presentation Framework (WPF).
Prerequisites
Before you dive in, please be sure you meet the following requirements:
- Visual Studio 2017 or later – You’ll use this to create the graphical user interface using WPF. You can download a free/community version.
- A script editor – I use Visual Studio Code, but you can also use another text editor of your choice. Some other options are Notepad++ and the built-in PowerShell ISE
- A Windows 10 computer with Windows PowerShell 5.1.
Building the Script
In this post, you’ll create a simple script named Main.ps1. In the script, you’ll write code that will pull disk information from a local or remote system by querying the Win32_LogicalDisk WMI class.
You’ll need a script to wrap a GUI around first. I’ve chosen to use a script that allows you to provide a computer name and query disk information. This is, by no means, necessary to build a GUI though. Use the techniques you learn in this post to adapt your GUIs to your own scripts.
As an example script, I’ll create a function that performs the following actions:
- Accept input for the name of the computer to query
- Query the computer and store the fixed disks information to a variable
- Return the results
Writing the Function
Below is the function you’ll use for this project, aptly named Get-FixedDisk
. This project’s purpose is to get the information about the non-removable or fixed disks on the target machine.
While this piece of code can be used as is, creating a GUI would be beneficial if you just want to perform a quick query without having to dot source the function and manually typing in the commands each time.
You can see that I’ve added a param() block in the code. This is to instruct the function to accept inputs based on the type of data indicated.
In the example, I’ve added a Computer
parameter which accepts a string value. Also, by adding the Mandatory
parameter attribute, it ensures that the function does not run if the Computer
parameter is not specified at runtime.
Next, line 18 shows the actual WMI query command which gets the list of all logical disks and saves the results to a variable named $DiskInfo
. I’ve also added a filter to get only the disks with DriveType=3
. This filter ensures that only the information about local fixed disks is displayed.
Importing the Code (Dot Sourcing)
At this point, you now have a working script and are ready to test it. But before you can test the script, you need to import the code into a PowerShell session. One way of loading code into a PowerShell session is by dot sourcing.
To dot source a script, type a dot (.
) and a space before the script path. If the script were in the C:PoshGUI-sample folder, you could dot source it like below.
You can also specify the full path if you’re no in the current working directory. In the example code below, you can see the full path of the script.
Now that we have imported the code into the memory, we can proceed with testing the function we’ve created. In the below example, it shows that the Get-FixedDisk
function is used to query the computer poshLabExc.
Building the PowerShell GUI
At this point, you’ve created the script file named Main.ps1, and inside the script created the function Get-FixedDisk
. You were also able to test and confirm that the function is working.
Visual Studio Code Wpf Build
Now that you know that the script works, you can start building the GUI.
Designing the PowerShell GUI Form
First plan how you’d like the GUI to look and the elements you’d like to use. For this simple example, our GUI will have:
- a text box where the computer name can be entered
- a button to execute the function
- a text box where we can display the results
.net Core Wpf Visual Studio Code
Next, you can begin building it!
To start creating the GUI, open up Visual Studio and create a new project.
Once Visual Studio is open, click on File (1) –> New (2) –> Project (3).
Under the New Project window, choose Visual C# (1), select WPF App (.NET Framework) (2), change the name to PoshGUI-sample (3) and click OK.
Once the project is created, a blank form will be presented with the name of MainWindow.xaml.
You now need to format this form to fit our requirements. Below are the controls and format that you’ll need to add.
- Window
- Title: Disk Information
- Height: 326
- Width: 403
- Controls (4)
- Label
- Content: “Computer Name:”
- Margin: 10, 10, 0, 0
- TextBox
- Name: txtComputer
- Text: “”
- Height: 23
- Width: 174
- Button
- Name: btnQuery
- Content: Query
- Margin: 0, 13, 12, 0
- TextBox
- Name: txtResults
- Text: “”
- IsReadOnly: True
- Margin: 10, 60, 0, 0
- Height: 225
- Width: 373
- Label
The final appearance of the form should be similar to what is shown in the image below. You can rearrange the layout of your window differently. Be creative!
Combining the Script and the PowerShell GUI
Once you are happy with your design, you can now start integrating it with the script.
PowerShell cannot display forms natively. To be able to display the form, we need to add a line of code to the very top of our script to support rendering of the WPF Form.
Then add code to perform the following actions:
- Import and read the XAML code of the form.
- Dynamically create variables assigned to each named controls
- Display the form
Below is the updated code inside your script.
Note: Make sure to modify the line $xamlFile
and point it to the full path of your MainWindow.xaml file.
Note: $Null = $window.ShowDialog()
must always be the last line of code inside your script.
When you run this code by executing the Main.ps1 script, you should see the example output below.
As you can see, the three named controls were assigned their variables. These variable names will be referenced later on in the script when we add the control logic code.
- var_btnQuery
- var_btnComputer
- var_txtResults
Bear in mind that the script at this point can only display the form, but the controls are useless since you haven’t added the code yet.
Adding the Button Click Event Code
Now that you’ve successfully modified the script to import and display the GUI, begin adding the code to the controls to retrieve and display the disk information data.
In this project, only the btnQuery
button will be assigned an action. The other controls will only serve as input and output/display controls. This means that we only need to add a click event code to btnQuery
.
To add the click action to btnQuery
, assign the code below to its corresponding variable name $var_btnQuery
. Copy the code below and insert it in between the Get-Variable var_*
and $Null = $window.ShowDialog()
code references in the script.
Testing the Finished PowerShell GUI
With all parts covered, below is the completed code for our script which incorporates the function and the PowerShell GUI that we’ve designed.
As you can see below, after calling the script in PowerShell, the PowerShell GUI windows appeared. Then you’re able to enter a valid computer name to test the functionality.
Summary
In this article, you learned how to create a simple function that accepts input and return results. You also learned how to create a basic WPF PowerShell GUI as well as how to import it to act as a front-end for the PowerShell script you created.
This is just a basic script and GUI combination. Numerous improvements can be done such as:
- Formatting the size and free space to display as GB values.
- Change the name of the property displayed.
- Use GridView instead of TextBox to show the results.
- Add an import button to loop through a list of servers from a CSV file.
It is up to you to modify and add functionality based on your requirements.