Wpf where to put code?
Windows Presentation Foundation (WPF) is a technology for building user interfaces using .NET. Just as with Windows Forms, you can build interfaces that work with PowerShell and don’t require the installation of any additional libraries on target machines. You can deploy a PS1 script that contains both the UI code as well as the business logic code for running the scripts you’d like.
In this blog post, we’ll look at the new WPF designer for PSScriptPad and how it integrates into Visual Studio Code. We’ll create a new window, add a couple of controls and then wire up an event handler to handle a button click.
You can view the video version of this tutorial on YouTube.
To create a new WPF window, we will need to create a new XAML file. XAML, or Extensible Application Markup Language, is similar to XML and allows you to define GUIs with a declarative syntax. Unlike Windows Forms, it doesn’t generate the GUI using .NET method calls. Rather, the XAML is read by .NET to produce the class based on the XML definition.
A basic Window looks like this.
This XAML above creates a simple window with a title and a predefined size. In order to show this Window via PowerShell, we need to load the XAML and produce a Window object that we can use from a script.
Depending on your PowerShell host (ISE vs PowerShell.exe), you may need to load the WPF libraries in order to use the classes required to work with XAML. To do so, you can use the following method call. This will work in Windows PowerShell and PowerShell 7 but not PowerShell 6.
The following function can be used to load a XAML file and produce a window object.
The function reads the window.xaml file and then loads it using the XamlReader class. It returns a Window object that we can then call methods on. The ShowDialog method call will open the window. Running this script should produce a window like this.
PowerShell Pro Tools provides a drag and drop editor for WPF XAML files. When you install the PowerShell Pro Tools extension for VS Code, it will include a new button at the top of XAML files to show a form designer.
When you click this button, it will open PSScriptPad and present you with the XAML editor for your WPF Window. The top pane shows the current designer view. You can visually drag and resize components within the designer view. The bottom portion provides you with the XAML code so you can quickly make changes and see them reflected in the designer at the top. The Properties panel allows you to set properties of the selected component.
In the bottom pane, we can add a couple of controls using the following XAML. This creates a new Grid with a label and a button.
The resulting designer will look like this.
You can run the PS1 file from VS Code to see the updated WPF window.
Now that we have added several controls to the XAML file, we can start to work with those controls within PowerShell. To work with controls in PowerShell, you will need to set the name of the control within the XAML, we’ve done this with the button and label above using the x:Name attribute.
You can find controls within the window using the FindName method. Simply pass in the name of the control and a new object will be returned for that control. To find the button and label, we can find each by name.
Now that we have a reference to our button and label controls, we can call method sand set properties of those controls. For the purpose of this example, we are going to add a click handler to the button and update the label when the button is clicked. This code is similar to what you would do within Windows Forms.
When the click the button, it will now set the label’s content to the string Hello.
In this blog post, we looked at how to build WPF applications in VS Code with PowerShell. You can define WPF windows with XAML and call those XAML files via PowerShell. You can use the PowerShell Pro Tools WPF designer to set properties and drag and drop controls within your WPF form.
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.
In this tutorial, you learn how to:
Here's a preview of the app you'll build while following this tutorial:
The first step to creating a new app is opening Visual Studio and generating the app from a template.
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.
Support for WPF in Visual Studio has five important components that you'll interact with as you create an app:
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 example:
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
First, run the project and see the default output. You'll see 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:
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
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
Before we add the new rows and columns, add a new attribute to the
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:
Now that the grid has been created, we can start adding controls to it. First, start with the label control. Create a new
The 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: . 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
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. For example, the label control has a height value that ensures that you can read it.
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 that 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
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.
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.
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
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
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
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.
The
Now you need to generate the handler code. Right-click on ButtonAddName_Click and select Go To Definition. This action generates 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:
- 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.
- In the Configure your new project window, do the following:
Code-behind is a term used to describe the code that is joined with markup-defined objects, when a XAML page is markup-compiled. This topic describes requirements for code-behind as well as an alternative inline code mechanism for code in XAML.
This topic contains the following sections:
This topic assumes that you have read the XAML in WPF and have some basic knowledge of the CLR and object-oriented programming.
The XAML language includes language-level features that make it possible to associate code files with markup files, from the markup file side. Specifically, the XAML language defines the language features x:Class Directive, x:Subclass Directive, and x:ClassModifier Directive. Exactly how the code should be produced, and how to integrate markup and code, is not part of what the XAML language specifies. It is left up to frameworks such as WPF to determine how to integrate the code, how to use XAML in the application and programming models, and the build actions or other support that all this requires.
x:Code is a directive element defined in WPF schema) will not try to interpret the contents literally as XML.
You should consider avoiding or limiting the use of inline code. In terms of architecture and coding philosophy, maintaining a separation between markup and code-behind keeps the designer and developer roles much more distinct. On a more technical level, the code that you write for inline code can be awkward to write, because you are always writing into the WPF mappings include most but not all CLR namespaces that are present in the WPF assemblies; you will have to fully qualify calls to types and members contained within the other CLR namespaces. You also cannot define anything beyond the partial class in the inline code, and all user code entities you reference must exist as a member or variable within the generated partial class. Other language specific programming features, such as macros or #ifdef against global variables or build variables, are also not available. For more information, see x:Code Intrinsic XAML Type.