.NET WPF Introduction
Windows Presentation Foundation (WPF) is a powerful UI framework for creating desktop applications with visually stunning user interfaces on Windows. If you're looking to build modern, feature-rich desktop applications with .NET, WPF is an excellent choice to explore.
What is Windows Presentation Foundation?
Windows Presentation Foundation (WPF) is a part of the .NET Framework that provides a unified programming model for building desktop applications with rich user interfaces. Introduced with .NET Framework 3.0 in 2006, WPF has evolved to be a robust platform for creating Windows desktop applications.
Key Features of WPF
- XAML-based UI design: Separate UI design from business logic
- Rich graphics support: Vector-based rendering for resolution independence
- Data binding: Powerful connection between UI and data
- Templates and styles: Easily customize and reuse UI elements
- Animation and effects: Create dynamic and engaging interfaces
- Hardware acceleration: Leverage GPU for faster performance
WPF vs. Windows Forms
Before WPF, Windows Forms was the primary UI framework for Windows desktop applications. Here's how they compare:
Feature | WPF | Windows Forms |
---|---|---|
Rendering | Vector-based (resolution independent) | Pixel-based |
UI Description | XAML (declarative) | C# code (imperative) |
UI/Logic Separation | Strong separation (MVVM pattern) | Limited separation |
Styling | Rich styling system | Basic styling |
3D Support | Built-in | Limited |
Hardware Acceleration | Yes | No |
Getting Started with WPF
Let's create a simple "Hello World" WPF application to get started.
Required Tools
- Visual Studio (Community edition or higher)
- .NET SDK (Latest version recommended)
- Basic understanding of C# programming
Creating Your First WPF Application
- Open Visual Studio
- Select "Create a new project"
- Search for "WPF App" and select "WPF Application" under C#
- Name your project "HelloWPF" and click "Create"
Visual Studio will generate two important files:
- MainWindow.xaml: Contains UI in XAML format
- MainWindow.xaml.cs: Contains C# code-behind for the window
Understanding the Basic Structure
Here's the default XAML generated when you create a new WPF application:
<Window x:Class="HelloWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HelloWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
This creates an empty window with a Grid layout container. The Window element is the root of a WPF window, and Grid is one of several layout containers in WPF.
Adding Content to the Window
Let's add some basic controls to our window:
<Window x:Class="HelloWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:HelloWPF"
mc:Ignorable="d"
Title="Hello WPF" Height="300" Width="400">
<StackPanel Margin="20">
<TextBlock Text="Hello, WPF!"
FontSize="24"
FontWeight="Bold"
Margin="0,0,0,20"/>
<TextBox x:Name="nameTextBox"
Margin="0,0,0,10"
PlaceholderText="Enter your name"/>
<Button Content="Say Hello"
Click="Button_Click"
Width="100"
HorizontalAlignment="Left"/>
<TextBlock x:Name="outputText"
Margin="0,20,0,0"
FontSize="16"/>
</StackPanel>
</Window>
Now let's add the logic in the code-behind file (MainWindow.xaml.cs):
using System.Windows;
namespace HelloWPF
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(nameTextBox.Text))
{
outputText.Text = "Please enter your name.";
}
else
{
outputText.Text = $"Hello, {nameTextBox.Text}! Welcome to WPF.";
}
}
}
}
When you run this application, you'll see a window with:
- A title "Hello, WPF!"
- A text box where you can enter your name
- A button labeled "Say Hello"
- An area where a greeting will appear when you click the button
Understanding XAML
XAML (eXtensible Application Markup Language) is the declarative XML-based language used to define WPF user interfaces. It separates UI design from the application logic.
Key Concepts in XAML
Element Syntax
XAML elements correspond to .NET classes:
<Button Content="Click Me" />
This creates an instance of the Button
class and sets its Content
property.
Property Attribute Syntax
Simple properties are set as attributes:
<Button Content="Click Me" Width="100" Height="30" />
Property Element Syntax
Complex properties use property element syntax:
<Button>
<Button.Content>
<StackPanel>
<Image Source="/Images/icon.png" Width="16" Height="16" />
<TextBlock Text="Click Me" />
</StackPanel>
</Button.Content>
</Button>
Content Syntax
For classes with a content property (usually Content
), you can use simplified syntax:
<Button>Click Me</Button>
WPF Layout Containers
WPF offers several layout containers to organize UI elements:
Grid
The most versatile layout panel, allowing elements to be positioned in rows and columns:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">Name:</Label>
<TextBox Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="1" Grid.Column="0">Email:</Label>
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
StackPanel
Arranges elements in a single line (horizontal or vertical):
<StackPanel Orientation="Vertical">
<Button Content="Button 1" Margin="5" />
<Button Content="Button 2" Margin="5" />
<Button Content="Button 3" Margin="5" />
</StackPanel>
WrapPanel
Similar to StackPanel, but wraps elements to the next line when space runs out:
<WrapPanel>
<Button Content="Button 1" Width="100" Margin="5" />
<Button Content="Button 2" Width="100" Margin="5" />
<Button Content="Button 3" Width="100" Margin="5" />
<Button Content="Button 4" Width="100" Margin="5" />
</WrapPanel>
DockPanel
Docks elements to the edges of the container:
<DockPanel>
<Button DockPanel.Dock="Top" Content="Top" Height="50" />
<Button DockPanel.Dock="Left" Content="Left" Width="50" />
<Button DockPanel.Dock="Right" Content="Right" Width="50" />
<Button DockPanel.Dock="Bottom" Content="Bottom" Height="50" />
<TextBlock Text="Center Content" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DockPanel>
Canvas
Provides absolute positioning of elements:
<Canvas>
<Button Canvas.Left="10" Canvas.Top="10" Content="Button 1" />
<Button Canvas.Left="100" Canvas.Top="50" Content="Button 2" />
<Ellipse Canvas.Left="50" Canvas.Top="100" Width="100" Height="50" Fill="Blue" />
</Canvas>
Data Binding
One of the most powerful features of WPF is data binding, which connects UI elements with data sources.
Basic Data Binding
Let's create a simple data binding example:
<Window x:Class="BindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Data Binding Demo" Height="200" Width="400">
<StackPanel Margin="10">
<TextBox x:Name="inputTextBox" Margin="0,0,0,10" />
<TextBlock Text="{Binding ElementName=inputTextBox, Path=Text}"
FontWeight="Bold" />
<Slider x:Name="slider" Minimum="0" Maximum="100"
Value="50" Margin="0,20,0,0" />
<TextBlock Text="{Binding ElementName=slider, Path=Value}"
FontWeight="Bold" />
</StackPanel>
</Window>
In this example:
- The first
TextBlock
shows whatever you type in theTextBox
- The second
TextBlock
displays the current value of theSlider
Binding to Custom Objects
For more complex scenarios, you can bind to custom objects:
// Person.cs
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
<Window x:Class="BindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Data Binding Demo" Height="300" Width="400"
Loaded="Window_Loaded">
<StackPanel Margin="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">First Name:</Label>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" />
<Label Grid.Row="1" Grid.Column="0">Last Name:</Label>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" />
<Label Grid.Row="2" Grid.Column="0">Age:</Label>
<TextBox Grid.Row="2" Grid.Column="1"
Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" />
<Label Grid.Row="3" Grid.Column="0">Full Name:</Label>
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
</Grid>
</StackPanel>
</Window>
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
private Person _person;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_person = new Person
{
FirstName = "John",
LastName = "Doe",
Age = 30
};
this.DataContext = _person;
}
}
Real-World Application Example
Let's create a simple task management application to demonstrate WPF in a real-world context:
<Window x:Class="TaskManager.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Task Manager" Height="450" Width="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<StackPanel Grid.Row="0" Margin="0,0,0,10">
<TextBlock Text="Task Manager" FontSize="24" FontWeight="Bold"/>
<TextBlock Text="Manage your daily tasks efficiently" Opacity="0.6"/>
</StackPanel>
<!-- Task List -->
<ListView Grid.Row="1" x:Name="taskListView" Margin="0,0,0,10"
SelectionChanged="taskListView_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn Header="Done" Width="50">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsCompleted, Mode=TwoWay}"
Checked="CheckBox_Changed"
Unchecked="CheckBox_Changed"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Title" Width="200"
DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Priority" Width="100"
DisplayMemberBinding="{Binding Priority}"/>
<GridViewColumn Header="Due Date" Width="150"
DisplayMemberBinding="{Binding DueDate, StringFormat='{}{0:MM/dd/yyyy}'}"/>
</GridView>
</ListView.View>
</ListView>
<!-- Add Task Form -->
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" x:Name="newTaskTextBox"
Margin="0,0,5,0" PlaceholderText="Enter new task..." />
<ComboBox Grid.Column="1" x:Name="priorityComboBox"
Width="100" Margin="0,0,5,0">
<ComboBoxItem Content="Low"/>
<ComboBoxItem Content="Medium" IsSelected="True"/>
<ComboBoxItem Content="High"/>
</ComboBox>
<DatePicker Grid.Column="2" x:Name="dueDatePicker"
Width="120" Margin="0,0,5,0"/>
<Button Grid.Column="3" Content="Add Task"
Click="AddTask_Click" Width="80"/>
</Grid>
</Grid>
</Window>
// Task.cs
public class Task
{
public string Title { get; set; }
public string Priority { get; set; }
public DateTime DueDate { get; set; }
public bool IsCompleted { get; set; }
}
// MainWindow.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TaskManager
{
public partial class MainWindow : Window
{
private ObservableCollection<Task> _tasks;
public MainWindow()
{
InitializeComponent();
_tasks = new ObservableCollection<Task>();
// Add some example tasks
_tasks.Add(new Task { Title = "Learn WPF", Priority = "High", DueDate = DateTime.Now.AddDays(1) });
_tasks.Add(new Task { Title = "Complete project", Priority = "Medium", DueDate = DateTime.Now.AddDays(7) });
_tasks.Add(new Task { Title = "Read documentation", Priority = "Low", DueDate = DateTime.Now.AddDays(3) });
taskListView.ItemsSource = _tasks;
// Initialize date picker with today's date
dueDatePicker.SelectedDate = DateTime.Today;
}
private void AddTask_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(newTaskTextBox.Text))
{
MessageBox.Show("Please enter a task title.", "Validation Error", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
var newTask = new Task
{
Title = newTaskTextBox.Text,
Priority = ((ComboBoxItem)priorityComboBox.SelectedItem).Content.ToString(),
DueDate = dueDatePicker.SelectedDate ?? DateTime.Today,
IsCompleted = false
};
_tasks.Add(newTask);
newTaskTextBox.Clear();
}
private void CheckBox_Changed(object sender, RoutedEventArgs e)
{
// You can add logic here to handle task completion
}
private void taskListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// You can add logic here to handle task selection
}
}
}
Summary
In this introduction to WPF, we've explored:
- The basics of WPF and how it compares to Windows Forms
- Creating a simple WPF application
- Understanding XAML, the markup language for WPF
- Working with various layout containers
- Using data binding to connect UI to data
- Building a real-world task management application
WPF provides a powerful platform for creating modern Windows desktop applications with rich user interfaces. Its separation of UI and logic through XAML and code-behind, along with features like data binding and templates, makes it an excellent choice for desktop application development.
Additional Resources
Practice Exercises
-
Calculator Application: Create a simple calculator with buttons for digits and basic operations.
-
Image Viewer: Build an application that allows users to browse and view images from a folder.
-
Contact Manager: Develop a contact management application that stores contact information and allows searching and filtering.
-
Weather App: Create an application that fetches and displays weather data for a selected location.
-
Theme Switcher: Build a WPF application that allows users to switch between different visual themes (light/dark mode).
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)