一、WPF 入门与环境搭建
1.1 WPF 简介
Windows Presentation Foundation(WPF)是微软开发的一个 UI 框架,用于创建具有丰富视觉效果和交互性的 Windows 桌面应用程序。它引入了 XAML(可扩展应用程序标记语言),将界面设计与代码逻辑分离,提升了开发效率和可维护性。WPF 支持矢量图形、动画、多媒体等多种技术,能够打造出专业且美观的用户界面。
1.2 开发环境搭建
1.2.1 安装 Visual Studio
访问微软官方网站,下载并安装最新版本的 Visual Studio。在安装过程中,选择包含 “.NET 桌面开发” 的工作负载,其中涵盖了开发 WPF 应用所需的工具和框架。
1.2.2 创建 WPF 项目
打开 Visual Studio,选择 “创建新项目”。在搜索框中输入 “WPF 应用”,根据需求选择合适的项目模板,如 “WPF 应用(.NET 7.0)”。输入项目名称和保存位置后,点击 “创建” 按钮,Visual Studio 会自动生成一个基本的 WPF 项目结构。
1.3 项目结构概述
一个典型的 WPF 项目包含以下几个主要部分:
- App.xaml 和 App.xaml.cs:
App.xaml
是应用程序的全局资源和启动设置文件,App.xaml.cs
是其对应的代码隐藏文件,负责应用程序的启动逻辑。 - MainWindow.xaml 和 MainWindow.xaml.cs:
MainWindow.xaml
定义了主窗口的界面布局,MainWindow.xaml.cs
包含了与主窗口交互的代码逻辑。 - Properties 文件夹:包含项目的属性设置文件,如
AssemblyInfo.cs
用于设置程序集的元数据。 - Resources 文件夹:可用于存放应用程序所需的资源文件,如图像、音频等。
二、XAML 深入解析
2.1 XAML 基础语法
2.1.1 元素和属性
XAML 使用 XML 标签来定义 UI 元素,每个元素可以有多个属性用于设置其外观和行为。例如:
<Button Content="Click Me" Width="100" Height="30" />
这里的 <Button>
是元素,Content
、Width
和 Height
是属性。
2.1.2 嵌套元素
元素可以嵌套使用,以构建复杂的界面布局。例如:
<StackPanel><TextBlock Text="Welcome to WPF!" FontSize="24" /><Button Content="Explore" Width="120" Height="40" />
</StackPanel>
<StackPanel>
是一个容器元素,包含了一个 <TextBlock>
和一个 <Button>
。
2.1.3 命名空间
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="Main Window" Height="450" Width="800"><!-- 窗口内容 -->
</Window>
默认命名空间 http://schemas.microsoft.com/winfx/2006/xaml/presentation
包含了 WPF 的核心 UI 元素类型,xmlns:x
引用的命名空间用于 XAML 语言本身的特性,如 x:Class
用于指定代码隐藏文件中对应的类。
2.2 XAML 中的数据绑定
2.2.1 基本绑定
数据绑定是 WPF 的核心特性之一,它允许将 UI 元素的属性与数据源的属性进行关联。例如,将一个 TextBlock
的 Text
属性绑定到一个 ViewModel
中的属性:
<TextBlock Text="{Binding UserName}" />
在代码隐藏文件或 ViewModel
中设置 DataContext
:
public partial class MainWindow : Window
{public MainWindow(){InitializeComponent();DataContext = new { UserName = "John Doe" };}
}
2.2.2 绑定模式
WPF 支持多种绑定模式,如 OneWay
、TwoWay
、OneTime
等。
OneWay
:数据从数据源流向 UI 元素,当数据源属性值发生变化时,UI 元素会自动更新。
<TextBlock Text="{Binding UserName, Mode=OneWay}" />
TwoWay
:数据在数据源和 UI 元素之间双向流动,当 UI 元素的属性值发生变化时,数据源的属性也会相应更新。常用于用户输入的场景,如TextBox
。
<TextBox Text="{Binding UserName, Mode=TwoWay}" />
OneTime
:数据在初始化时从数据源流向 UI 元素,之后数据源的变化不会影响 UI 元素。
<TextBlock Text="{Binding AppVersion, Mode=OneTime}" />
2.2.3 绑定转换器
当数据源的属性类型与 UI 元素的属性类型不匹配时,或者需要对数据进行格式化处理时,可以使用绑定转换器。例如,将一个布尔值转换为字符串显示:
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 null;}
}
在 XAML 中使用转换器:
<Window.Resources><local:BooleanToStringConverter x:Key="BooleanToStringConverter" />
</Window.Resources>
<TextBlock Text="{Binding IsEnabled, Converter={StaticResource BooleanToStringConverter}}" />
2.3 XAML 中的样式和模板
2.3.1 样式定义
样式可以集中设置 UI 元素的属性,提高代码的复用性。例如,定义一个按钮样式:
<Window.Resources><Style x:Key="MyButtonStyle" TargetType="Button"><Setter Property="Background" Value="LightBlue" /><Setter Property="Foreground" Value="White" /><Setter Property="FontSize" Value="16" /></Style>
</Window.Resources>
<Button Style="{StaticResource MyButtonStyle}" Content="Styled Button" />
2.3.2 模板定制
模板可以完全改变 UI 元素的外观和行为。例如,自定义一个按钮的模板:
<Window.Resources><ControlTemplate x:Key="CustomButtonTemplate" TargetType="Button"><Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"><ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter TargetName="border" Property="Background" Value="LightGray" /></Trigger><Trigger Property="IsPressed" Value="True"><Setter TargetName="border" Property="Background" Value="DarkGray" /></Trigger></ControlTemplate.Triggers></ControlTemplate>
</Window.Resources>
<Button Template="{StaticResource CustomButtonTemplate}" Content="Custom Button" />
三、WPF 控件与布局
3.1 常用控件介绍
3.1.1 文本控件
- TextBlock:用于显示静态文本,支持丰富的文本格式设置,如字体、颜色、字号等。
<TextBlock Text="This is a TextBlock" FontSize="18" Foreground="Blue" />
- TextBox:用于用户输入文本,可设置多行输入、密码模式等。
<TextBox Text="Enter text here" Width="200" Height="30" />
3.1.2 按钮控件
- Button:最常用的交互控件,用于触发操作。可通过
Click
事件处理点击操作。
<Button Content="Submit" Click="Button_Click" />
private void Button_Click(object sender, RoutedEventArgs e)
{MessageBox.Show("Button clicked!");
}
3.1.3 列表控件
- ListView:用于显示数据列表,可自定义列表项的显示方式。
<ListView ItemsSource="{Binding Items}"><ListView.ItemTemplate><DataTemplate><TextBlock Text="{Binding ItemName}" /></DataTemplate></ListView.ItemTemplate>
</ListView>
- ComboBox:下拉列表框,用户可以从预定义的选项中选择一个值。
<ComboBox ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}" />
3.1.4 容器控件
- 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>
3.2 布局管理
3.2.1 布局容器的嵌套使用
可以将不同的布局容器嵌套使用,以实现复杂的界面布局。例如,在 Grid
中嵌套 StackPanel
:
<Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="*" /></Grid.RowDefinitions><StackPanel Orientation="Horizontal" Grid.Row="0"><Button Content="Home" /><Button Content="About" /><Button Content="Contact" /></StackPanel><ListView Grid.Row="1" ItemsSource="{Binding Items}" />
</Grid>
3.2.2 自适应布局
WPF 支持自适应布局,可根据窗口大小的变化自动调整界面元素的大小和位置。例如,使用 *
作为 RowDefinition
或 ColumnDefinition
的高度或宽度值,可实现按比例分配空间:
<Grid><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="2*" /></Grid.ColumnDefinitions><Button Content="Left Button" Grid.Column="0" /><Button Content="Right Button" Grid.Column="1" />
</Grid>
四、MVVM 模式在 WPF 中的应用
4.1 MVVM 模式概述
MVVM(Model - View - ViewModel)是一种设计模式,用于分离 UI 设计和业务逻辑,提高代码的可维护性和可测试性。
- Model:表示应用程序的数据和业务逻辑,如数据库实体、业务规则等。
- View:负责界面的呈现,即 XAML 文件定义的 UI 元素。
- ViewModel:作为 View 和 Model 之间的桥梁,负责处理视图的交互逻辑和数据转换,实现了视图和模型的解耦。
4.2 实现 MVVM 模式
4.2.1 创建 Model 类
假设我们开发一个简单的联系人管理应用,创建一个 Contact
类作为 Model:
public class Contact
{public string Name { get; set; }public string Email { get; set; }public string Phone { get; set; }
}
4.2.2 创建 ViewModel 类
ViewModel 类需要实现 INotifyPropertyChanged
接口,以便在属性值发生变化时通知视图更新。
public class ContactViewModel : INotifyPropertyChanged
{private ObservableCollection<Contact> _contacts;public ObservableCollection<Contact> Contacts{get { return _contacts; }set{_contacts = value;OnPropertyChanged(nameof(Contacts));}}private Contact _selectedContact;public Contact SelectedContact{get { return _selectedContact; }set{_selectedContact = value;OnPropertyChanged(nameof(SelectedContact));}}public ContactViewModel(){Contacts = new ObservableCollection<Contact>{new Contact { Name = "John Doe", Email = "john@example.com", Phone = "123 - 456 - 7890" },new Contact { Name = "Jane Smith", Email = "jane@example.com", Phone = "098 - 765 - 4321" }};}public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}
}
4.2.3 在 View 中使用 ViewModel
在 XAML 文件中设置 DataContext
为 ViewModel 的实例,并进行数据绑定:
<Window x:Class="WpfApp.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Contact Manager" Height="450" Width="800"><Window.DataContext><local:ContactViewModel /></Window.DataContext><Grid><ListView ItemsSource="{Binding Contacts}" SelectedItem="{Binding SelectedContact}" /><TextBlock Text="{Binding SelectedContact.Name}" Margin="10" /><TextBlock Text="{Binding SelectedContact.Email}" Margin="10" /><TextBlock Text="{Binding SelectedContact.Phone}" Margin="10" /></Grid>
</Window>
4.3 命令模式在 MVVM 中的应用
4.3.1 实现 RelayCommand 类
在 MVVM 模式中,通常使用命令模式来处理视图的交互操作。实现一个 RelayCommand
类:
public class RelayCommand : ICommand
{private readonly Action<object> _execute;private readonly Predicate<object> _canExecute;public RelayCommand(Action<object> execute, Predicate<object> canExecute = null){_execute = execute;_canExecute = canExecute;}public bool CanExecute(object parameter){return _canExecute == null || _canExecute(parameter);}public event EventHandler CanExecuteChanged{add { CommandManager.RequerySuggested += value; }remove { CommandManager.RequerySuggested -= value; }}public void Execute(object parameter){_execute(parameter);}
}
4.3.2 在 ViewModel 中使用命令
在 ContactViewModel
中添加一个命令来处理删除联系人的操作:
public class ContactViewModel : INotifyPropertyChanged
{// 其他属性和构造函数...public ICommand DeleteContactCommand { get; set; }public ContactViewModel(){// 初始化联系人集合...DeleteContactCommand = new RelayCommand(DeleteContact, CanDeleteContact);}private void DeleteContact(object parameter){if (SelectedContact != null){Contacts.Remove(SelectedContact);}}private bool CanDeleteContact(object parameter){return SelectedContact != null;}// INotifyPropertyChanged 实现...
}
4.3.3 在 View 中绑定命令
在 XAML 中为删除按钮绑定 DeleteContactCommand
:
<Window x:Class="WpfApp.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Contact Manager" Height="450" Width="800"><Window.DataContext><local:ContactViewModel /></Window.DataContext><Grid><ListView ItemsSource="{Binding Contacts}" SelectedItem="{Binding SelectedContact}" /><Button Content="Delete Contact" Command="{Binding DeleteContactCommand}" /><!-- 其他显示联系人信息的 TextBlock --></Grid>
</Window>
五、WPF 动画与多媒体
5.1 动画基础
5.1.1 线性动画
线性动画是最简单的动画类型,它在指定的时间内从一个值线性变化到另一个值。例如,实现一个按钮在点击时逐渐变大的动画:
<Window.Resources><Storyboard x:Key="GrowButtonAnimation"><DoubleAnimation Storyboard.TargetProperty="Width"From="100" To="150" Duration="0:0:1" /><DoubleAnimation Storyboard.TargetProperty="Height"From="50" To="75" Duration="0:0:1" /></Storyboard>
</Window.Resources>
<Button Content="Animate Me" Click="Button_Click"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource GrowButtonAnimation}" /></EventTrigger></Button.Triggers>
</Button>
private void Button_Click(object sender, RoutedEventArgs e)
{// 动画会自动触发
}
5.1.2 关键帧动画
关键帧动画允许在动画过程中定义多个关键帧,每个关键帧指定一个特定的时间点和属性值。例如,实现一个按钮在不同时间点改变颜色的动画:
<Window.Resources><Storyboard x:Key="ColorAnimationStoryboard"><ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Duration="0:0:3"><LinearColorKeyFrame Value="Red" KeyTime="0:0:0" /><LinearColorKeyFrame Value="Green" KeyTime="0:0:1" /><LinearColorKeyFrame Value="Blue" KeyTime="0:0:2" /><LinearColorKeyFrame Value="Yellow" KeyTime="0:0:3" /></ColorAnimationUsingKeyFrames></Storyboard>
</Window.Resources>
<Button Content="Color Animate" Click="Button_Click_1"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource ColorAnimationStoryboard}" /></EventTrigger></Button.Triggers>
</Button>
5.1.3 路径动画
路径动画允许元素沿着指定的路径移动。例如,让一个椭圆沿着一个圆形路径移动:
<Window.Resources><Storyboard x:Key="PathAnimationStoryboard"><PointAnimationUsingPath Storyboard.TargetProperty="(Canvas.LeftProperty)"Storyboard.TargetName="ellipse"Duration="0:0:5"RepeatBehavior="Forever"><PointAnimationUsingPath.PathGeometry><EllipseGeometry Center="200,200" RadiusX="100" RadiusY="100" /></PointAnimationUsingPath.PathGeometry></PointAnimationUsingPath><PointAnimationUsingPath Storyboard.TargetProperty="(Canvas.TopProperty)"Storyboard.TargetName="ellipse"Duration="0:0:5"RepeatBehavior="Forever"><PointAnimationUsingPath.PathGeometry><EllipseGeometry Center="200,200" RadiusX="100" RadiusY="100" /></PointAnimationUsingPath.PathGeometry></PointAnimationUsingPath></Storyboard>
</Window.Resources>
<Canvas><Ellipse x:Name="ellipse" Width="20" Height="20" Fill="Red" Canvas.Left="100" Canvas.Top="100" /><Button Content="Start Animation" Canvas.Left="10" Canvas.Top="10" Click="Button_Click_2"><Button.Triggers><EventTrigger RoutedEvent="Button.Click"><BeginStoryboard Storyboard="{StaticResource PathAnimationStoryboard}" /></EventTrigger></Button.Triggers></Button>
</Canvas>
5.2 多媒体支持
5.2.1 播放视频
使用 MediaElement
控件可以播放视频文件。例如:
<MediaElement Source="video.mp4" Width="640" Height="360"LoadedBehavior="Play" UnloadedBehavior="Stop" />
确保 video.mp4
文件存在于应用程序的运行目录中。
5.2.2 播放音频
同样使用 MediaElement
控件播放音频文件。可以将控件的宽度和高度设置为 0,使其在界面上不可见。
<MediaElement Source="audio.mp3" Width="0" Height="0"LoadedBehavior="Play" UnloadedBehavior="Stop" />