一、WPF 简介
Windows Presentation Foundation(WPF)是微软推出的一款用于构建用户界面的框架,它为开发 Windows 桌面应用程序提供了统一的编程模型、语言和框架。WPF 将用户界面的设计与业务逻辑分离开来,采用了 XAML(可扩展应用程序标记语言)来描述界面元素,使得界面设计更加直观和灵活。与传统的 Windows Forms 相比,WPF 在图形渲染、动画效果、数据绑定等方面具有显著优势,能够创建出更加美观、交互性强的应用程序。
(一)WPF 的特点
- 声明式 UI 设计:通过 XAML,开发人员可以以声明的方式描述用户界面的结构和外观,无需编写大量的代码来创建和管理界面元素。例如:
<Window x:Class="WpfApp1.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">
<Grid>
<Button Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
这段 XAML 代码定义了一个包含一个按钮的窗口,按钮位于窗口的中心位置。
- 强大的图形渲染能力:WPF 基于 DirectX 技术,能够提供高质量的 2D 和 3D 图形渲染效果。它支持硬件加速,使得复杂的图形和动画能够流畅运行。通过使用 Path、Geometry 等元素,可以创建各种形状,并且可以对这些形状进行填充、描边等操作。例如:
<Path Stroke="Black" StrokeThickness="2">
<Path.Data>
<EllipseGeometry Center="100,100" RadiusX="50" RadiusY="50"/>
</Path.Data>
</Path>
上述代码创建了一个黑色边框、半径为 50 的圆形。
- 丰富的动画支持:WPF 提供了一套完整的动画系统,包括线性动画、关键帧动画、路径动画等。可以通过动画来改变元素的属性,如位置、大小、透明度等,从而创建出生动的用户界面。例如,下面的代码实现了一个按钮在点击时逐渐变大的动画:
<Window.Resources>
<Storyboard x:Key="GrowButtonAnimation">
<DoubleAnimation Storyboard.TargetProperty="Width"
From="100" To="150" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="Height"
From="50" To="75" Duration="0:0:0.5"/>
</Storyboard>
</Window.Resources>
<Button Content="Animate Me" Click="Button_Click">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource GrowButtonAnimation}"/>
</EventTrigger>
</Button.Triggers>
</Button>
在后台代码中,Button_Click
事件不需要额外的逻辑,因为动画已经通过 XAML 定义好了。
- 数据绑定:数据绑定是 WPF 的核心特性之一,它允许将 UI 元素与数据源进行关联,使得 UI 能够自动反映数据源的变化,并且数据源也能够根据 UI 的操作进行更新。数据绑定支持多种数据源,如对象、集合、XML 等。例如,将一个 TextBox 的 Text 属性绑定到一个 ViewModel 中的字符串属性:
<TextBox Text="{Binding MyProperty}"/>
在 ViewModel 中定义MyProperty
属性:
public class ViewModel : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged(nameof(MyProperty));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
这样,当MyProperty
的值在 ViewModel 中发生变化时,TextBox 中的文本会自动更新。
- 样式和模板:WPF 允许通过样式和模板来定义 UI 元素的外观和行为。样式可以集中设置一组属性,应用到多个元素上,实现统一的外观风格。模板则可以自定义元素的可视化结构,例如为 Button 创建自定义的模板,使其具有独特的外观。
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="14"/>
</Style>
<Button Style="{StaticResource MyButtonStyle}" Content="Styled Button"/>
通过定义MyButtonStyle
样式,并将其应用到 Button 上,按钮就具有了浅蓝色背景、白色前景色和 14 号字体的样式。
(二)WPF 的应用场景
- 桌面应用程序开发:无论是企业级的业务应用程序,还是面向消费者的工具软件,WPF 都能够提供良好的用户体验。例如,办公软件、图形设计工具、数据分析应用等。
- 多媒体应用:由于其强大的图形渲染和动画能力,WPF 适用于开发视频播放器、音频编辑软件、游戏界面等多媒体相关的应用。
- 数据可视化应用:在需要展示大量数据的场景中,如金融数据可视化、科学数据展示等,WPF 的数据绑定和丰富的图形元素能够方便地创建直观的数据可视化界面。
二、创建 WPF 项目
(一)安装开发环境
要开发 WPF 应用程序,首先需要安装 Visual Studio。可以从微软官方网站下载并安装最新版本的 Visual Studio,在安装过程中,确保选择了 “.NET 桌面开发” 工作负载,其中包含了开发 WPF 应用所需的工具和框架。
(二)创建项目
- 打开 Visual Studio,选择 “创建新项目”。
- 在项目模板列表中,选择 “WPF 应用 (.NET Framework)”(如果使用的是.NET Core 或.NET 5 及以上版本,可选择对应的 WPF 项目模板)。
- 输入项目名称和存储位置,点击 “创建” 按钮。
(三)项目结构
创建完成后,项目中会包含以下主要文件和文件夹:
- App.xaml:应用程序的入口点,包含应用程序的资源、启动设置等。在 App.xaml.cs 文件中,可以编写应用程序的启动逻辑。
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
}
- MainWindow.xaml:主窗口的 XAML 文件,用于定义主窗口的界面布局和元素。对应的 MainWindow.xaml.cs 文件包含窗口的代码逻辑,如事件处理程序等。
- Properties 文件夹:包含项目的属性设置,如应用程序图标、版本信息等。
- References 文件夹:列出了项目所引用的程序集,通过添加引用可以使用其他库中的功能。
三、XAML 基础
(一)XAML 语法
- 元素和属性:XAML 中的元素对应于.NET 类型,元素名称通常与类型名称相同。属性用于设置元素的特性,例如:
<Button Content="OK" Width="100" Height="30" Background="Green"/>
这里Button
是元素,Content
、Width
、Height
、Background
是属性。属性值可以是简单的文本、数值,也可以是更复杂的对象,如颜色对象(通过颜色名称或 RGB 值表示)。
- 嵌套元素:元素可以嵌套在其他元素内部,形成层次结构。例如:
<StackPanel>
<TextBlock Text="First Line"/>
<TextBlock Text="Second Line"/>
</StackPanel>
StackPanel
是一个容器元素,它包含了两个TextBlock
元素,StackPanel
会按照垂直方向依次排列这些子元素。
- 命名空间:XAML 使用命名空间来引用不同的类型。在每个 XAML 文件的根元素中,通常会定义默认命名空间和其他需要的命名空间。例如:
<Window x:Class="WpfApp1.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">
<!-- 窗口内容 -->
</Window>
默认命名空间http://schemas.microsoft.com/winfx/2006/xaml/presentation
包含了 WPF 的核心 UI 元素类型,如Window
、Button
、Grid
等。xmlns:x
引用的命名空间用于 XAML 语言本身的特性,如x:Class
用于指定代码隐藏文件中对应的类。
- XAML 语法扩展:XAML 提供了语法扩展机制,用于创建更复杂的属性值。常见的语法扩展有
Binding
、StaticResource
、DynamicResource
等。例如,Binding
扩展用于数据绑定:
<TextBox Text="{Binding MyProperty}"/>
StaticResource
扩展用于引用资源字典中的资源:
<Button Style="{StaticResource MyButtonStyle}"/>
(二)常用 XAML 元素
- 布局容器
- Grid:是最常用的布局容器之一,它将界面划分为行和列的网格,子元素可以通过
Grid.Row
和Grid.Column
附加属性指定在网格中的位置。例如:
- Grid:是最常用的布局容器之一,它将界面划分为行和列的网格,子元素可以通过
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Content="Top - Left" Grid.Row="0" Grid.Column="0"/>
<Button Content="Top - Right" Grid.Row="0" Grid.Column="1"/>
<Button Content="Bottom - Left" Grid.Row="1" Grid.Column="0"/>
<Button Content="Bottom - Right" Grid.Row="1" Grid.Column="1"/>
</Grid>
- StackPanel:按照水平或垂直方向排列子元素。通过
Orientation
属性可以设置排列方向,默认是垂直方向。例如:
<StackPanel Orientation="Horizontal">
<Button Content="Button 1"/>
<Button Content="Button 2"/>
<Button Content="Button 3"/>
</StackPanel>
- WrapPanel:子元素会按照指定的方向依次排列,当一行或一列排满时,会自动换行或换列。例如:
<WrapPanel>
<Button Content="Short Button"/>
<Button Content="A Longer Button"/>
<Button Content="Another Button"/>
</WrapPanel>
- DockPanel:子元素可以停靠在面板的边缘,通过
DockPanel.Dock
附加属性设置停靠位置。例如:
<DockPanel>
<Button Content="Top" DockPanel.Dock="Top"/>
<Button Content="Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Left" DockPanel.Dock="Left"/>
<Button Content="Right" DockPanel.Dock="Right"/>
<Button Content="Center"/>
</DockPanel>
- 控件元素
- Button:按钮控件,用于触发操作。可以通过
Content
属性设置按钮显示的文本或其他内容(如图片),通过Click
事件处理按钮的点击操作。
- Button:按钮控件,用于触发操作。可以通过
<Button Content="Submit" Click="Button_Click"/>
在后台代码中:
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button clicked!");
}
- TextBox:用于输入文本。可以通过
Text
属性获取或设置文本内容,通过TextChanged
事件响应文本的变化。
<TextBox TextChanged="TextBox_TextChanged"/>
后台代码:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
string text = textBox.Text;
// 处理文本变化逻辑
}
- ComboBox:下拉列表框,用户可以从预定义的选项中选择一个值。通过
ItemsSource
属性绑定数据源,通过SelectedItem
或SelectedValue
属性获取当前选中的项。
<ComboBox ItemsSource="{Binding MyItems}" SelectedValue="{Binding SelectedItem}"/>
假设在 ViewModel 中定义了MyItems
集合和SelectedItem
属性:
public class ViewModel
{
public ObservableCollection<string> MyItems { get; set; }
public string SelectedItem { get; set; }
public ViewModel()
{
MyItems = new ObservableCollection<string>() { "Option 1", "Option 2", "Option 3" };
}
}
- CheckBox:复选框控件,用于表示布尔值的选择状态。通过
IsChecked
属性获取或设置复选框的选中状态,通过Checked
和Unchecked
事件响应状态变化。
<CheckBox Content="Remember Me" IsChecked="{Binding IsRememberMe}"/>
在 ViewModel 中定义IsRememberMe
属性:
private bool _isRememberMe;
public bool IsRememberMe
{
get { return _isRememberMe; }
set
{
_isRememberMe = value;
OnPropertyChanged(nameof(IsRememberMe));
}
}
- RadioButton:单选按钮,一组单选按钮中只能有一个被选中。通过
GroupName
属性将多个单选按钮分组,通过IsChecked
属性判断是否被选中。
<StackPanel>
<RadioButton Content="Male" GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=Male}"/>
<RadioButton Content="Female" GroupName="Gender" IsChecked="{Binding Gender, Converter={StaticResource GenderConverter}, ConverterParameter=Female}"/>
</StackPanel>
这里使用了一个转换器GenderConverter
来将 ViewModel 中的Gender
属性值与单选按钮的选中状态进行转换。