教程:在 Visual Studio 2019 中创建第一个 WPF 应用程序

本文介绍如何开发 Windows Presentation Foundation(WPF)桌面应用程序,其中包含大多数 WPF 应用程序通用的元素:可扩展应用程序标记语言(XAML)标记、代码隐藏、应用程序定义、控件、布局、数据绑定和样式。 若要开发应用程序,你将使用 Visual Studio。

重要

本文是针对 .NET Framework 编写的。 若要开始使用 .NET,请参阅教程:创建新的 WPF 应用(WPF .NET)。

本教程中,您将学习如何:

  • 创建 WPF 项目。
  • 使用 XAML 设计应用程序用户界面(UI)的外观。
  • 编写代码以生成应用程序的行为。
  • 创建用于管理应用程序的应用程序定义。
  • 添加控件并创建布局以撰写应用程序 UI。
  • 创建样式以在整个应用程序的 UI 中保持一致的外观。
  • 将 UI 绑定到数据,既要填充数据中的 UI,又要使数据和 UI 保持同步。

本教程结束时,你将构建一个独立的 Windows 应用程序,允许用户查看所选人员的支出报表。 该应用程序由多个 WPF 页面组成,这些页面托管在浏览器样式窗口中。

小窍门

本教程中使用的示例代码适用于 教程 WPF 应用示例代码中的 Visual Basic 和 C# 。

可以使用本页顶部的语言选择器在 C# 和 Visual Basic 之间切换示例代码的代码语言。

先决条件

创建应用程序项目

第一步是创建应用程序基础结构,其中包括应用程序定义、两页和一个图像。

  1. 在 Visual Basic 或 Visual C# 中创建名为 ExpenseIt

    1. 打开 Visual Studio,然后选择“开始”菜单下的“创建新项目”。

      此时会打开 “创建新项目 ”对话框。

    2. “语言 ”下拉列表中,选择 C#Visual Basic

    3. 选择 WPF 应用(.NET Framework) 模板,然后选择“ 下一步”。

      “创建新项目”对话框

      此时会打开 “配置新项目 ”对话框。

    4. 输入项目名称 ExpenseIt ,然后选择“ 创建”。

      配置新项目对话框

      Visual Studio 创建项目,并打开名为 MainWindow.xaml 的默认应用程序窗口的设计器。

  2. 打开 Application.xaml (Visual Basic)或 App.xaml (C#)。

    此 XAML 文件定义 WPF 应用程序和任何应用程序资源。 还可以使用此文件来指定 UI,在本例中, MainWindow.xaml 会在应用程序启动时自动显示。

    XAML 在 Visual Basic 中应如下所示:

    <Application x:Class="Application"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        StartupUri="MainWindow.xaml">
        <Application.Resources>
            
        </Application.Resources>
    </Application>
    

    在 C# 中如下所示:

    <Application x:Class="ExpenseIt.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         StartupUri="MainWindow.xaml">
        <Application.Resources>
             
        </Application.Resources>
    </Application>
    
  3. 打开 MainWindow.xaml

    此 XAML 文件是应用程序的主窗口,并显示在页面中创建的内容。 该 Window 类定义窗口的属性,例如窗口的标题、大小或图标,并处理事件,例如关闭或隐藏。

  4. Window 元素更改为 a NavigationWindow,如以下 XAML 所示:

    <NavigationWindow x:Class="ExpenseIt.MainWindow"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         ...
    </NavigationWindow>
    

    此应用根据用户输入导航到不同的内容。 这就是为什么主要 Window 需要更改为 a NavigationWindowNavigationWindow 继承 . 的所有属性 Window。 XAML 文件中的NavigationWindow元素创建了NavigationWindow类的实例。 有关详细信息,请参阅 导航概述

  5. NavigationWindow标签之间删除Grid元素。

  6. 更改元素的 XAML 代码 NavigationWindow 中的以下属性:

    • Title 属性设置为“ExpenseIt”。

    • Height 属性设置为 350 像素。

    • Width 属性设置为 500 像素。

    你的 XAML 应如下所示,适用于 Visual Basic:

    <NavigationWindow x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ExpenseIt" Height="350" Width="500">
     
    </NavigationWindow>
    

    对于 C#,如下所示:

    <NavigationWindow x:Class="ExpenseIt.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ExpenseIt" Height="350" Width="500">
        
    </NavigationWindow>
    
  7. 打开 MainWindow.xaml.vbMainWindow.xaml.cs

    此文件是一个代码隐藏文件,其中包含用于处理 MainWindow.xaml 中声明的事件的代码。 此文件包含 XAML 中定义的窗口的分部类。

  8. 如果使用的是 C#,请将 MainWindow 类更改为派生自 NavigationWindow。 (在 Visual Basic 中,在 XAML 中更改窗口时会自动发生这种情况。C# 代码现在应如下所示:

    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace ExpenseIt
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : NavigationWindow
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    }
    

将文件添加到应用程序

在本部分中,你将向应用程序添加两个页面和一个图像。

  1. 向项目添加新页面,并将其命名 ExpenseItHome.xaml为:

    1. 解决方案资源管理器中,右键单击 ExpenseIt 项目节点,然后选择“ 添加>页面”。

    2. 在“ 添加新项 ”对话框中,已选择 “页面”(WPF) 模板。 输入名称 ExpenseItHome,然后选择“ 添加”。

    此页面是启动应用程序时显示的第一页。 它将显示要从中选择的人员列表,以显示费用报表。

  2. 打开 ExpenseItHome.xaml

  3. Title 设置为 “ExpenseIt - Home”。

  4. DesignHeight 设置为 350 像素和 DesignWidth 500 像素。

    现在,VISUAL Basic 的 XAML 如下所示:

    <Page x:Class="ExpenseItHome"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="350" d:DesignWidth="500"
      Title="ExpenseIt - Home">
        <Grid>
            
        </Grid>
    </Page>
    

    对于 C#,如下所示:

    <Page x:Class="ExpenseIt.ExpenseItHome"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          mc:Ignorable="d" 
          d:DesignHeight="350" d:DesignWidth="500"
        Title="ExpenseIt - Home">
    
        <Grid>
            
        </Grid>
    </Page>
    
  5. 打开 MainWindow.xaml

  6. 添加一个Source属性到NavigationWindow元素,并将其设置为“ExpenseItHome.xaml”。

    这设置为 ExpenseItHome.xaml 应用程序启动时打开的第一页。

    Visual Basic 中的示例 XAML:

    <NavigationWindow x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ExpenseIt" Height="350" Width="500" Source="ExpenseItHome.xaml">
        
    </NavigationWindow>
    

    在 C# 中:

    <NavigationWindow x:Class="ExpenseIt.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ExpenseIt" Height="350" Width="500" Source="ExpenseItHome.xaml">
        
    </NavigationWindow>
    

    小窍门

    还可以在“属性”窗口的“杂项”类别中设置“源”属性。

    “属性”窗口中的源属性

  7. 将另一个新的 WPF 页添加到项目,并将其命名为 ExpenseReportPage.xaml::

    1. 解决方案资源管理器中,右键单击 ExpenseIt 项目节点,然后选择“ 添加>页面”。

    2. 在“ 添加新项 ”对话框中,选择 “页面”(WPF) 模板。 输入名称 ExpenseReportPage,然后选择“ 添加”。

    此页面将显示页面上所选 ExpenseItHome 人员的支出报表。

  8. 打开 ExpenseReportPage.xaml

  9. Title 设置为 "ExpenseIt - View Expense"。

  10. DesignHeight 设置为 350 像素和 DesignWidth 500 像素。

    ExpenseReportPage.xaml 现在在 Visual Basic 中如下所示:

    <Page x:Class="ExpenseReportPage"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          mc:Ignorable="d" 
          d:DesignHeight="350" d:DesignWidth="500"
          Title="ExpenseIt - View Expense">
        <Grid>
            
        </Grid>
    </Page>
    

    在 C# 中如下所示:

    <Page x:Class="ExpenseIt.ExpenseReportPage"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          mc:Ignorable="d" 
          d:DesignHeight="350" d:DesignWidth="500"
        Title="ExpenseIt - View Expense">
    
        <Grid>
            
        </Grid>
    </Page>
    
  11. 打开 ExpenseItHome.xaml.vbExpenseReportPage.xaml.vb,或 ExpenseItHome.xaml.csExpenseReportPage.xaml.cs

    创建新的页面文件时,Visual Studio 会自动创建其 代码隐藏 文件。 这些代码隐藏文件负责处理响应用户输入的逻辑。

    您编写的代码应对于ExpenseItHome如下所示:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace ExpenseIt
    {
        /// <summary>
        /// Interaction logic for ExpenseItHome.xaml
        /// </summary>
        public partial class ExpenseItHome : Page
        {
            public ExpenseItHome()
            {
                InitializeComponent();
            }
        }
    }
    
    Class ExpenseItHome
    
    End Class
    

    ExpenseReportPage 的以下内容类似:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace ExpenseIt
    {
        /// <summary>
        /// Interaction logic for ExpenseReportPage.xaml
        /// </summary>
        public partial class ExpenseReportPage : Page
        {
            public ExpenseReportPage()
            {
                InitializeComponent();
            }
        }
    }
    
    Class ExpenseReportPage
    
    End Class
    
  12. 向项目添加名为 watermark.png 的图像。 可以创建自己的映像、从示例代码复制文件,或者从 microsoft/WPF-Samples GitHub 存储库获取该文件。

    1. 右键单击项目节点并选择“ 添加>现有项”,或按 Shift+Alt+A

    2. “添加现有项 ”对话框中,将文件筛选器设置为 “所有文件 ”或“ 图像文件”,浏览到要使用的图像文件,然后选择“ 添加”。

    3. 解决方案资源管理器中选择映像文件,然后在 “属性” 窗口中,将 “生成作 ”设置为 “资源”。

生成并运行应用程序

  1. 若要生成并运行应用程序,请按 F5 或从“调试”菜单中选择“开始调试”。

    下图显示了带有 NavigationWindow 按钮的应用程序:

    生成并运行后的应用程序。

  2. 关闭应用程序以返回到 Visual Studio。

创建布局

布局提供了放置 UI 元素的有序方法,并在调整 UI 大小时管理这些元素的大小和位置。 通常使用以下布局控件之一创建布局:

  • Canvas - 定义一个区域,您可以使用相对于 Canvas 区域的坐标来显式定位该区域内的子元素。
  • DockPanel - 定义一个区域,可在其中水平或垂直排列子元素,彼此相对。
  • Grid - 定义由列和行组成的灵活网格区域。
  • StackPanel - 将子元素排列成可水平或垂直方向的单个线条。
  • VirtualizingStackPanel - 在水平或垂直方向的单个行上排列和虚拟化内容。
  • WrapPanel - 将子元素放在从左到右的顺序位置,将内容中断到包含框边缘的下一行。 后续排序按顺序从上到下或从右到左的顺序进行,具体取决于 Orientation 属性的值。

其中每个布局控件都为其子元素支持特定类型的布局。 ExpenseIt 可以调整页面大小,并且每个页面都具有与其他元素一起水平和垂直排列的元素。 在此示例中,Grid 被用作应用程序的布局元素。

小窍门

有关元素的详细信息 Panel ,请参阅 面板概述。 有关布局的详细信息,请参阅 布局

在本部分中,通过将列和行定义添加到 Grid in ExpenseItHome.xaml,创建包含三行和 10 像素边距的单列表。

  1. ExpenseItHome.xaml中,将MarginGrid元素上的属性设置为“10,0,10,10”,对应于左、上、右和下边距:

    <Grid Margin="10,0,10,10">
    

    小窍门

    还可以在“属性”窗口中在“布局”类别下设置边距值:

    “属性”窗口中的边距值

  2. Grid 标记之间添加以下 XAML 来定义行和列:

    <Grid.ColumnDefinitions>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    

    Height的两行设置为Auto,这意味着根据其内容调整行的大小。 默认值 HeightStar 大小调整,这意味着行高是可用空间的加权比例。 例如,如果两行各有一个 Height “*”,则每个行的高度为可用空间的一半。

    您的 Grid 现在应包含以下 XAML:

    <Grid Margin="10,0,10,10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
    </Grid>
    

添加控件

在本部分中,你将更新主页的 UI 以显示人物列表,并在其中选择一个人来显示该人的费用报表。 控件是允许用户与应用程序交互的 UI 对象。 有关详细信息,请参阅 控件

若要创建此 UI,需要将以下元素添加到 ExpenseItHome.xaml

  • A ListBox (适用于人员列表)。
  • A Label (对于列表标头)。
  • Button (单击以查看列表中所选人员的支出报表)。

Grid的某一行中通过设置Grid.Row附加属性放置每个控件。 有关附加属性的详细信息,请参阅 “附加属性概述”。

  1. Grid 标签之间的某处 ExpenseItHome.xaml,添加以下 XAML:

    
    <!-- People list -->
    <Border Grid.Column="0" Grid.Row="0" Height="35" Padding="5" Background="#4E87D4">
        <Label VerticalAlignment="Center" Foreground="White">Names</Label>
    </Border>
    <ListBox Name="peopleListBox" Grid.Column="0" Grid.Row="1">
        <ListBoxItem>Mike</ListBoxItem>
        <ListBoxItem>Lisa</ListBoxItem>
        <ListBoxItem>John</ListBoxItem>
        <ListBoxItem>Mary</ListBoxItem>
    </ListBox>
    
    <!-- View report button -->
    <Button Grid.Column="0" Grid.Row="2" Margin="0,10,0,10" Width="125" Height="25" HorizontalAlignment="Right">View</Button>
    

    小窍门

    还可以通过将控件从 工具箱 窗口拖动到设计窗口,然后在 “属性” 窗口中设置控件的属性来创建控件。

  2. 生成并运行应用程序。

    下图显示了创建的控件:

显示名称列表的 ExpenseIt 示例屏幕截图

添加图像和标题

在本部分中,你将使用图像和页面标题更新主页 UI。

  1. ExpenseItHome.xaml 中,将固定宽度为 230 像素的另一列添加到 ColumnDefinitions 中。

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="230" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    
  2. 将另一行添加到 RowDefinitions,共四行:

    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    
  3. 通过在三个控件(Border、ListBox 和 Button)中的每一个控件中将 `Grid.Column` 属性设置为 1,把控件移动到第二列。

  4. 将每个控件(边框、ListBox 和 Button)及 Border 元素的 Grid.Row 值递增 1,从而使每个控件下移一行。

    三个控件的 XAML 现在如下所示:

      <Border Grid.Column="1" Grid.Row="1" Height="35" Padding="5" Background="#4E87D4">
          <Label VerticalAlignment="Center" Foreground="White">Names</Label>
      </Border>
      <ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2">
          <ListBoxItem>Mike</ListBoxItem>
          <ListBoxItem>Lisa</ListBoxItem>
          <ListBoxItem>John</ListBoxItem>
          <ListBoxItem>Mary</ListBoxItem>
      </ListBox>
    
      <!-- View report button -->
      <Button Grid.Column="1" Grid.Row="3" Margin="0,10,0,0" Width="125"
    Height="25" HorizontalAlignment="Right">View</Button>
    
  5. 将以下 XAML 添加到 <Grid></Grid> 标签之间的任意位置,以将 Background 属性设置为 watermark.png 图像文件:

    <Grid.Background>
        <ImageBrush ImageSource="watermark.png"/>
    </Grid.Background>
    
  6. Border 元素之前,添加 Label 内容为“查看费用报表”。 此标签是页面的标题。

    <Label Grid.Column="1" VerticalAlignment="Center" FontFamily="Trebuchet MS" 
            FontWeight="Bold" FontSize="18" Foreground="#0066cc">
        View Expense Report
    </Label>
    
  7. 生成并运行应用程序。

下图显示了刚刚添加的内容的结果:

ExpenseIt 示例屏幕截图,其中显示了新的图像背景和页面标题

添加代码以处理事件

  1. ExpenseItHome.xaml中,向 Click 元素添加事件处理程序 Button 。 有关详细信息,请参阅 如何:创建简单的事件处理程序

      <!-- View report button -->
      <Button Grid.Column="1" Grid.Row="3" Margin="0,10,0,0" Width="125"
    Height="25" HorizontalAlignment="Right" Click="Button_Click">View</Button>
    
  2. 打开 ExpenseItHome.xaml.vbExpenseItHome.xaml.cs

  3. 将以下代码添加到 ExpenseItHome 类,以添加按钮单击事件处理程序。 事件处理程序将打开 ExpenseReportPage 页。

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // View Expense Report
        ExpenseReportPage expenseReportPage = new ExpenseReportPage();
        this.NavigationService.Navigate(expenseReportPage);
    }
    
    Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' View Expense Report
        Dim expenseReportPage As New ExpenseReportPage()
        Me.NavigationService.Navigate(expenseReportPage)
    
    End Sub
    

创建 ExpenseReportPage 的 UI

ExpenseReportPage.xaml 显示页面上所选 ExpenseItHome 人员的支出报表。 在本部分中,你将为 ExpenseReportPage 创建 UI。 你还将向各种 UI 元素添加背景和填充颜色。

  1. 打开 ExpenseReportPage.xaml

  2. Grid标签之间添加以下XAML:

     <Grid.Background>
         <ImageBrush ImageSource="watermark.png" />
     </Grid.Background>
     <Grid.ColumnDefinitions>
         <ColumnDefinition Width="230" />
         <ColumnDefinition />
     </Grid.ColumnDefinitions>
     <Grid.RowDefinitions>
         <RowDefinition Height="Auto" />
         <RowDefinition />
     </Grid.RowDefinitions>
    
    
     <Label Grid.Column="1" VerticalAlignment="Center" FontFamily="Trebuchet MS" 
     FontWeight="Bold" FontSize="18" Foreground="#0066cc">
         Expense Report For:
     </Label>
     <Grid Margin="10" Grid.Column="1" Grid.Row="1">
    
         <Grid.ColumnDefinitions>
             <ColumnDefinition />
             <ColumnDefinition />
         </Grid.ColumnDefinitions>
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto" />
             <RowDefinition Height="Auto" />
             <RowDefinition />
         </Grid.RowDefinitions>
    
         <!-- Name -->
         <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Orientation="Horizontal">
             <Label Margin="0,0,0,5" FontWeight="Bold">Name:</Label>
             <Label Margin="0,0,0,5" FontWeight="Bold"></Label>
         </StackPanel>
    
         <!-- Department -->
         <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Orientation="Horizontal">
             <Label Margin="0,0,0,5" FontWeight="Bold">Department:</Label>
             <Label Margin="0,0,0,5" FontWeight="Bold"></Label>
         </StackPanel>
    
         <Grid Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" VerticalAlignment="Top" 
               HorizontalAlignment="Left">
             <!-- Expense type and Amount table -->
             <DataGrid  AutoGenerateColumns="False" RowHeaderWidth="0" >
                 <DataGrid.ColumnHeaderStyle>
                     <Style TargetType="{x:Type DataGridColumnHeader}">
                         <Setter Property="Height" Value="35" />
                         <Setter Property="Padding" Value="5" />
                         <Setter Property="Background" Value="#4E87D4" />
                         <Setter Property="Foreground" Value="White" />
                     </Style>
                 </DataGrid.ColumnHeaderStyle>
                 <DataGrid.Columns>
                     <DataGridTextColumn Header="ExpenseType" />
                     <DataGridTextColumn Header="Amount"  />
                 </DataGrid.Columns>
             </DataGrid>
         </Grid>
     </Grid>
    

    此界面类似于 ExpenseItHome.xaml,但报表数据显示在一个 DataGrid中。

  3. 生成并运行应用程序。

  4. 选择 “视图 ”按钮。

    费用报告页面出现了。 另请注意,已启用后退导航按钮。

下图显示了添加到 ExpenseReportPage.xaml 的 UI 元素。

ExpenseIt 示例屏幕截图,其中显示了刚刚为 ExpenseReportPage 创建的 UI。

样式控件

对于 UI 中具有相同类型的所有元素,各种元素的外观通常相同。 UI 使用 样式 使外观可跨多个元素重复使用。 样式的可重用性有助于简化 XAML 的创建和管理。 本部分将前面步骤中定义的每元素属性替换为样式。

  1. 打开 Application.xamlApp.xaml

  2. Application.Resources 标签之间添加以下 XAML:

    
    <!-- Header text style -->
    <Style x:Key="headerTextStyle">
        <Setter Property="Label.VerticalAlignment" Value="Center"></Setter>
        <Setter Property="Label.FontFamily" Value="Trebuchet MS"></Setter>
        <Setter Property="Label.FontWeight" Value="Bold"></Setter>
        <Setter Property="Label.FontSize" Value="18"></Setter>
        <Setter Property="Label.Foreground" Value="#0066cc"></Setter>
    </Style>
    
    <!-- Label style -->
    <Style x:Key="labelStyle" TargetType="{x:Type Label}">
        <Setter Property="VerticalAlignment" Value="Top" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Margin" Value="0,0,0,5" />
    </Style>
    
    <!-- DataGrid header style -->
    <Style x:Key="columnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5" />
        <Setter Property="Background" Value="#4E87D4" />
        <Setter Property="Foreground" Value="White" />
    </Style>
    
    <!-- List header style -->
    <Style x:Key="listHeaderStyle" TargetType="{x:Type Border}">
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5" />
        <Setter Property="Background" Value="#4E87D4" />
    </Style>
    
    <!-- List header text style -->
    <Style x:Key="listHeaderTextStyle" TargetType="{x:Type Label}">
        <Setter Property="Foreground" Value="White" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Left" />
    </Style>
    
    <!-- Button style -->
    <Style x:Key="buttonStyle" TargetType="{x:Type Button}">
        <Setter Property="Width" Value="125" />
        <Setter Property="Height" Value="25" />
        <Setter Property="Margin" Value="0,10,0,0" />
        <Setter Property="HorizontalAlignment" Value="Right" />
    </Style>
    

    此 XAML 添加以下样式:

    • headerTextStyle:设置页面标题 Label的格式。

    • labelStyle:为Label控件设置格式。

    • columnHeaderStyle:将DataGridColumnHeader格式化。

    • listHeaderStyle:设置列表标头 Border 控件的格式。

    • listHeaderTextStyle:设置列表标头 Label的格式。

    • buttonStyle:在ExpenseItHome.xaml上格式化Button

    请注意,样式是属性元素的资源和子 Application.Resources 元素。 在此位置,样式将应用于应用程序中的所有元素。 有关在 .NET 应用中使用资源的示例,请参阅 “使用应用程序资源”。

  3. ExpenseItHome.xaml 中,将 Grid 元素之间的所有内容替换为以下 XAML:

       <Grid.Background>
           <ImageBrush ImageSource="watermark.png"  />
       </Grid.Background>
      
       <Grid.ColumnDefinitions>
           <ColumnDefinition Width="230" />
           <ColumnDefinition />
       </Grid.ColumnDefinitions>
       
       <Grid.RowDefinitions>
           <RowDefinition/>
           <RowDefinition Height="Auto"/>
           <RowDefinition />
           <RowDefinition Height="Auto"/>
       </Grid.RowDefinitions>
    
       <!-- People list -->
      
       <Label Grid.Column="1" Style="{StaticResource headerTextStyle}" >
           View Expense Report
       </Label>
       
       <Border Grid.Column="1" Grid.Row="1" Style="{StaticResource listHeaderStyle}">
           <Label Style="{StaticResource listHeaderTextStyle}">Names</Label>
       </Border>
       <ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2">
           <ListBoxItem>Mike</ListBoxItem>
           <ListBoxItem>Lisa</ListBoxItem>
           <ListBoxItem>John</ListBoxItem>
           <ListBoxItem>Mary</ListBoxItem>
       </ListBox>
    
       <!-- View report button -->
       <Button Grid.Column="1" Grid.Row="3" Click="Button_Click" Style="{StaticResource buttonStyle}">View</Button>
    

    定义每个控件外观的属性,如VerticalAlignmentFontFamily,已被删除,并通过应用样式进行替换。 例如,headerTextStyle是应用于“查看费用报表”Label

  4. 打开 ExpenseReportPage.xaml

  5. Grid 元素之间的所有内容替换为以下 XAML:

      <Grid.Background>
          <ImageBrush ImageSource="watermark.png" />
      </Grid.Background>
      <Grid.ColumnDefinitions>
          <ColumnDefinition Width="230" />
          <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition />
      </Grid.RowDefinitions>
    
    
      <Label Grid.Column="1" Style="{StaticResource headerTextStyle}">
          Expense Report For:
      </Label>
      <Grid Margin="10" Grid.Column="1" Grid.Row="1">
    
          <Grid.ColumnDefinitions>
              <ColumnDefinition />
              <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition Height="Auto" />
              <RowDefinition />
          </Grid.RowDefinitions>
    
          <!-- Name -->
          <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Orientation="Horizontal">
              <Label Style="{StaticResource labelStyle}">Name:</Label>
              <Label Style="{StaticResource labelStyle}"></Label>
          </StackPanel>
    
          <!-- Department -->
          <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" 
      Orientation="Horizontal">
              <Label Style="{StaticResource labelStyle}">Department:</Label>
              <Label Style="{StaticResource labelStyle}"></Label>
          </StackPanel>
    
          <Grid Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" VerticalAlignment="Top" 
                HorizontalAlignment="Left">
              <!-- Expense type and Amount table -->
              <DataGrid ColumnHeaderStyle="{StaticResource columnHeaderStyle}" 
                        AutoGenerateColumns="False" RowHeaderWidth="0" >
                  <DataGrid.Columns>
                      <DataGridTextColumn Header="ExpenseType" />
                      <DataGridTextColumn Header="Amount"  />
                  </DataGrid.Columns>
              </DataGrid>
          </Grid>
      </Grid>
    

    此 XAML 向元素LabelBorder添加样式。

  6. 生成并运行应用程序。 窗口外观与之前的外观相同。

    ExpenseIt 示例屏幕截图,其外观与上一部分的外观相同。

  7. 关闭应用程序以返回到 Visual Studio。

将数据绑定到控件

在本部分中,你将创建绑定到各种控件的 XML 数据。

  1. ExpenseItHome.xaml 开始 Grid 元素之后,添加以下 XAML 以创建一个包含每个人数据的 XmlDataProvider

    <Grid.Resources>
        <!-- Expense Report Data -->
        <XmlDataProvider x:Key="ExpenseDataSource" XPath="Expenses">
            <x:XData>
                <Expenses xmlns="">
                    <Person Name="Mike" Department="Legal">
                        <Expense ExpenseType="Lunch" ExpenseAmount="50" />
                        <Expense ExpenseType="Transportation" ExpenseAmount="50" />
                    </Person>
                    <Person Name="Lisa" Department="Marketing">
                        <Expense ExpenseType="Document printing"
              ExpenseAmount="50"/>
                        <Expense ExpenseType="Gift" ExpenseAmount="125" />
                    </Person>
                    <Person Name="John" Department="Engineering">
                        <Expense ExpenseType="Magazine subscription" 
             ExpenseAmount="50"/>
                        <Expense ExpenseType="New machine" ExpenseAmount="600" />
                        <Expense ExpenseType="Software" ExpenseAmount="500" />
                    </Person>
                    <Person Name="Mary" Department="Finance">
                        <Expense ExpenseType="Dinner" ExpenseAmount="100" />
                    </Person>
                </Expenses>
            </x:XData>
        </XmlDataProvider>
    </Grid.Resources>
    

    数据作为 Grid 资源创建。 通常,此数据将作为文件加载,但为简单起见,数据将内联添加。

  2. <Grid.Resources>元素中添加以下<xref:System.Windows.DataTemplate>元素,该元素定义如何在<XmlDataProvider>元素之后显示ListBox中的数据。

    <Grid.Resources>
        <!-- Name item template -->
        <DataTemplate x:Key="nameItemTemplate">
            <Label Content="{Binding XPath=@Name}"/>
        </DataTemplate>
    </Grid.Resources>
    

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

  3. 将现有 ListBox 代码替换为以下 XAML:

    <ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2" 
             ItemsSource="{Binding Source={StaticResource ExpenseDataSource}, XPath=Person}"
             ItemTemplate="{StaticResource nameItemTemplate}">
    </ListBox>
    

    此 XAML 将 ListBox 的属性 ItemsSource 绑定到数据源,并将数据模板应用于作为 ItemTemplate

将数据连接到控件

接下来,将添加代码以检索在 ExpenseItHome 页上选择的名称,并将其传递给 ExpenseReportPage 的构造函数。 ExpenseReportPage 使用传递的项设置其数据上下文,这是 ExpenseReportPage.xaml 中定义的控件绑定到的内容。

  1. 打开 ExpenseReportPage.xaml.vbExpenseReportPage.xaml.cs

  2. 添加采用对象的构造函数,以便传递所选人员的支出报表数据。

    public partial class ExpenseReportPage : Page
    {
        public ExpenseReportPage()
        {
            InitializeComponent();
        }
    
        // Custom constructor to pass expense report data
        public ExpenseReportPage(object data):this()
        {
            // Bind to expense report data.
            this.DataContext = data;
        }
    }
    
    Partial Public Class ExpenseReportPage
        Inherits Page
        Public Sub New()
            InitializeComponent()
        End Sub
    
        ' Custom constructor to pass expense report data
        Public Sub New(ByVal data As Object)
            Me.New()
            ' Bind to expense report data.
            Me.DataContext = data
        End Sub
    
    End Class
    
  3. 打开 ExpenseItHome.xaml.vbExpenseItHome.xaml.cs

  4. 更改 Click 事件处理程序以调用传递所选人员的支出报表数据的新构造函数。

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // View Expense Report
        ExpenseReportPage expenseReportPage = new ExpenseReportPage(this.peopleListBox.SelectedItem);
        this.NavigationService.Navigate(expenseReportPage);
    }
    
    Private Sub Button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' View Expense Report
        Dim expenseReportPage As New ExpenseReportPage(Me.peopleListBox.SelectedItem)
        Me.NavigationService.Navigate(expenseReportPage)
    
    End Sub
    

使用数据模板设置数据样式

在本部分中,你将使用数据模板更新数据绑定列表中每个项的 UI。

  1. 打开 ExpenseReportPage.xaml

  2. 将“Name”和“Department” Label 元素的内容绑定到相应的数据源属性。 有关数据绑定的详细信息,请参阅 数据绑定概述

    <!-- Name -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Orientation="Horizontal">
        <Label Style="{StaticResource labelStyle}">Name:</Label>
        <Label Style="{StaticResource labelStyle}" Content="{Binding XPath=@Name}"></Label>
    </StackPanel>
    
    <!-- Department -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Orientation="Horizontal">
        <Label Style="{StaticResource labelStyle}">Department:</Label>
        <Label Style="{StaticResource labelStyle}" Content="{Binding XPath=@Department}"></Label>
    </StackPanel>
    
  3. 打开 Grid 元素后,添加以下数据模板,该模板定义如何显示费用报表数据:

    <!--Templates to display expense report data-->
    <Grid.Resources>
        <!-- Reason item template -->
        <DataTemplate x:Key="typeItemTemplate">
            <Label Content="{Binding XPath=@ExpenseType}"/>
        </DataTemplate>
        <!-- Amount item template -->
        <DataTemplate x:Key="amountItemTemplate">
            <Label Content="{Binding XPath=@ExpenseAmount}"/>
        </DataTemplate>
    </Grid.Resources>
    
  4. DataGridTextColumn 元素替换为 DataGridTemplateColumn 元素下 DataGrid ,并向其应用模板。 此外,请在 DataGrid 元素中指定 ItemsSource 属性及其值。

    <!-- Expense type and Amount table -->
    <DataGrid ItemsSource="{Binding XPath=Expense}" ColumnHeaderStyle="{StaticResource columnHeaderStyle}" AutoGenerateColumns="False" RowHeaderWidth="0" >
       
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="ExpenseType" CellTemplate="{StaticResource typeItemTemplate}" />
            <DataGridTemplateColumn Header="Amount" CellTemplate="{StaticResource amountItemTemplate}" />
        </DataGrid.Columns>
        
    </DataGrid>
    
  5. 生成并运行应用程序。

  6. 选择人员,然后选择 “视图 ”按钮。

下图显示了应用程序的两个页面 ExpenseIt ,其中应用了控件、布局、样式、数据绑定和数据模板:

注释

此示例演示了 WPF 的特定功能,并不遵循安全、本地化和辅助功能等所有最佳做法。 有关 WPF 和 .NET 应用程序开发最佳实践的详细介绍,请参考以下主题:

后续步骤

在本演练中,你学习了许多使用 Windows Presentation Foundation (WPF) 创建 UI 的技术。 现在,你应该对数据绑定的 .NET 应用的构建基块有基本的了解。 有关 WPF 体系结构和编程模型的详细信息,请参阅以下主题:

有关创建应用程序的详细信息,请参阅以下主题:

另请参阅