一、WPF 概述

1.1 什么是 WPF
Windows Presentation Foundation(WPF)是微软为开发 Windows 桌面应用程序而推出的一个 UI 框架。它引入了许多先进的技术和概念,如基于矢量图形的渲染、数据绑定、样式和模板等,使得开发者能够创建出具有丰富视觉效果和交互性的应用程序。与传统的 Windows Forms 相比,WPF 更加注重用户体验和界面设计的灵活性。
1.2 WPF 的优势
- 声明式编程:通过 XAML(可扩展应用程序标记语言),开发者可以以声明的方式定义界面,将界面设计与业务逻辑分离,提高开发效率和可维护性。
- 强大的图形渲染:基于 DirectX 技术,WPF 能够实现高质量的 2D 和 3D 图形渲染,支持硬件加速,确保复杂图形和动画的流畅显示。
- 数据绑定:提供了强大的数据绑定机制,使得 UI 元素能够自动反映数据源的变化,并且可以双向绑定,方便数据的交互和更新。
- 样式和模板:允许开发者定义统一的样式和模板,实现界面的一致性和复用性,同时可以轻松地定制控件的外观和行为。
- 动画支持:内置了丰富的动画系统,包括线性动画、关键帧动画等,能够创建出生动的用户界面。
1.3 WPF 的应用场景
- 企业级应用:如办公软件、数据分析工具等,需要提供复杂的界面和交互功能,WPF 可以满足这些需求。
- 多媒体应用:视频播放器、音频编辑软件等,利用 WPF 的图形和多媒体处理能力,实现高质量的媒体播放和编辑功能。
- 游戏开发:虽然不是主流的游戏开发平台,但 WPF 可以用于开发一些小型的 2D 游戏,尤其是需要精美的界面和动画效果的游戏。
二、创建 WPF 项目
2.1 安装开发环境
要开始开发 WPF 应用程序,需要安装 Visual Studio。可以从微软官方网站下载并安装最新版本的 Visual Studio,在安装过程中选择 ".NET 桌面开发" 工作负载,其中包含了开发 WPF 应用所需的工具和框架。
2.2 创建新的 WPF 项目
- 打开 Visual Studio,选择 "创建新项目"。
- 在搜索框中输入 "WPF 应用",选择适合的项目模板,如 "WPF 应用(.NET Framework)" 或 "WPF 应用(.NET Core)",根据自己的需求选择。
- 输入项目名称和存储位置,然后点击 "创建" 按钮。
2.3 项目结构
创建好的 WPF 项目包含以下主要文件和文件夹:
- App.xaml 和 App.xaml.cs:App.xaml 是应用程序的入口文件,定义了应用程序的资源和启动设置;App.xaml.cs 是对应的代码文件,包含应用程序的启动逻辑。
- MainWindow.xaml 和 MainWindow.xaml.cs:MainWindow.xaml 是主窗口的 XAML 文件,用于定义窗口的界面布局和元素;MainWindow.xaml.cs 是对应的代码文件,包含窗口的事件处理和业务逻辑。
- Properties 文件夹:包含项目的属性设置,如应用程序图标、版本信息等。
- References 文件夹:列出了项目所引用的程序集,开发者可以根据需要添加或删除引用。
三、XAML 基础
3.1 XAML 简介
XAML 是一种基于 XML 的标记语言,用于描述 WPF 应用程序的用户界面。它通过标签和属性的方式来定义界面元素的结构和外观,使得界面设计更加直观和易于理解。
3.2 XAML 语法
- 元素和属性:XAML 中的元素对应于.NET 类型,元素名称通常与类型名称相同。属性用于设置元素的特性,例如:
XML
<Button Content="Click Me" Width="100" Height="30" />
这里的 <Button>
是元素,Content
、Width
和 Height
是属性。
- 嵌套元素:元素可以嵌套在其他元素内部,形成层次结构。例如:
XML
<StackPanel>
<TextBlock Text="Hello, World!" />
<Button Content="OK" />
</StackPanel>
<StackPanel>
是一个容器元素,包含了一个 <TextBlock>
和一个 <Button>
。
- 命名空间:XAML 使用命名空间来引用不同的类型。在每个 XAML 文件的根元素中,通常会定义默认命名空间和其他需要的命名空间。例如:
XML
<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 元素类型,xmlns:x
引用的命名空间用于 XAML 语言本身的特性,如 x:Class
用于指定代码隐藏文件中对应的类。
3.3 常用 XAML 元素
- 布局容器
- Grid :将界面划分为行和列的网格,子元素可以通过
Grid.Row
和Grid.Column
附加属性指定在网格中的位置。例如:
- Grid :将界面划分为行和列的网格,子元素可以通过
XML
<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
属性可以设置排列方向,默认是垂直方向。例如:
XML
<StackPanel Orientation="Horizontal">
<Button Content="Button 1" />
<Button Content="Button 2" />
<Button Content="Button 3" />
</StackPanel>
- WrapPanel:子元素会按照指定的方向依次排列,当一行或一列排满时,会自动换行或换列。例如:
XML
<WrapPanel>
<Button Content="Short Button" />
<Button Content="A Longer Button" />
<Button Content="Another Button" />
</WrapPanel>
- DockPanel :子元素可以停靠在面板的边缘,通过
DockPanel.Dock
附加属性设置停靠位置。例如:
XML
<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 :用于触发操作,通过
XML
<Button Content="Submit" Click="Button_Click" />
在后台代码中:
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button clicked!");
}
- TextBox :用于输入文本,通过
Text
属性获取或设置文本内容,通过TextChanged
事件响应文本的变化。
XML
<TextBox TextChanged="TextBox_TextChanged" />
后台代码:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
string text = textBox.Text;
// 处理文本变化逻辑
}
- ComboBox :下拉列表框,用户可以从预定义的选项中选择一个值。通过
ItemsSource
属性绑定数据源,通过SelectedItem
或SelectedValue
属性获取当前选中的项。
XML
<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
事件响应状态变化。
XML
<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
属性判断是否被选中。
XML
<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
属性值与单选按钮的选中状态进行转换。
四、数据绑定
4.1 数据绑定的概念
数据绑定是 WPF 的核心特性之一,它建立了 UI 元素(绑定目标)和数据源(绑定源)之间的连接,使得 UI 元素能够自动反映数据源的变化,并且可以将用户在 UI 上的操作反馈到数据源。
4.2 绑定模式
- OneWay:数据从绑定源流向绑定目标,当绑定源属性值发生变化时,绑定目标属性会自动更新,但绑定目标的变化不会影响绑定源。常用于显示只读数据的场景,如显示数据库中的记录。
XML
<TextBlock Text="{Binding ReadOnlyProperty, Mode=OneWay}" />
- TwoWay:数据在绑定源和绑定目标之间双向流动,当绑定源属性值变化时,绑定目标更新;当绑定目标属性值变化时,绑定源也会相应更新。常用于需要用户输入并更新数据的场景,如编辑表单。
XML
<TextBox Text="{Binding EditableProperty, Mode=TwoWay}" />
- OneTime:数据在初始化时从绑定源流向绑定目标,之后绑定源的变化不会再影响绑定目标。适用于数据在应用程序运行过程中不会改变的情况,如显示应用程序的版本号。
XML
<TextBlock Text="{Binding AppVersion, Mode=OneTime}" />
4.3 实现数据绑定
- 创建数据源 :通常会创建一个 ViewModel 类来作为数据源。ViewModel 类应该实现
INotifyPropertyChanged
接口,以便在属性值发生变化时通知绑定目标更新。例如:
cs
public class UserViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
- 设置 DataContext :在窗口或控件的代码隐藏文件中,将 ViewModel 的实例设置为
DataContext
,这样 XAML 中的绑定就可以找到对应的数据源。
cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UserViewModel viewModel = new UserViewModel();
DataContext = viewModel;
}
}
- 在 XAML 中进行绑定 :使用
Binding
标记扩展将 UI 元素的属性绑定到 ViewModel 的属性上。
XML
<TextBox Text="{Binding Name}" />
4.4 绑定到集合
在实际应用中,经常需要将 UI 元素绑定到集合数据上,例如将 ListView
或 ComboBox
绑定到一个 ObservableCollection
。ObservableCollection
是一个动态集合,当集合中的元素发生添加、删除或修改操作时,会自动通知绑定的 UI 元素进行更新。
以下是一个将 ListView
绑定到 ObservableCollection
的示例:
cs
public class EmployeeViewModel
{
public ObservableCollection<Employee> Employees { get; set; }
public EmployeeViewModel()
{
Employees = new ObservableCollection<Employee>
{
new Employee { Name = "John Doe", Age = 30 },
new Employee { Name = "Jane Smith", Age = 25 },
new Employee { Name = "Bob Johnson", Age = 35 }
};
}
}
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
}
在 XAML 中:
XML
<Window.Resources>
<DataTemplate x:Key="EmployeeTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="5" />
<TextBlock Text="{Binding Age}" Margin="5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListView ItemsSource="{Binding Employees}" ItemTemplate="{StaticResource EmployeeTemplate}" />
在窗口的代码隐藏文件中设置 DataContext
:
cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
EmployeeViewModel viewModel = new EmployeeViewModel();
DataContext = viewModel;
}
}
4.5 数据绑定的其他特性
- 绑定转换器 :当绑定源和绑定目标的类型不匹配时,或者需要对绑定值进行一些转换时,可以使用绑定转换器。绑定转换器是实现了
IValueConverter
接口的类。例如,将一个布尔值转换为字符串显示:
cs
public class BooleanToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? "Yes" : "No";
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue)
{
return stringValue == "Yes";
}
return false;
}
}
在 XAML 中使用转换器:
XML
<Window.Resources>
<local:BooleanToStringConverter x:Key="BooleanToStringConverter" />
</Window.Resources>
<TextBlock Text="{Binding IsActive, Converter={StaticResource BooleanToStringConverter}}" />
- 绑定验证 :在用户输入数据时,需要对输入的数据进行验证,确保数据的有效性。WPF 提供了绑定验证机制,可以通过实现
IDataErrorInfo
接口或使用ValidationRule
类来实现验证。例如,验证一个文本框输入的是否为有效的整数:
cs
public class IntegerValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (int.TryParse(value as string, out _))
{
return ValidationResult.ValidResult;
}
return new ValidationResult(false, "请输入有效的整数。");
}
}
在 XAML 中应用验证规则:
XML
<Window.Resources>
<local:IntegerValidationRule x:Key="IntegerValidationRule" />
</Window.Resources>
<TextBox>
<TextBox.Text>
<Binding Path="Age" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:IntegerValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
当用户输入的不是有效的整数时,文本框会显示验证错误信息。