WPF 入门教程(四)
十二.进阶属性
1.依赖属性
扩展传统的.NET属性系统,为WPF的数据绑定、样式、动画等高级功能提供了基础支持。 属性快捷键
// prop 属性
public int MyProperty { get; set; }
// propi 只能初始化属性(只能在构造函数和初始属性上初始化)
public int MyProperty1 { get; init; }
// propg 只能在当前类中设置属性
public int MyProperty2 { get; private set; }
// propfull 完整属性
private int myVar;
public int Myvar
{
get { return myVar; }
set { myVar = value; }
}
// propdp 依赖属性
// propa 附加属性
1.1 自定义文本框
1.1.1 创建MyTextBox
public class MyTextBox:TextBox
{
private string _initPlaceHolder = string.Empty;
public string PlaceHolder
{
get { return (string)GetValue(PlaceHolderProperty); }
set { SetValue(PlaceHolderProperty, value); }
}
public static readonly DependencyProperty PlaceHolderProperty =
DependencyProperty.Register(nameof(PlaceHolder), typeof(string), typeof(MyTextBox),
new PropertyMetadata(default(string)));
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
_initPlaceHolder = PlaceHolder;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
PlaceHolder=string.IsNullOrWhiteSpace(Text) ? _initPlaceHolder : string.Empty;
}
}
1.1.2 新建样式
<Style x:Key="myTextBox" TargetType="controls:MyTextBox">
<Setter Property="PlaceHolder" Value="请输入..."/>
<Setter Property="Width" Value="180"/>
<Setter Property="Height" Value="30"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:MyTextBox">
<Border BorderBrush="{TemplateBinding BorderBrush}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost"/>
<Border.Background>
<VisualBrush Stretch="None" Opacity="0.5" AlignmentX="Left" AlignmentY="Center">
<VisualBrush.Visual>
<TextBlock Text="{TemplateBinding PlaceHolder}"/>
</VisualBrush.Visual>
</VisualBrush>
</Border.Background>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
1.1.3 使用
<controls:MyTextBox PlaceHolder="请输入内容..." Style="{StaticResource myTextBox}"/>

2.附加属性
附加属性是一种特殊类型的依赖属性,可以被"附加"到任何对象上,即使该对象本身没有定义这个属性。 经典场景
<TextBlock Grid.Row="1"/>
2.1 WatermarkHelper
public class WatermarkHelper
{
// 定义附加属性
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.RegisterAttached(
"Watermark",
typeof(string),
typeof(WatermarkHelper),
new PropertyMetadata(string.Empty, OnWatermarkChanged));
// Get方法
public static string GetWatermark(DependencyObject obj)
{
return (string)obj.GetValue(WatermarkProperty);
}
// Set方法
public static void SetWatermark(DependencyObject obj, string value)
{
obj.SetValue(WatermarkProperty, value);
}
// 属性变更回调
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBox textBox)
{
// 当水印文本变化时更新显示
if (string.IsNullOrEmpty(textBox.Text))
{
textBox.Text = e.NewValue as string;
textBox.Foreground = Brushes.Gray;
}
textBox.GotFocus += (s, args) =>
{
if (textBox.Text == GetWatermark(textBox))
{
textBox.Text = string.Empty;
textBox.Foreground = Brushes.Black;
}
};
textBox.LostFocus += (s, args) =>
{
if (string.IsNullOrEmpty(textBox.Text))
{
textBox.Text = GetWatermark(textBox);
textBox.Foreground = Brushes.Gray;
}
};
}
}
}
<StackPanel>
<TextBox helpers:WatermarkHelper.Watermark="请输入内容..." Width="200" Height="50"/>
<TextBox helpers:WatermarkHelper.Watermark="请输入内容2..." Width="200" Height="50"/>
</StackPanel>
3.路由事件
路由事件(Routed Event)是WPF中一个核心概念,它允许事件在可视化树中"路由"传递,为事件处理提供了更灵活的方式。
3.1 基本概念
路由方向:
- 冒泡(Bubbling):从事件源向上传递到根元素
- 隧道(Tunneling):从根元素向下传递到事件源
- 直接(Direct):仅在事件源处理,不路由
事件处理机制:
- 允许在父元素上处理子元素的事件
- 支持事件处理器共享
- 提供更灵活的事件处理方式
3.2 路由类型
冒泡事件
// 示例:Button的Click事件是冒泡事件
<StackPanel Button.Click="StackPanel_Click">
<Button Content="点击我" />
</StackPanel>
隧道事件
// 示例:PreviewMouseDown是隧道事件
<StackPanel PreviewMouseDown="StackPanel_PreviewMouseDown">
<Button Content="点击我" />
</StackPanel>
直接事件
// 示例:大多数标准事件都是直接事件
<Button Click="Button_Click" />
3.3 自定义路由事件
public class MyButton : Button
{
// 1. 注册路由事件
public static readonly RoutedEvent TapEvent =
EventManager.RegisterRoutedEvent(
"Tap", // 事件名称
RoutingStrategy.Bubble, // 路由策略
typeof(RoutedEventHandler), // 事件处理程序类型
typeof(MyButton)); // 拥有者类型
// 2. 提供CLR事件包装器
public event RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
// 3. 触发事件的方法
protected virtual void OnTap()
{
RoutedEventArgs args = new RoutedEventArgs(TapEvent, this);
RaiseEvent(args);
}
}
十三.转换
WPF转换(Transform)是用于改变UI元素位置、大小、旋转和倾斜等视觉效果的机制。所有继承自UIElement的控件都支持转换。 转换类型:
- TranslateTransform - 平移转换
- RotateTransform - 旋转转换
- ScaleTransform - 缩放转换
- SkewTransform - 倾斜转换
- MatrixTransform - 矩阵转换
- TransformGroup - 组合转换
1.TranslateTransform (平移)
<Button Content="平移按钮">
<Button.RenderTransform>
<TranslateTransform X="50" Y="30"/>
</Button.RenderTransform>
</Button>
2.RotateTransform (旋转)
<Button Content="旋转按钮">
<Button.RenderTransform>
<RotateTransform Angle="45" CenterX="50" CenterY="50"/>
</Button.RenderTransform>
</Button>
- Angle: 旋转角度(0-360)
- CenterX/CenterY: 旋转中心点坐标
3.ScaleTransform (缩放)
<Button Content="缩放按钮">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.5" ScaleY="0.8"/>
</Button.RenderTransform>
</Button>
- ScaleX: 水平缩放比例(1.0为原始大小)
- ScaleY: 垂直缩放比例
- CenterX/CenterY: 缩放中心点(可选)
4.SkewTransform (倾斜)
<Button Content="倾斜按钮">
<Button.RenderTransform>
<SkewTransform AngleX="20" AngleY="10"/>
</Button.RenderTransform>
</Button>
5.TransformGroup (组合转换)
<Button Content="组合转换">
<Button.RenderTransform>
<TransformGroup>
<RotateTransform Angle="30"/>
<ScaleTransform ScaleX="1.2" ScaleY="1.2"/>
<TranslateTransform X="20" Y="10"/>
</TransformGroup>
</Button.RenderTransform>
</Button>
6.布局转换 vs 渲染转换
- LayoutTransform: 在布局阶段应用,影响布局计算
- RenderTransform: 在渲染阶段应用,不影响布局
十四.画刷
WPF提供了多种画刷类型:
- SolidColorBrush - 纯色画刷
- LinearGradientBrush - 线性渐变画刷
- RadialGradientBrush - 径向渐变画刷
- ImageBrush - 图像画刷
- DrawingBrush - 绘图画刷
- VisualBrush - 可视化元素画刷
1.SolidColorBrush (纯色画刷)
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<SolidColorBrush Color="Blue"/>
</Rectangle.Fill>
</Rectangle>

<!-- 简写形式 -->
<Rectangle Width="100" Height="50" Fill="Red"/>

2.LinearGradientBrush (线性渐变画刷)
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Yellow" Offset="0.0"/>
<GradientStop Color="Red" Offset="0.5"/>
<GradientStop Color="Blue" Offset="1.0"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
StartPoint/EndPoint: 定义渐变方向(0,0到1,1的坐标系)
GradientStop: 定义渐变的关键点
- Color: 颜色值
- Offset: 位置(0.0到1.0)
3.RadialGradientBrush (径向渐变画刷)
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<RadialGradientBrush GradientOrigin="0.5,0.5" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Blue" Offset="1"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>

GradientOrigin: 渐变起点 Center: 渐变中心点 RadiusX/RadiusY: 渐变半径 同样使用GradientStop定义颜色变化
4.ImageBrush (图像画刷)
<Rectangle Width="200" Height="100">
<Rectangle.Fill>
<ImageBrush ImageSource="https://www.diandiandidi.club/favicon.ico" Stretch="UniformToFill"/>
</Rectangle.Fill>
</Rectangle>
ImageSource: 图像路径
Stretch: 填充模式:
- None: 原始大小
- Fill: 拉伸填充
- Uniform: 等比例缩放
- UniformToFill: 等比例填充(可能裁剪)
5.DrawingBrush (绘图画刷)
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="LightBlue">
<GeometryDrawing.Pen>
<Pen Brush="Blue" Thickness="1"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,100,50"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>

- 使用Drawing对象定义复杂图案
- 适合创建自定义纹理和重复图案
6.VisualBrush (可视化元素画刷)
<StackPanel>
<StackPanel x:Name="sourcePanel" Background="LightGray">
<Button Content="示例按钮"/>
<Ellipse Width="50" Height="30" Fill="Green"/>
</StackPanel>
<Rectangle Width="200" Height="100" Margin="0,10,0,0">
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=sourcePanel}"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>

- 可以将任何可视化元素作为画刷内容
- 适合创建反射效果、放大镜等效果
7.画刷资源
<Window.Resources>
<LinearGradientBrush x:Key="myGradient" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Yellow" Offset="0.0"/>
<GradientStop Color="Orange" Offset="1.0"/>
</LinearGradientBrush>
</Window.Resources>
<Grid Background="{StaticResource myGradient}">
<!-- 内容 -->
</Grid>
8.画刷转换
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<LinearGradientBrush.RelativeTransform>
<RotateTransform Angle="45" CenterX="0.5" CenterY="0.5"/>
</LinearGradientBrush.RelativeTransform>
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Yellow" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
9.不透明度
<Rectangle Width="100" Height="50">
<Rectangle.Fill>
<SolidColorBrush Color="Blue" Opacity="0.5"/>
</Rectangle.Fill>
</Rectangle>
十五.特效
WPF特效是基于像素的视觉效果,可以应用于任何UI元素,为应用程序添加专业级的视觉效果而无需编写复杂代码。 WPF提供以下内置特效:
- BlurEffect - 模糊效果
- DropShadowEffect - 投影效果
- ShaderEffect - 自定义着色器效果
- BitmapEffect (已过时) - 早期特效(不推荐使用)
1.BlurEffect (模糊效果)
<Button Content="模糊按钮" Width="100" Height="30">
<Button.Effect>
<BlurEffect Radius="2" KernelType="Gaussian"/>
</Button.Effect>
</Button>

Radius: 模糊半径(0-20),值越大越模糊 KernelType: 模糊内核类型
- Box - 简单快速但质量较低
- Gaussian - 高斯模糊(质量更好)
2.DropShadowEffect (投影效果)
<Button Content="投影按钮" Width="100" Height="30">
<Button.Effect>
<DropShadowEffect
Color="Black"
Direction="315"
ShadowDepth="10"
Opacity="0.7"
BlurRadius="5"/>
</Button.Effect>
</Button>
Color: 阴影颜色(默认Black) Direction: 阴影方向(0-360度) ShadowDepth: 阴影距离(默认5) Opacity: 透明度(0.0-1.0) BlurRadius: 模糊半径(默认5) RenderingBias: 性能与质量选择
- Performance - 性能优先
- Quality - 质量优先

3.悬浮效果
<Button Content="悬停效果">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="5" ShadowDepth="2" Opacity="0.5"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect BlurRadius="15" ShadowDepth="10" Opacity="0.8"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>


4.聚焦高亮
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="DodgerBlue" BlurRadius="10" ShadowDepth="0"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

十六.动画
WPF动画系统特点
- 基于时间线:通过时间线控制属性变化
- 声明式编程:可在XAML中直接定义动画
- 硬件加速:利用GPU提高性能
- 丰富的动画类型:支持多种属性动画
核心动画类型
- 基本动画:DoubleAnimation, ColorAnimation, PointAnimation等
- 关键帧动画:DoubleAnimationUsingKeyFrames, ColorAnimationUsingKeyFrames等
- 路径动画:DoubleAnimationUsingPath, PointAnimationUsingPath
- 自定义动画:继承AnimationTimeline
简单案例 鼠标放上去后,0.2秒内宽度从30变成40
<Button x:Name="myButton" Content="动画按钮" Width="100" Height="30">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="myButton"
Storyboard.TargetProperty="Height"
From="30" To="40" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
1.基础动画
1.1 属性动画 (Property Animation)
适用场景:
- 简单属性变化(如宽度、高度、透明度等)
- 不需要复杂时间控制的动画
常用类型:
- DoubleAnimation - 用于双精度值属性(Width/Height/Opacity等)
- ColorAnimation - 用于颜色属性(Background/Foreground等)
- PointAnimation - 用于点属性(如图形位置)
- ThicknessAnimation - 用于边距属性(Margin/Padding)
<!-- 宽度动画 -->
<DoubleAnimation
Storyboard.TargetProperty="Width"
From="100" To="200" Duration="0:0:1"/>
<!-- 颜色动画 -->
<ColorAnimation
Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
From="Red" To="Blue" Duration="0:0:2"/>
2.关键帧动画
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width">
<!-- 线性变化 -->
<LinearDoubleKeyFrame Value="100" KeyTime="0:0:0"/>
<!-- 突然变化 -->
<DiscreteDoubleKeyFrame Value="120" KeyTime="0:0:1"/>
<!-- 平滑变化 -->
<SplineDoubleKeyFrame Value="200" KeyTime="0:0:2"
KeySpline="0.5,0 0.5,1"/>
</DoubleAnimationUsingKeyFrames>
3.路径动画
<Canvas>
<!-- 定义路径 -->
<Path x:Name="motionPath" Data="M 10,100 C 35,0 135,0 160,100" Stroke="Black"/>
<!-- 动画元素 -->
<Rectangle Width="20" Height="20" Fill="Red">
<Rectangle.RenderTransform>
<TranslateTransform x:Name="rectTransform"/>
</Rectangle.RenderTransform>
</Rectangle>
<!-- 路径动画 -->
<DoubleAnimationUsingPath
Storyboard.TargetName="rectTransform"
Storyboard.TargetProperty="X"
PathGeometry="{Binding Data, ElementName=motionPath}"
Duration="0:0:3" Source="X"/>
</Canvas>
4.并行动画
<Storyboard>
<ParallelTimeline>
<DoubleAnimation Storyboard.TargetProperty="Width" To="200" Duration="0:0:1"/>
<ColorAnimation Storyboard.TargetProperty="Background.Color" To="Blue" Duration="0:0:1"/>
</ParallelTimeline>
</Storyboard>
5.顺序动画
<Storyboard>
<SequenceTimeline>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.5"/>
<DoubleAnimation Storyboard.TargetProperty="Width" To="0" Duration="0:0:0.5"/>
</SequenceTimeline>
</Storyboard>
十七.行为
1.什么是Behavior?
在 WPF (Windows Presentation Foundation) 中,Behavior 是一种强大的设计模式,允许开发者通过附加属性(Attached Properties)将可重用的交互逻辑或功能封装到 UI 元素(如控件)中,而无需修改控件本身的代码或继承子类。Behavior 的核心思想是通过附加而非继承来扩展控件的功能。
1.1 Behavior 的核心概念
- 属于 Microsoft.Xaml.Behaviors 库:Behavior 最初是 System.Windows.Interactivity 命名空间的一部分(来自 Blend SDK),后来迁移到独立的库 Microsoft.Xaml.Behaviors.Wpf。需通过 NuGet 安装此包。
- 基于附加属性(Attached Properties):Behavior 通过附加属性将自身绑定到控件,从而监听事件、修改属性或调用方法。
- 无需继承或模板重写:例如:为按钮添加鼠标悬停动画,无需创建自定义按钮类。
1.2 Behavior 的组成
Behavior
触发器和动作 Trigger(触发器):监听条件(如点击、数据变化),触发时执行关联的 Action。 Action(动作):定义具体的响应行为(如播放动画、导航页面)。
2.示例:按钮动画
public class ScaleButtonBehavior : Behavior<Button>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += OnButtonClick;
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var scaleTransform = new ScaleTransform(1, 1);
AssociatedObject.RenderTransform = scaleTransform;
var animation = new DoubleAnimation(1.2, TimeSpan.FromSeconds(0.3));
scaleTransform.BeginAnimation(ScaleTransform.ScaleXProperty, animation);
scaleTransform.BeginAnimation(ScaleTransform.ScaleYProperty, animation);
}
protected override void OnDetaching()
{
AssociatedObject.Click -= OnButtonClick;
base.OnDetaching();
}
}
<Window xmlns:i="http://schemas.microsoft.com/xaml/behaviors">
<Button Content="Click Me">
<i:Interaction.Behaviors>
<local:ScaleButtonBehavior />
</i:Interaction.Behaviors>
</Button>
</Window>
十八.导航
在 WPF 中,导航(Navigation) 允许应用程序在不同的页面(Page)之间切换,类似于 Web 浏览器中的页面跳转。WPF 提供了内置的导航框架,支持前进、后退、页面缓存等功能,适用于需要多页面的应用程序(如向导、设置窗口或文档查看器)。
1.WPF导航的核心组件
1.1 Frame 控件
作用:作为导航容器,承载并切换不同的 Page。 关键属性:
- Source:指定初始页面的 URI。
- NavigationUIVisibility:控制是否显示导航工具栏(前进/后退按钮)。
- JournalOwnership:决定是否保留导航历史记录(默认 OwnsJournal)。
1.2 Page 类
作用:表示可导航的页面,类似于 Window,但专为导航设计。 关键特性:
- 支持标题(Title)、窗口宽度/高度(WindowWidth、WindowHeight)。
- 可通过 NavigationService 实现编程导航。
1.3 NavigationService
作用:提供导航的编程控制(如跳转、前进、后退)。 常用方法:
- Navigate():跳转到指定页面。
- GoBack() / GoForward():导航历史记录的前进/后退。
- StopLoading():取消导航。
2.实现导航的步骤
2.1 定义 Page
创建多个页面(如 Page1.xaml 和 Page2.xaml):
<!-- Page1.xaml -->
<Page x:Class="WpfApp.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page 1">
<StackPanel>
<TextBlock Text="This is Page 1"/>
<Button Content="Go to Page 2" Click="NavigateToPage2"/>
</StackPanel>
</Page>
// Page1.xaml.cs
private void NavigateToPage2(object sender, RoutedEventArgs e)
{
// 通过 NavigationService 导航
this.NavigationService?.Navigate(new Uri("Page2.xaml", UriKind.Relative));
}
2.2 使用 Frame 承载导航
在 MainWindow.xaml 中添加 Frame:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WPF Navigation" Height="300" Width="400">
<Frame x:Name="MainFrame" Source="Page1.xaml" NavigationUIVisibility="Visible"/>
</Window>
3.导航的常见操作
3.1 编程导航
通过 NavigationService 或 Frame 直接控制:
// 跳转到 Page2
MainFrame.Navigate(new Uri("Page2.xaml", UriKind.Relative));
// 前进/后退
if (MainFrame.CanGoBack)
MainFrame.GoBack();
if (MainFrame.CanGoForward)
MainFrame.GoForward();
3.2 传递参数
使用 Navigate 方法传递对象:
// 传递参数
MainFrame.Navigate(new Page2(), "Hello from Page1!");
// 在 Page2 中接收参数
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (e.ExtraData is string message)
{
MessageBox.Show(message); // 输出 "Hello from Page1!"
}
}
3.3 拦截导航事件
- Navigating:导航开始前触发(可取消)。
- Navigated:导航完成后触发。
MainFrame.Navigating += (sender, e) =>
{
if (e.Uri.ToString().Contains("Page2"))
{
e.Cancel = true; // 阻止跳转到 Page2
}
};
4.导航的进阶用法
4.1 页面缓存
默认情况下,WPF 会缓存已访问的页面。可通过 Page 的 KeepAlive 属性控制:
<Page KeepAlive="False" ... />
4.2 自定义导航工具栏
隐藏默认工具栏,自定义按钮:
<Frame NavigationUIVisibility="Hidden">
<Frame.Template>
<ControlTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Back" Command="{x:Static NavigationCommands.BrowseBack}"/>
<Button Content="Forward" Command="{x:Static NavigationCommands.BrowseForward}"/>
</StackPanel>
<ContentPresenter/> <!-- 显示当前 Page -->
</StackPanel>
</ControlTemplate>
</Frame.Template>
</Frame>