WPF (Windows Presentation Foundation) and XAML 2020
- To create the user interface in WPF we use Extensible Application Markup Language (XAML), which is the XML based language for creating declarative user interfaces. This also helps to separate appearance/visual behavior (designer) from functionality/program logic (programmer).
- It's primary measuring unit is not pixel based, so applications display at any dpi (Resolution independence using vector graphics).
- WPF is using DirectX that can enjoy the benefits of hardware acceleration for smoother graphics and overall better performance.
- Rich composition and customization (WPF controls can be composed in many ways).
Select WPF Application under the Windows node of the Visual C# root.
By selecting the WPF Application, we will be provided with initial Window and Application-derived types as well as the references to each of the WPF assemblies such as PresentationCore.dll, PresentationFramework.dll,WindowsBase.dll, and Extensible Application Markup Language or XAML(pronounced "zammel") definition.
At this point, we should be able to compile and run the application.
Visual Studio 2013 provides a Toolbox that has numerous WPF controls, a visual designer that can be used to assemble our UI, and a Properties window to set the properties of a selected control.
The designer for an *.axml file is divided into two panes, by default, the upper pane for the window and the bottom pane for the XAML definition.
When we try to type into the XAML pane, we may find the IntelliSense as we expected:
Handling events within a WPF application, unlike Windows Form, is not done by clicking the lightning bolt button of the Properties window. Actually, this button does not exist in WPF application! When we want to handle events for a WPF widget, we could author all of the code manually using the expected C# syntax; however, if we type an event name in the XAML editor, we will activate the New Event Handler pop-up menu as we can see from the picture below:
If we manually enter an event name encased in quotation mark, we can specify any method we want. If we would rather simply have an IDE generate a default name which takes the form NameOfControl_NameOfEvent, we can double-click the
private void Button_Click(object sender, RoutedEventArgs e) { }
Since we've seen the basic toos used within Visual Studio 2013 to manipulate WPF applications, let's use this IDE to build an example code that demonstrates the process of parsing XAML at runtime.
To interact with XAML at runtime, we need ti know the XamlReader and XamlWriter types, both of which are defined within the System.Windows.Markup namespace. To show how to programmatically handle a
<!-- MainWindow.xaml --> <Window x:Class="WPFApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" Closed="Window_Closed" WindowStartupLocation="CenterScreen"> <DockPanel LastChildFill="True"> <!-- This button will launch a window with defined XAML--> <Button DockPanel.Dock="Top" Name="btnViewXaml" Width="100" Height="40" Content="View Xaml" Click="btnViewXaml_Click" /> <!-- Input area for the text we type in --> <TextBox AcceptsReturn="True" Name="txtXamlData" FontSize="14" Background="Black" Foreground="Yellow" BorderBrush="Blue" VerticalScrollBarVisibility="Auto" AcceptsTab="True"> </TextBox> </DockPanel> </Window>
Note that we replaced the initial <Grid;> with a <DockPanel;> that contains a Button and a TextBox, and the Button's event Click has been handled. Also, within the <Window> element, the Loaded and Closed events of the Window itself have been added.
If we have used the designer to handle the events, we can find the following code has been generated in the file, MainWindow.xaml.cs.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WPFApp { ////// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { } private void Window_Closed(object sender, EventArgs e) { } private void btnViewXaml_Click(object sender, RoutedEventArgs e) { } } }
At this point, we need to import the following namespaces into our MainWindow.xaml.cs:
using System.IO; using System.Windows.Markup;
The Loaded event of our main window checks if there is a YourXaml.xaml file in the folder containing the application. If it exists, we will read in the data and place it into the TextBox on the main window. If not, we will fill the TextBox with an initial default XAML description of an empty window. Note that we're using <StakPanel> to set the Window's Content property.
private void Window_Loaded(object sender, RoutedEventArgs e) { // When the main window of the app loads, // place basic XAML text into the text box if(File.Exists(System.Environment.CurrentDirectory + "\\YourXaml.xaml")) { txtXamlData.Text = File.ReadAllText("YourXaml.xaml"); } else { txtXamlData.Text = "<Window xmlns= \"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n" +"xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"" +" Height=\"350\" Width=\"525\" WindowStartupLocation=\"CenterScreen\">\n" +"<StackPanel>\n" +"</StackPanel>\n" +"</Window>"; } }
Now, we can compile and run this xaml viewer application:
When we click the Button, we will first save the current data in the TextBox into the YourXaml.xaml file. Then, we'll read in the persistent data use File.Open() to get a Stream-derived type. This is necessary because the XamlReader.Load() method requires a Stream-derived type to represent the XAML to be parsed.
After loading the XAML description of the <Window> we want to construct, create an instance of System.Windows.Window based on the in-memory XAML, and display the Window as a modal dialog.
private void btnViewXaml_Click(object sender, RoutedEventArgs e) { // Write the data in the text block to a local *.xaml file. File.WriteAllText("YourXaml.xaml", txtXamlData.Text); // This is the window that will put XAML dynamically Window myWindow = null; // Open local *.xaml file. try { using (Stream st = File.Open("YourXaml.xaml", FileMode.Open)) { // Connect the XAML to the Window object myWindow = (Window)XamlReader.Load(st); myWindow.ShowDialog(); } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
In the code, we wrapped much of our logic within a try/catch block to handle the case when the YourXaml.xaml contains ill-formed markup.
The Closed event of our Window type will ensure that the data in the TextBox is persisted to the YourXaml.xaml file:
private void Window_Closed(object sender, EventArgs e) { // Write the data in the text block to a local *.xamol file File.WriteAllText("YourXaml.xaml", txtXamlData.Text); }
Now, it's the time for testing.
Run the code and enter some XAML into the text area.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="350" Width="525" WindowStartupLocation="CenterScreen"> <StackPanel> <Rectangle Fill = "Green" Height = "40" Width = "200" /> <Button Content = "OK" Height = "40" Width = "100" /> <Label Content = "{x:Type Label}" /> </StackPanel> </Window>
Once we click the button, we will see a window that renders our XAML definitions:
Note: There are still a lot more to learn, especially, WPF Controls, and this will be in my next tutorial. - Thanks.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization