WPF 概述

Windows Presentation Foundation(WPF)允许你为 Windows 创建具有直观令人惊叹的用户体验的桌面客户端应用程序。

Contoso Healthcare UI 示例

WPF 的核心是一个与分辨率无关且基于矢量的呈现引擎,旨在利用现代图形硬件。 WPF 使用一组全面的应用程序开发功能扩展核心,这些功能包括可扩展应用程序标记语言(XAML)、控件、数据绑定、布局、2D 和 3D 图形、动画、样式、模板、文档、媒体、文本和版式。 WPF 是 .NET 的一部分,因此可以生成包含 .NET API 其他元素的应用程序。

此概述适用于新用户,涵盖 WPF 的主要功能和概念。

使用 WPF 进行编程

WPF 作为位于 System.Windows 命名空间中的 .NET 类型的子集存在(在大多数情况下)。 如果以前使用托管技术(如 ASP.NET 和 Windows 窗体)生成了 .NET 的应用程序,则应熟悉基本的 WPF 编程体验;可以使用你喜欢的 .NET 编程语言(如 C# 或 Visual Basic)实例化类、设置属性、调用方法和处理事件。

WPF 包括增强属性和事件的其他编程构造:依赖属性路由事件

标记和代码隐藏

WPF 允许你使用 标记后置代码来开发应用程序,这种开发体验应该是 ASP.NET 开发人员所熟悉的。 通常使用 XAML 标记来实现应用程序的外观,同时使用托管编程语言(后台代码)来实现其行为。 这种外观和行为的分离具有以下优势:

  • 开发和维护成本会降低,因为特定于外观的标记与行为特定的代码没有紧密耦合。

  • 开发更高效,因为设计人员在设计应用程序的外观时,可以与开发人员同时实现应用程序的功能。

  • WPF 应用程序的全球化和本地化 得以简化。

标记

XAML 是基于 XML 的标记语言,以声明方式实现应用程序的外观。 通常使用它来创建窗口、对话框、页面和用户控件,并用控件、形状和图形填充它们。

以下示例使用 XAML 实现包含单个按钮的窗口的外观:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Title="Window with Button"
    Width="250" Height="100">

  <!-- Add button to window -->
  <Button Name="button">Click Me!</Button>

</Window>

具体而言,此 XAML 分别使用 WindowButton 元素定义窗口和按钮。 每个元素都配置了属性,例如 Window 元素的 Title 属性来指定窗口的标题栏文本。 在运行时,WPF 将标记中定义的元素和属性转换为 WPF 类的实例。 例如,Window 元素转换为 Window 类的实例,该类 Title 属性是 Title 特性的值。

下图显示了上一示例中 XAML 定义的用户界面(UI):

包含按钮 的窗口

由于 XAML 是基于 XML 的,因此使用 XAML 撰写的 UI 在嵌套元素的层次结构中组装,称为 元素树。 元素树提供了创建和管理 UI 的逻辑直观方法。

代码隐藏

应用程序的主要行为是实现响应用户交互的功能,包括处理事件(例如单击菜单、工具栏或按钮),以及调用响应中的业务逻辑和数据访问逻辑。 在 WPF 中,此行为在与标记关联的代码中实现。 此类代码称为代码隐藏。 下面的示例演示上一个示例的更新标记和代码隐藏:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.AWindow"
    Title="Window with Button"
    Width="250" Height="100">

  <!-- Add button to window -->
  <Button Name="button" Click="button_Click">Click Me!</Button>

</Window>
using System.Windows; // Window, RoutedEventArgs, MessageBox

namespace SDKSample
{
    public partial class AWindow : Window
    {
        public AWindow()
        {
            // InitializeComponent call is required to merge the UI
            // that is defined in markup with this class, including  
            // setting properties and registering event handlers
            InitializeComponent();
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            // Show message box when button is clicked.
            MessageBox.Show("Hello, Windows Presentation Foundation!");
        }
    }
}
Namespace SDKSample

    Partial Public Class AWindow
        Inherits System.Windows.Window

        Public Sub New()

            ' InitializeComponent call is required to merge the UI
            ' that is defined in markup with this class, including  
            ' setting properties and registering event handlers
            InitializeComponent()

        End Sub

        Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)

            ' Show message box when button is clicked.
            MessageBox.Show("Hello, Windows Presentation Foundation!")

        End Sub

    End Class

End Namespace

在此示例中,后台代码实现了一个继承自 Window 类的类。 x:Class 属性用于将标记与后台代码类相关联。 从代码隐藏类的构造函数调用 InitializeComponent,以将标记中定义的 UI 与代码隐藏类合并在一起。 (生成应用程序时会为你生成InitializeComponent,这就是为什么你不需要手动实现它的原因。x:ClassInitializeComponent 的组合可确保在创建实现时正确初始化。 代码隐藏类还可实现按钮的 Click 事件的事件处理程序。 单击按钮时,事件处理程序通过调用 System.Windows.MessageBox.Show 方法显示消息框。

下图显示了单击按钮时的结果:

消息框

控件

应用程序模型所提供的用户体验是由构建的控件形成的。 在 WPF 中,控件 是一个伞式术语,适用于在窗口或页面中托管的 WPF 类类别、具有用户界面并实现某些行为。

有关详细信息,请参阅 控件

按函数排序的 WPF 控件

此处列出了内置 WPF 控件:

输入和命令

控制通常检测和响应用户输入。 WPF 输入系统 使用直接事件和路由事件来支持文本输入、焦点管理和鼠标定位。

应用程序通常具有复杂的输入要求。 WPF 提供了一个 命令系统,用于将用户输入操作与响应这些操作的代码分开。

布局

创建用户界面时,按位置和大小排列控件以形成布局。 任何布局的关键要求是适应窗口大小和显示设置的变化。 WPF 提供一流的可扩展布局系统,而不是强制你编写代码来适应这些情况下的布局。

布局系统的基石是相对定位,这增加了适应不断变化的窗口和显示条件的能力。 此外,布局系统还管理控件之间的协商以确定布局。 协商是一个双重过程:首先,控件告诉其父级它需要的位置和大小:其次,父级告诉控件它可以具有的空间。

该布局系统通过基 WPF 类公开给子控件。 对于网格、堆叠和停靠等常见布局,WPF 包括多个布局控件:

  • Canvas:子控件提供其自己的布局。

  • DockPanel:子控件与面板的边缘对齐。

  • Grid:子控件由行和列定位。

  • StackPanel:子控件垂直或水平堆叠。

  • VirtualizingStackPanel:子控件在水平或垂直的行上虚拟化并排列。

  • WrapPanel:子控件按从左到右的顺序定位,在当前行上的控件超出允许的空间时,换行到下一行。

以下示例使用 DockPanel 来布局多个 TextBox 控件:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.LayoutWindow"
    Title="Layout with the DockPanel" Height="143" Width="319">
  
  <!--DockPanel to layout four text boxes--> 
  <DockPanel>
    <TextBox DockPanel.Dock="Top">Dock = "Top"</TextBox>
    <TextBox DockPanel.Dock="Bottom">Dock = "Bottom"</TextBox>
    <TextBox DockPanel.Dock="Left">Dock = "Left"</TextBox>
    <TextBox Background="White">This TextBox "fills" the remaining space.</TextBox>
  </DockPanel>

</Window>

DockPanel 允许子 TextBox 控件,以告诉它如何排列这些控件。 为了完成此操作,DockPanel 实现 Dock 附加了属性,该属性公开给子控件,以允许每个子控件指定停靠样式。

说明

由父控件实现以供子控件使用的属性是一个 WPF 构造,称为 附加属性

下图显示了前面的示例中 XAML 标记的结果:

DockPanel 页

数据绑定

大多数应用程序都是为了为用户提供查看和编辑数据的方法而创建的。 对于 WPF 应用程序,存储和访问数据的工作已经由 SQL Server 和 ADO .NET 等技术提供。 访问数据并将其加载到应用程序的托管对象后,WPF 应用程序的辛勤工作开始。 实质上,这涉及两件事:

  1. 将数据从托管对象复制到控件中,可在其中显示和编辑数据。

  2. 确保使用控件对数据所做的更改复制回托管对象。

为了简化应用程序开发,WPF 提供了一个数据绑定引擎来自动执行这些步骤。 数据绑定引擎的核心单元是 Binding 类,其作业是将控件(绑定目标)绑定到数据对象(绑定源)。 下图说明了此关系:

基本数据绑定关系图

下一个示例演示如何将 TextBox 绑定到自定义 Person 对象的实例。 以下代码中显示了 Person 实现:

Namespace SDKSample

    Class Person

        Private _name As String = "No Name"

        Public Property Name() As String
            Get
                Return _name
            End Get
            Set(ByVal value As String)
                _name = value
            End Set
        End Property

    End Class

End Namespace
namespace SDKSample
{
    class Person
    {
        string name = "No Name";

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }
}

以下标记将 TextBox 绑定到自定义 Person 对象的实例:

 <Window
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="SDKSample.DataBindingWindow">

   <!-- Bind the TextBox to the data source (TextBox.Text to Person.Name) -->
   <TextBox Name="personNameTextBox" Text="{Binding Path=Name}" />

 </Window>
Imports System.Windows ' Window

Namespace SDKSample

    Partial Public Class DataBindingWindow
        Inherits Window

        Public Sub New()
            InitializeComponent()

            ' Create Person data source
            Dim person As Person = New Person()

            ' Make data source available for binding
            Me.DataContext = person

        End Sub

    End Class

End Namespace
using System.Windows; // Window

namespace SDKSample
{
    public partial class DataBindingWindow : Window
    {
        public DataBindingWindow()
        {
            InitializeComponent();

            // Create Person data source
            Person person = new Person();

            // Make data source available for binding
            this.DataContext = person;
        }
    }
}

在此示例中,Person 类在后置代码中实例化,并设为 DataBindingWindow的数据上下文。 在标记中,TextBoxText 属性绑定到 Person.Name 属性(使用“{Binding ... }”XAML 语法)。 此 XAML 告知 WPF 将 TextBox 控件绑定到存储在窗口 DataContext 属性中的 Person 对象。

WPF 数据绑定引擎提供其他支持,包括验证、排序、筛选和分组。 此外,数据绑定还支持在标准 WPF 控件显示的用户界面不适合时使用数据模板为绑定数据创建自定义用户界面。

有关详细信息,请参阅 数据绑定概述

图形

WPF 引入了一组广泛的、可缩放且灵活的图形功能,这些功能具有以下优势:

  • 图形与分辨率和设备均无关。 WPF 图形系统中的基本度量单位是设备无关像素,它是一英寸的 1/96,与实际屏幕分辨率无关,并为分辨率无关和设备无关的渲染提供基础。 每个与设备无关的像素都会自动缩放,以匹配呈现它的系统的每英寸点数 (dpi) 设置。

  • 改进了精度。 WPF 坐标系使用双精度浮点数字进行测量,而不是单精度。 转换和不透明度值也表示为双精度数字。 WPF 还支持宽色域(scRGB),并提供集成支持来管理来自不同颜色空间的输入。

  • 高级图形和动画支持。 WPF 通过为你管理动画场景来简化图形编程;无需担心场景处理、呈现循环和双线性内插。 此外,WPF 还提供了点击测试支持和全面的 alpha 合成支持。

  • 硬件加速。 WPF 图形系统利用图形硬件来最大程度地减少 CPU 使用率。

二维形状

WPF 提供了一组常用的矢量绘制的二维形状库,例如下图所示的矩形和椭圆:

省略号和矩形

形状的一项有趣的功能是,它们不只是用于显示;形状实现控件所需的许多功能,包括键盘和鼠标输入。 下面的示例演示要处理的 MouseUpEllipse 事件:

<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.EllipseEventHandlingWindow"
    Title="Click the Ellipse">
    <Ellipse Name="clickableEllipse" Fill="Blue" MouseUp="clickableEllipse_MouseUp" />
</Window>
Imports System.Windows ' Window, MessageBox
Imports System.Windows.Input ' MouseButtonEventArgs

Namespace SDKSample

    Public Class EllipseEventHandlingWindow
        Inherits Window

        Public Sub New()
            InitializeComponent()
        End Sub

        Private Sub clickableEllipse_MouseUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
            MessageBox.Show("You clicked the ellipse!")
        End Sub

    End Class

End Namespace
using System.Windows; // Window, MessageBox
using System.Windows.Input; // MouseButtonEventHandler

namespace SDKSample
{
    public partial class EllipseEventHandlingWindow : Window
    {
        public EllipseEventHandlingWindow()
        {
            InitializeComponent();
        }

        void clickableEllipse_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Display a message
            MessageBox.Show("You clicked the ellipse!");
        }
    }
}

下图显示了上述代码生成的内容:

显示文本“你已单击该椭圆!”的窗口

有关详细信息,请参阅 WPF 概述中的“形状和基本绘图”

2D 几何图形

WPF 提供的 2D 形状涵盖一套标准的基本形状。 但是,可能需要创建自定义形状来简化自定义用户界面的设计。 为此,WPF 提供几何图形。 下图演示了如何使用几何图形来创建可直接绘制、用作画笔或用于剪辑其他形状和控件的自定义形状。

Path 对象可用于绘制封闭或打开的形状、多个形状,甚至曲线形状。

Geometry 对象可用于剪辑、命中测试以及呈现二维图形数据。

路径的多种用法

有关详细信息,请参阅 几何学概述

2D 效果

WPF 2D 功能的子集包括视觉效果,如渐变、位图、绘图、使用视频绘制、旋转、缩放和倾斜。 这些都是通过画笔实现的;下图显示了一些示例:

不同画笔的插图

有关详细信息,请参阅 WPF 画笔概述

3D 渲染

WPF 还包括与 2D 图形集成的 3D 渲染功能,以允许创建更令人兴奋的有趣的用户界面。 例如,下图显示呈现在三维形状上的二维图像:

Visual3D 示例屏幕截图

有关详细信息,请参阅 3D 图形概述

动画

WPF 动画支持可以使控件变大、抖动、旋转和淡出,以形成有趣的页面过渡等。 你可以对大多数 WPF 类(甚至自定义类)进行动画处理。 下图显示了运行中的一个简单动画:

具有动画效果的立方体图

有关详细信息,请参阅 动画概述

媒体

传达丰富内容的一种方法是通过使用音像媒体。 WPF 为图像、视频和音频提供特殊支持。

图像

图像对大多数应用程序很常见,WPF 提供了多种使用它们的方法。 下图显示了包含缩略图的列表框的用户界面。 选择缩略图后,图像将显示全尺寸。

缩略图和全尺寸图像

有关详细信息,请参阅 映像概述

视频和音频

MediaElement 控件能够同时播放视频和音频,并且它足够灵活,足以成为自定义媒体播放器的基础。 以下 XAML 标记实现媒体播放器:

<MediaElement 
  Name="myMediaElement" 
  Source="media/wpf.wmv" 
  LoadedBehavior="Manual" 
  Width="350" Height="250" />

下图中的窗口展示了 MediaElement 控件的操作:

具有音频和视频的 MediaElement 控件

有关详细信息,请参阅图形和多媒体

文本和版式

为了方便高质量的文本呈现,WPF 提供以下功能:

  • OpenType 字体支持。

  • ClearType 增强功能。

  • 高性能,得益于硬件加速。

  • 将文本与媒体、图形和动画集成。

  • 国际字体支持和回退机制。

作为文本与图形集成的演示,下图显示了文本修饰的应用:

带有各种文本修饰的文本

有关详细信息,请参阅 Windows Presentation Foundation中的 版式。

自定义 WPF 应用

至此,你已了解开发应用程序的核心 WPF 构建基块。 使用应用程序模型来托管和交付应用程序内容,主要包括控件。 为了简化用户界面中控件的排列,并确保在窗口大小和显示设置发生更改时保持排列,请使用 WPF 布局系统。 由于大多数应用程序允许用户与数据交互,因此使用数据绑定来减少将用户界面与数据集成的工作。 若要增强应用程序的视觉外观,请使用 WPF 提供的图形、动画和媒体支持的全面范围。

不过,基础知识通常不足以创建和管理真正独特的直观令人惊叹的用户体验。 标准 WPF 控件可能难以与您的应用程序所需的外观进行有效集成。 数据可能不会以最有效的方式显示。 应用程序的整体用户体验可能不适合 Windows 主题的默认外观。 在许多方面,呈现技术需要视觉扩展性,就像任何其他类型的扩展性一样。

因此,WPF 提供了各种机制来创建独特的用户体验,包括用于控件、触发器、控件和数据模板、样式、用户界面资源以及主题和外观的丰富内容模型。

内容模型

大多数 WPF 控件的主要用途是显示内容。 在 WPF 中,构成控件内容的项的类型和数量称为控件 内容模型。 某些控件可以包含单个项和内容类型;例如,TextBox 的内容是分配给 Text 属性的字符串值。 下面的示例设置 TextBox 的内容:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.TextBoxContentWindow"
    Title="TextBox Content">

    <TextBox Text="This is the content of a TextBox." />
</Window>

下图显示了结果:

包含文本 的 TextBox 控件

但是,其他控件可以包含不同类型的内容的多个项;Content 属性指定的 Button的内容可以包含各种项目,包括布局控件、文本、图像和形状。 下面的示例演示了 Button,其内容包括 DockPanelLabelBorderMediaElement

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.ButtonContentWindow"
    Title="Button Content">

  <Button Margin="20">
    <!-- Button Content -->
    <DockPanel Width="200" Height="180">
      <Label DockPanel.Dock="Top" HorizontalAlignment="Center">Click Me!</Label>
      <Border Background="Black" BorderBrush="Yellow" BorderThickness="2"
        CornerRadius="2" Margin="5">
        <MediaElement Source="media/wpf.wmv" Stretch="Fill" />
      </Border>
    </DockPanel>
  </Button>
</Window>

下图显示了此按钮的内容:

包含多种类型的内容 的按钮

有关各种控件支持的内容类型的详细信息,请参阅 WPF 内容模型

触发器

尽管 XAML 标记的主要用途是实现应用程序的外观,但也可以使用 XAML 实现应用程序行为的一些方面。 一个示例是使用触发器根据用户交互更改应用程序的外观。 有关详细信息,请参阅 样式和模板

控件模板

WPF 控件的默认用户界面通常是由其他控件和形状构成的。 例如,ButtonButtonChromeContentPresenter 控件组成。 ButtonChrome 提供标准按钮外观,而 ContentPresenter 显示由 Content 属性指定的按钮内容。

有时,控件的默认外观可能与应用程序的整体外观不一致。 在这种情况下,可以使用 ControlTemplate 更改控件用户界面的外观,而无需更改其内容和行为。

以下示例演示如何使用 ControlTemplate更改 Button 的外观:

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.ControlTemplateButtonWindow"
  Title="Button with Control Template" Height="158" Width="290">

  <!-- Button using an ellipse -->
  <Button Content="Click Me!" Click="button_Click">
    <Button.Template>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid Margin="5">
          <Ellipse Stroke="DarkBlue" StrokeThickness="2">
            <Ellipse.Fill>
              <RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5">
                <GradientStop Color="Azure" Offset="0.1" />
                <GradientStop Color="CornflowerBlue" Offset="1.1" />
              </RadialGradientBrush>
            </Ellipse.Fill>
          </Ellipse>
          <ContentPresenter Name="content" HorizontalAlignment="Center" 
            VerticalAlignment="Center"/>
        </Grid>
      </ControlTemplate>
    </Button.Template>

  </Button>

</Window>
using System.Windows; // Window, RoutedEventArgs, MessageBox

namespace SDKSample
{
    public partial class ControlTemplateButtonWindow : Window
    {
        public ControlTemplateButtonWindow()
        {
            InitializeComponent();
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            // Show message box when button is clicked
            MessageBox.Show("Hello, Windows Presentation Foundation!");
        }
    }
}
Imports System.Windows ' Window, RoutedEventArgs, MessageBox

Namespace SDKSample

    Public Class ControlTemplateButtonWindow
        Inherits Window

        Public Sub New()

            InitializeComponent()

        End Sub

        Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            MessageBox.Show("Hello, Windows Presentation Foundation!")
        End Sub

    End Class

End Namespace

在此示例中,默认按钮用户界面已被替换为 Ellipse,它具有深蓝色边框并使用 RadialGradientBrush 进行填充。 ContentPresenter 控件显示内容 Button"Click Me!"。单击 Button 时,仍会引发 Click 事件,这是 Button 控件的默认行为。 下图显示了结果:

椭圆按钮和第二个窗口

数据模板

而控件模板允许你指定控件的外观,而数据模板允许你指定控件内容的外观。 数据模板通常用于增强绑定数据的显示方式。 下图显示 ListBox 的默认外观,它绑定到 Task 对象的集合,其中每个任务都具有名称、描述和优先级:

具有默认外观的列表框

默认外观是你对 ListBox的期望。 但是,每个任务的默认外观仅包含任务名称。 若要显示任务名称、说明和优先级,必须使用 DataTemplate更改 ListBox 控件绑定列表项的默认外观。 下面的 XAML 定义了此类 DataTemplate,它通过使用 ItemTemplate 属性应用于每个任务:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.DataTemplateWindow"
  Title="With a Data Template">
  <Window.Resources>
    <!-- Data Template (applied to each bound task item in the task collection) -->
    <DataTemplate x:Key="myTaskTemplate">
      <Border Name="border" BorderBrush="DarkSlateBlue" BorderThickness="2"
        CornerRadius="2" Padding="5" Margin="5">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Row="0" Grid.Column="0" Padding="0,0,5,0" Text="Task Name:"/>
          <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=TaskName}"/>
          <TextBlock Grid.Row="1" Grid.Column="0" Padding="0,0,5,0" Text="Description:"/>
          <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=Description}"/>
          <TextBlock Grid.Row="2" Grid.Column="0" Padding="0,0,5,0" Text="Priority:"/>
          <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Priority}"/>
        </Grid>
      </Border>
    </DataTemplate>
  </Window.Resources>

  <!-- UI -->
  <DockPanel>
    <!-- Title -->
    <Label DockPanel.Dock="Top" FontSize="18" Margin="5" Content="My Task List:"/>

    <!-- Data template is specified by the ItemTemplate attribute -->
    <ListBox
      ItemsSource="{Binding}"
      ItemTemplate="{StaticResource myTaskTemplate}"
      HorizontalContentAlignment="Stretch"
      IsSynchronizedWithCurrentItem="True"
      Margin="5,0,5,5" />

 </DockPanel>
</Window>

下图显示了此代码的效果:

使用数据模板的列表框

请注意,ListBox 已保留其行为和整体外观;仅列表框显示的内容的外观已更改。

有关详细信息,请参阅 数据模板化概述

样式

样式使开发人员和设计人员能够对其产品的特定外观进行标准化。 WPF 提供强样式模型,其基础是 Style 元素。 以下示例创建一个样式,该样式将窗口上每个 Button 的背景色设置为 Orange

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.StyleWindow"
    Title="Styles">
    
    <Window.Resources>
        <!-- Style that will be applied to all buttons for this window -->
        <Style TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Orange" />
            <Setter Property="BorderBrush" Value="Crimson" />
            <Setter Property="FontSize" Value="20" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Margin" Value="5" />
        </Style>
    </Window.Resources>
    <StackPanel>

        <!-- This button will have the style applied to it -->
        <Button>Click Me!</Button>

        <!-- This label will not have the style applied to it -->
        <Label>Don't Click Me!</Label>

        <!-- This button will have the style applied to it -->
        <Button>Click Me!</Button>
        
    </StackPanel>
</Window>

由于此样式面向所有 Button 控件,因此该样式会自动应用于窗口中的所有按钮,如下图所示:

两个橙色按钮

有关详细信息,请参阅 样式和模板

资源

应用程序中的控件应共享相同的外观,其中包括从字体和背景颜色到控件模板、数据模板和样式的任何内容。 可以使用 WPF 对用户界面资源的支持将这些资源封装在单个位置以供重复使用。

以下示例定义由 ButtonLabel共享的常见背景色:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.ResourcesWindow"
    Title="Resources Window">

  <!-- Define window-scoped background color resource -->
  <Window.Resources>
    <SolidColorBrush x:Key="defaultBackground" Color="Red" />
  </Window.Resources>

  <!-- Button background is defined by window-scoped resource -->
  <Button Background="{StaticResource defaultBackground}">One Button</Button>

  <!-- Label background is defined by window-scoped resource -->
  <Label Background="{StaticResource defaultBackground}">One Label</Label>
</Window>

此示例使用 Window.Resources 属性元素实现背景色资源。 此资源可供 Window的所有子级使用。 有多种资源范围,按解决顺序列出如下:

  1. 单个控件(使用继承的 System.Windows.FrameworkElement.Resources 属性)。

  2. WindowPage(也使用继承的 System.Windows.FrameworkElement.Resources 属性)。

  3. Application(使用 System.Windows.Application.Resources 属性)。

各种范围使你可以灵活地定义和共享资源的方式。

作为直接将你的资源与特定作用域关联的替代方法,可以通过使用单独的 ResourceDictionary(可以在应用程序的其他部分引用)打包一个或多个资源。 例如,以下示例在资源字典中定义默认背景色:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <!-- Define background color resource -->
  <SolidColorBrush x:Key="defaultBackground" Color="Red" />

  <!-- Define other resources -->
</ResourceDictionary>

以下示例引用在上一示例中定义的资源字典,以便它跨应用程序共享:

<Application
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.App">

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="BackgroundColorResources.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

资源和资源字典是 WPF 主题和皮肤支持的基础。

有关详细信息,请参阅 资源

自定义控件

尽管 WPF 提供了大量自定义支持,但可能会遇到现有 WPF 控件无法满足应用程序或其用户的需求的情况。 当以下情况发生时,可能会发生这种情况:

  • 无法通过自定义现有 WPF 实现的外观来创建所需的用户界面。

  • 现有的 WPF 实现不支持你所需的行为(或支持起来很困难)。

但是,此时可以利用三个 WPF 模型之一创建新控件。 每个模型都面向特定方案,并要求自定义控件派生自特定的 WPF 基类。 下面列出了三种模型:

  • 用户控制模型。 自定义控件派生自 UserControl,由一个或多个其他控件组成。

  • 控件模型。 自定义控件派生自 Control,用于生成实现,这些实现使用模板将其行为与其外观分开,这与大多数 WPF 控件非常类似。 派生自 Control 使得你可以更自由地创建自定义用户界面(相较用户控件),但它可能需要花费更多精力。

  • 框架元素模型。 自定义控件在通过自定义呈现逻辑(而不是模板)定义其外观时,派生自 FrameworkElement

以下示例显示了派生自 UserControl的自定义数值向上/向下控件:

<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.NumericUpDown">

  <Grid>

    <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <!-- Value text box -->
    <Border BorderThickness="1" BorderBrush="Gray" Margin="2" Grid.RowSpan="2" 
      VerticalAlignment="Center" HorizontalAlignment="Stretch">
      <TextBlock Name="valueText" Width="60" TextAlignment="Right" Padding="5"/>
    </Border>

    <!-- Up/Down buttons -->
    <RepeatButton Name="upButton" Click="upButton_Click" Grid.Column="1" 
      Grid.Row="0">Up</RepeatButton>
    <RepeatButton Name="downButton" Click="downButton_Click" Grid.Column="1" 
      Grid.Row="1">Down</RepeatButton>

  </Grid>

</UserControl>
using System; // EventArgs
using System.Windows; // DependencyObject, DependencyPropertyChangedEventArgs,
                      // FrameworkPropertyMetadata, PropertyChangedCallback,
                      // RoutedPropertyChangedEventArgs
using System.Windows.Controls; // UserControl

namespace SDKSample
{
    public partial class NumericUpDown : UserControl
    {
        // NumericUpDown user control implementation
    }
}
imports System 'EventArgs
imports System.Windows 'DependencyObject, DependencyPropertyChangedEventArgs, 
                       ' FrameworkPropertyMetadata, PropertyChangedCallback, 
                       ' RoutedPropertyChangedEventArgs
imports System.Windows.Controls 'UserControl

Namespace SDKSample

    ' Interaction logic for NumericUpDown.xaml
    Partial Public Class NumericUpDown
        Inherits System.Windows.Controls.UserControl

        'NumericUpDown user control implementation

    End Class

End Namespace

以下示例演示了将用户控件合并到 Window所需的 XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.UserControlWindow"
    xmlns:local="clr-namespace:SDKSample" 
    Title="User Control Window">

  <!-- Numeric Up/Down user control -->
  <local:NumericUpDown />

</Window>

下图显示了 Window 中托管的 NumericUpDown 控件:

自定义 UserControl

有关自定义控件的详细信息,请参阅 控件创作概述

WPF 最佳做法

与任何开发平台一样,WPF 可以采用多种方式来实现所需的结果。 为了确保您的 WPF 应用程序能提供所需的用户体验并满足普通受众的需求,建议遵循在辅助功能、全球化和本地化以及性能方面的最佳做法。 有关详细信息,请参阅:

后续步骤

我们已经了解了 WPF 的主要功能。 现在是时候生成第一个 WPF 应用了。

另请参阅