Windows Presentation Foundation数据绑定:第 1 部分

 

Shawn Wildermuth

2006 年 5 月

适用于:
   Microsoft Windows Presentation Foundation

总结:说明如何使用基于 XAML 的数据绑定在 Microsoft Windows Presentation Foundation 项目中执行数据操作。 (16 个打印页)

目录

简介
XAML 绑定,简化
我们在哪?
参考

简介

Microsoft Windows Presentation Foundation (WPF;以前为 Avalon) 引入了一种为丰富客户端开发用户界面的深刻新方法。 WPF 首次将用户界面的设计与代码分开。 这种分离意味着,与 ASP.NET 非常类似,标记通常位于一个文件中,而代码位于另一个文件中。 不过,这种分离仅在编译时存在。 标记文件用于生成与代码文件结婚的代码,以便生成应用程序。

为了便于设计,Microsoft 开发了一种称为 XAML 的丰富标记语言。 XAML 是一种基于 XML 的标记语言,它支持用于开发对多种不同用户界面概念(如二维和三维绘图、动画、控件包含、控件和文档流以及丰富的数据绑定模型)具有本机支持的应用程序的新模型。 本文将概述 WPF 数据绑定。 本文假设你对 WPF 有一些粗略的了解。 如果不这样做,请参阅 Time Sneath's Hitchhiker 的 WPF Beta 1 指南 一文以获取概述。

为何使用数据绑定?

如果你开始使用 WPF,你可能想知道是否更容易避免学习数据绑定,而只是编写代码来执行项目中的大部分数据操作。 虽然这可能是一种有效的方法,但我怀疑你会逐渐使用,甚至喜欢基于 XAML 的数据绑定。 让我们以一个小示例为例。

图 1 显示了简单 WPF 项目的用户界面。 它是 RSS 源的编辑器,允许用户查看和编辑源。

单击此处查看较大的图像

图 1. RSS 编辑器 (单击图像以获取更大的图片)

编辑器的布局非常简单,如下面的 XAML 代码所示。

<Window x:Class="ExampleCS.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="ExampleCS"
    Loaded="Window1_Loaded"
    >
  <StackPanel>
    <TextBlock HorizontalAlignment="Center" 
          FontWeight="Bold">
      BlogEditor
    </TextBlock>
    <StackPanel Orientation="Horizontal" 
           HorizontalAlignment="Center">
      <ListBox Name="entryListBox" 
          Height="300" 
          SelectionChanged="entryListBox_Changed"/>
      <Grid Width="500" Margin="5">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="50" />
          <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
          <RowDefinition Height="25" />
          <RowDefinition Height="25" />
          <RowDefinition Height="25" />
          <RowDefinition Height="*" />
          <RowDefinition Height="25" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
        <TextBox Grid.Row="0" Grid.Column="1" Name="titleText" />
        <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
        <TextBox Grid.Row="1" Grid.Column="1" Name="urlText" />
        <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
        <TextBox Grid.Row="2" Grid.Column="1" Name="dateText" />
        <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
        <TextBox Grid.Row="3" Grid.Column="1" 
            Name="bodyText" 
            TextWrapping="Wrap" />
        <Button Grid.Row="4" 
                  Grid.ColumnSpan="2" 
                  Grid.Column="1"
            Click="updateButton_Click">
          Update
        </Button>
      </Grid>
    </StackPanel>
  </StackPanel>
</Window>

记下粗体事件处理程序。 这就是大多数代码将发生的位置,即加载 RSS 源并填充 WPF 控件。

C#

XmlDocument blog = new XmlDocument();
const string BLOGURL = @"z:\adoguy.RSS";

public Window1()
{
  InitializeComponent();
  blog.Load(BLOGURL);
}

void Window1_Loaded(object sender, RoutedEventArgs e) 
{
  FillListBox();
}

void FillListBox()
{
  entryListBox.Items.Clear();

  XmlNodeList nodes = blog.SelectNodes("//item");
  foreach (XmlNode node in nodes)
  {
    ListBoxItem item = new ListBoxItem();
    item.Tag = node;
    item.Content = node["title"].InnerText;
    entryListBox.Items.Add(item);
  }
}

Visual Basic .NET

  Dim blog As New XmlDocument
  Const BLOGURL As String = "z:\adoguy.RSS"

  Public Sub New()
    InitializeComponent()
    blog.Load(BLOGURL)
  End Sub

  Sub Window1_Loaded(ByVal sender As Object, _
                     ByVal e As RoutedEventArgs) 
    FillListBox()

  End Sub

  Sub FillListBox()

    entryListBox.Items.Clear()

    Dim nodes As XmlNodeList = blog.SelectNodes("//item")

    For Each node As XmlNode In nodes

      Dim item As New ListBoxItem
      item.Tag = node
      item.Content = node("title").InnerText
      entryListBox.Items.Add(item)

    Next

  End Sub

在此代码中可以看到,我们正在加载带有 RSS 源的 XML 文档,并在 ListBox 中填写所有条目的标题。

接下来,我们需要处理在 ListBox 中做出选择时发生的情况。

C#

void entryListBox_Changed(object sender, RoutedEventArgs e)
{
  if (entryListBox.SelectedIndex != -1)
  {
    XmlNode node = ((ListBoxItem)entryListBox.SelectedItem).Tag as XmlNode;
    if (node != null)
    {
      titleText.Text = node["title"].InnerText;
      urlText.Text = node["guid"].InnerText;
      dateText.Text = node["pubDate"].InnerText;
      bodyText.Text = node["description"].InnerText;
    }
  }
}

Visual Basic .NET

  Sub entryListBox_Changed(ByVal sender As Object, _
                           ByVal e As SelectionChangedEventArgs) 

    If entryListBox.SelectedIndex <> -1 Then

      Dim node As XmlNode = CType(entryListBox.SelectedItem, ListBoxItem).Tag
      If Not node Is Nothing Then

        titleText.Text = node("title").InnerText
        urlText.Text = node("guid").InnerText
        dateText.Text = node("pubDate").InnerText
        bodyText.Text = node("description").InnerText

      End If

    End If

  End Sub

更改 ListBox 时,我们会使用所选 RSS 源项填充 TextBox 。 我们通过获取源项的内部文本来执行此操作。 请注意,它还要求在 ListBoxItem 周围保留节点的副本,并执行一些强制转换来获取每个事件处理程序。

最后,我们需要处理单击“ 更新 ”按钮时发生的情况。

C#

void updateButton_Click(object sender, RoutedEventArgs e)
{
  if (entryListBox.SelectedIndex != -1)
  {
    XmlNode node = ((ListBoxItem)entryListBox.SelectedItem).Tag as XmlNode;
    if (node != null)
    {
      node["title"].InnerText = titleText.Text;
      node["guid"].InnerText = urlText.Text;
      node["pubDate"].InnerText = dateText.Text;
      node["description"].InnerText = bodyText.Text;

      blog.Save(BLOGURL);

      FillListBox();
    }
  }
}

Visual Basic .NET

  Sub updateButton_Click(ByVal sender As Object, _
                         ByVal e As RoutedEventArgs)

    If entryListBox.SelectedIndex <> -1 Then

      Dim node As XmlNode = CType(entryListBox.SelectedItem, ListBoxItem).Tag

      If Not node Is Nothing Then

        node("title").InnerText = titleText.Text
        node("guid").InnerText = urlText.Text
        node("pubDate").InnerText = dateText.Text
        node("description").InnerText = bodyText.Text

        blog.Save(BLOGURL)

        FillListBox()

      End If

    End If

  End Sub

在这里,我们使用新信息更新节点,保存 XML 文档,并在标题发生更改时重新填充 ListBox 。 虽然这是一个简单的小应用程序,但所需的代码有点广泛。 使用 XAML 数据绑定可以更轻松地执行此操作吗? 是的,可能是。 下面是使用 XAML 数据绑定的同一项目的 XAML。

<StackPanel 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="RssFeed" Source="z:\adoguy.RSS" />
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
      FontWeight="Bold">
    Blog Editor
  </TextBlock>
  <StackPanel Orientation="Horizontal" 
    HorizontalAlignment="Center">
    <ListBox Name="entryListBox" 
        Height="300" 
        ItemsSource="{Binding Source={StaticResource RssFeed}, XPath=//item}"
    >
      <ListBox.ItemTemplate>
        <DataTemplate>
          <TextBlock Text="{Binding XPath=title}" />
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
    <Grid Width="500" 
        Margin="5" 
        DataContext="{Binding ElementName=entryListBox, Path=SelectedItem}" >
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50" />
        <ColumnDefinition Width="*" />
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition Height="25" />
        <RowDefinition Height="25" />
        <RowDefinition Height="25" />
        <RowDefinition Height="*" />
        <RowDefinition Height="25" />
      </Grid.RowDefinitions>
      <TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
      <TextBox Grid.Row="0" Grid.Column="1" 
          Name="titleText" 
          Text="{Binding XPath=title}" />
      <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
      <TextBox Grid.Row="1" Grid.Column="1" 
          Name="urlText" 
          Text="{Binding XPath=guid}" />
      <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
      <TextBox Grid.Row="2" Grid.Column="1" 
          Name="dateText" 
          Text="{Binding XPath=pubDate}" />
      <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
      <TextBox Grid.Row="3" Grid.Column="1" 
          Name="bodyText" 
          TextWrapping="Wrap" 
          Text="{Binding XPath=description}" />
      <Button Grid.Row="4" Grid.ColumnSpan="2" Grid.Column="1" >
          Update
      </Button>
    </Grid>
  </StackPanel>
</StackPanel>

此 XAML 无需任何代码隐藏即可工作。 图 2 显示了仅使用 XAMLPad) 的 XAML (的应用程序。

单击此处查看较大的图像

图 2. XAMLPad 中的 RSS 编辑器 (单击图像以获取较大的图片)

你可能想知道,“这怎么能起作用?”它之所以有效,是因为我们使用 XAML 绑定为我们完成大部分工作。 尽管稍后会详细介绍 XAML 中的每一种方法,但让我们通过数据绑定的各个部分来解释其工作原理。

首先,我们创建一个 XmlDataProvider 对象,用于加载和管理编辑器的 XML 文档。

<XmlDataProvider x:Key="RssFeed" Source="z:\adoguy.RSS" />

这会告知 XAML 从我的 Z: 驱动器加载 XML 文档,以便填写表单。

接下来,绑定 ListBox

<ListBox Name="entryListBox" 
        Height="300" 
        ItemsSource="{Binding Source={StaticResource RssFeed}, XPath=//item}"
    >

这会告知列表框在 XAML 中查找名为 RssFeed 的资源,并将其用作数据源。 此外,它告知列表框从 XPath 表达式 (获取要绑定到的项列表,在这种情况下,文档中的所有项元素) 。

接下来,通过指定数据模板来指定要在列表框的项中显示的内容。

<DataTemplate>
  <TextBlock Text="{Binding XPath=title}" />
</DataTemplate>

这会告知 ListBox 为每个项目创建一个 TextBlock 对象,并使用 XPath 确定要显示在 TextBlock 中的内容。

接下来,绑定包含所有详细信息的网格。

<Grid Width="500" 
        Margin="5" 
        DataContext="{Binding ElementName=entryListBox, Path=SelectedItem}" >

在这里,我们告诉 XAML 将容器 (在本例中 ,网格) 绑定到 ListBox 的选定项。 我们使用 ElementName 而不是 Source,因为我们想要绑定到 XAML 文档中的控件,而不是某些外部数据。 通过设置 DataContext,我们允许 将 Grid 中的所有控件设置为同一对象 (在本例中为 SelectedItem) 。

接下来,我们将每个 TextBox 绑定到所需的 XML 文档部分。

<TextBlock Grid.Row="0" Grid.Column="0">Title:</TextBlock>
      <TextBox Grid.Row="0" Grid.Column="1" 
          Name="titleText" 
          Text="{Binding XPath=title}" />
      <TextBlock Grid.Row="1" Grid.Column="0">Url:</TextBlock>
      <TextBox Grid.Row="1" Grid.Column="1" 
          Name="urlText" 
          Text="{Binding XPath=guid}" />
      <TextBlock Grid.Row="2" Grid.Column="0">Date:</TextBlock>
      <TextBox Grid.Row="2" Grid.Column="1" 
          Name="dateText" 
          Text="{Binding XPath=pubDate}" />
      <TextBlock Grid.Row="3" Grid.Column="0">Body:</TextBlock>
      <TextBox Grid.Row="3" Grid.Column="1" 
          Name="bodyText" 
          TextWrapping="Wrap" 
          Text="{Binding XPath=description}" />

由于我们已经设置了 DataContext,因此只需指定 XPath 表达式即可访问所需的 RSS 源部分。

这是相当多的尝试吞咽在一个口。 停下来喘口气。 我没想到你一下子就拿到了。 在下一部分中,我已将你在上一示例中看到的许多想法分解为更易于管理的内容。

XAML 绑定,简化

若要从 Binding 对象工作原理的简单示例开始,让我们看一下以下非常简单的 XAML 文档。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Text="This is a TextBox" />
    <TextBlock Canvas.Top="25" >Test</TextBlock>
  </Canvas>
</Window>

这将创建一个包含两个控件的简单画布,如图 3 所示。

Aa480224.wpfdatabinding_pt103 (en-us,MSDN.10) .gif

图 3. 简单的 XAML 画布

我们可能需要绑定 TextBlock 以在键入 时显示 TextBox 的文本。 为此,我们需要一个 Binding 对象来将两个对象绑定在一起。 首先要做的是向 TextBox 添加一个名称,以便我们可以按元素的名称来引用它。

<TextBox Name="theTextBox" />

接下来,我们需要将 Binding 添加到 TextBlockText 元素。

<TextBlock Canvas.Top="25">
  <TextBlock.Text>    <Binding ElementName="theTextBox" Path="Text" />  </TextBlock.Text>
</TextBlock>

这会告知 TextBlockText 设置为当用户在 TextBox 控件中键入时。

将所有内容组合在一起会生成以下代码。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBlock Canvas.Top="25">
      <TextBlock.Text>
        <Binding ElementName="theTextBox" Path="Text" />
      </TextBlock.Text>
    </TextBlock>
  </Canvas>
</Window>

此 XAML 创建一个窗体,该窗体会在你在 TextBox 中键入时更改 TextBlock,如图 4 所示。

Aa480224.wpfdatabinding_pt104 (en-us,MSDN.10) .gif

图 4。 绑定控件

恭喜:你拥有了第一个绑定! 但 XML 语法有点冗长。 应该有更好的方法来编写它。

使用绑定速记

在前面的示例中,我们了解了如何通过将 Binding 元素添加到属性来创建数据 绑定 。 在这个简单的示例中,以这种方式进行数据绑定似乎并不太繁重;但是,随着 XAML 文档的增长,此详细语法可能会变得繁琐。 为了缓解此问题,XAML 支持绑定语法的简写版本。

例如,使用简写,前面的示例如下所示。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBlock Canvas.Top="25"
               Text="{Binding ElementName=theTextBox, Path=Text}" />
  </Canvas>
</Window>

简写语法括在) ({} 大括号中,以单词 Binding 开头,并包含绑定属性的名称/值对。 简写语法的目的是通过更少的击键和更高的可读性启用数据绑定。 对于本文的其余部分,我将使用速记语法。

绑定源

至此,我们已使用另一个控件作为所有绑定示例的源。 但是,在大多数基于 XAML 的项目中,你将绑定到其他控件以外的源。 大多数数据绑定的关键是 Source 属性。 在前面的示例中,我们使用 ElementName 属性,该属性用于绑定到控件,而不是使用 Source 属性。 对于大多数应用程序,需要绑定到更重要的源,例如 XML 或 .NET 对象。 XAML 使用其数据 提供程序 对象支持此功能。 XAML 中内置了两种类型的数据提供程序: ObjectDataProviderXmlDataProviderObjectDataProvider 用于绑定到 .NET 对象以及从 .NET 对象绑定,毫不奇怪,XmlDataProvider 用于绑定到 XML 片段和文档以及从中绑定 XML 片段和文档。 可以在任何 XAML 容器的 resources 节中指定数据提供程序。

使用 XmlDataProvider

下面是使用 XmlDataProvider 对象的示例。

<StackPanel 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="FavoriteColors">      <x:XData>        <Colors >          <Color>Blue</Color>           <Color>Black</Color>           <Color>Green</Color>           <Color>Red</Color>         </Colors>      </x:XData>    </XmlDataProvider>
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML Example
  </TextBlock>
  <ListBox Width="200" Height="300" 
           ItemsSource="{Binding Source={StaticResource FavoriteColors},            XPath=/Colors/Color}">
  </ListBox>
</StackPanel>

StackPanel 的资源中,我们有一个 XmlDataProvider 对象。 x:Key 表示在 Binding 对象中引用它时要使用的名称。 在提供程序中,我们创建了内联 XML,以用作数据绑定的源。 若要使用内联数据,必须使用 XData 元素将其括起来,如代码中所示。 在 ListBoxBinding 中,我们已将提供程序指定为绑定的。 当数据源位于 XAML 文档中时,需要指定对象是静态资源,如代码中所示。 最后,我们使用 XPath 语句指定应使用 XML 文档中的哪个集合来填充 ListBox。 此代码生成如图 5 所示的窗体。

Aa480224.wpfdatabinding_pt105 (en-us,MSDN.10) .gif

图 5。 XML 数据绑定

或者,通过指定 XmlDataProviderSource 属性,我们可以指定提供程序使用路径或 URL 查找 XML 的源,以创建同一窗体。

<XmlDataProvider x:Key="FavoriteColors" 
                 Source="D:\Writing\msdn\avalondatabindingpt1\xaml\colors.xml"
/>

XmlDataProviderSource 属性也可以指向标准 URL,以便创建对 XML API(如 RSS)的快速访问。

<StackPanel 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="MyRSS" 
      Source="http://adoguy.com/RSS"
    />
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML Example
  </TextBlock>
  <ListBox Width="500" Height="300" 
           ItemsSource="{Binding Source={StaticResource MyRSS}, 
           XPath=//item/title}">
  </ListBox>
</StackPanel>

通过调用 RSS 源,我可以创建一个页面,以在 ListBox 中快速列出博客的主题,如图 6 所示。

Aa480224.wpfdatabinding_pt106 (en-us,MSDN.10) .gif

图 6。 我的博客主题列表

使用 ObjectDataProvider

有时需要绑定到 .NET 对象。 这是 ObjectDataProvider 的用武之地。 此数据访问接口允许你为 .NET 数据类型创建绑定。

例如,我们可以在 .NET 中创建一个简单的字符串集合,如下所示。

public class MyStrings : List<String>
{
  public MyStrings()
  {
    this.Add("Hello");
    this.Add("Goodbye");
    this.Add("Heya");
    this.Add("Cya");
  }
}

–或者–

Public Class MyStrings
  Inherits List(Of String)

  Public Sub New()
    Me.Add("Hello")
    Me.Add("Goodbye")
    Me.Add("Heya")
    Me.Add("Cya")
  End Sub

End Class

还可以使用 XAML 文档中的处理指令,通过将命名空间指定为 xmlns 声明,将 CLR 对象的整个命名空间添加到文档支持的类型。 例如,我们可以将整个命名空间的类映射到 XAML 中,如下所示。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <!-- ... -->
</Window>

若要从外部程序集导入类,可以指定 xmlns 声明,但指定程序集名称,如下所示。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:sys="clr-namespace:System,assembly=mscorlib"
    x:Class="XamlExamples.SimpleSource"
 >
  <!-- ... -->
</Window>

导入类型后,可以使用 ObjectDataProvider 从这些类型之一指定数据源。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <Window.Resources>
    <ObjectDataProvider x:Key="MyStringData"                         ObjectType="{x:Type local:MyStrings}" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Simple Source Example
    </TextBlock>
    <ListBox Name="theListBox" Width="200" Height="300" 
      ItemsSource="{Binding Source={StaticResource MyStringData}}"/>
  </StackPanel>
</Window>

还可以通过指定具有命名空间名称和要使用的类型的 XAML 元素来创建资源,如下所示。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    xmlns:local="clr-namespace:XamlExamples"
    x:Class="XamlExamples.SimpleSource"
 >
  <Window.Resources>
    <local:MyStrings x:Key="MyStringData" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Simple Source Example
    </TextBlock>
    <ListBox Name="theListBox" Width="200" Height="300" 
      ItemsSource="{Binding Source={StaticResource MyStringData}}"/>
  </StackPanel>
</Window>

此语法的工作方式与 ObjectDataSource 类似,但使用起来要简单一些。 导入命名空间后,可以通过指定类的名称来添加引用数据源类的资源, (例如 MyStrings) 。 数据绑定与前面的示例相同,因为 XAML 代码并不关心它是哪种类型的数据源,只是考虑它是数据源。

绑定模式

在大多数情况下,你会希望 绑定 是双向绑定。 Binding 对象支持多种模式以支持不同的用例,如表 1 所示。

表 1. 支持的绑定模式

绑定模式 说明
TwoWay 以双向方式将更改从绑定控件或绑定源移动到其他控件。 (这是默认模式。)
OneWay 仅将更改从源移动到 控件。 源中发生更改时,绑定控件的数据将更改。
一次性 数据仅在启动时绑定,在控件首次填充数据后,将忽略对源的更改。

只需在标记中包含 模式即可指定模式,如下所示。

{Binding ElementName=theTextBox, Path=Text, Mode=OneTime}

查看双向绑定工作原理的一种方法是创建包含两个文本框的画布,一个文本框绑定到另一个文本框。

<Window    
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  >
  <Canvas>
    <TextBox Name="theTextBox" Text="Hello" />
    <TextBox Canvas.Top="25"
             Text="{Binding ElementName=theTextBox, Path=Text, Mode=TwoWay}" />
  </Canvas>
</Window>

如果将此代码粘贴到 SDK 附带的 XAMLPad 工具中,应注意到源文本框会在你键入时更新绑定文本框,但仅当绑定控件失去焦点时,才会从绑定控件更新到源代码管理。 如果将 模式 更改为 OneWayOneTime,可以看到这些不同的模式如何更改绑定的工作方式。

控制绑定时间

除了 模式,还可以使用 UpdateSourceTrigger 指定绑定推送更改的时间。 可以通过指定 UpdateSourceTrigger 类型来指定绑定仅在指定时间进行更改。

{Binding ElementName=theTextBox, Path=Text, UpdateSourceTrigger=LostFocus}

UpdateSourceTrigger 属性指定何时使用更改更新源。 这仅适用于 Mode=TwoWay 绑定 (这是默认) 。 表 2 中显示了 UpdateSourceTrigger 的有效值。

表 2. UpdateSourceTrigger 值

UpdateSourceTrigger 说明
明确 仅通过显式调用 BindingExpression.UpdateSource 方法更新源。
LostFocus 源在绑定控件失去焦点时更新。
PropertyChanged 每次属性更改时,更改都会更新到源。 此选项为默认行为。

使用 DataContext

本文要介绍的最后一个概念是如何使用 DataContextDataContext 专门用于指定将某个容器中的所有控件绑定到公共对象。

例如,以下示例中有一个 Canvas,该 画布 将显示 XML 文档中特定节点的值和文本。

<StackPanel 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  >
  <StackPanel.Resources>
    <XmlDataProvider x:Key="FavoriteColors">
      <Colors >
        <Color ID="1">Blue</Color> 
        <Color ID="2">Black</Color> 
        <Color ID="3">Green</Color> 
        <Color ID="4">Red</Color> 
      </Colors>
    </XmlDataProvider>
  </StackPanel.Resources>
  <TextBlock HorizontalAlignment="Center" 
             FontWeight="Bold">
    XML DataContext Example
  </TextBlock>
  <Canvas DataContext="{Binding Source={StaticResource FavoriteColors},                         XPath='/Colors/Color[@ID=2]'}">
    <TextBlock Text="{Binding XPath=@ID}" />
    <TextBlock Canvas.Top="25" Text="{Binding XPath=.}" />
  </Canvas>
</StackPanel>

通过将 DataContext 设置为 XML 文档 (和特定的 XPath 表达式) ,我们告诉 Canvas ,其中不包含 Source 的所有控件都可以使用容器的 DataContext。 这样,我们只需指定 XPath 表达式即可绑定 TextBlock。 请注意,每个 TextBlock中的 XPath 表达式都是相对的 XPath 表达式。 这与对象绑定相同。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Simple Source Binding"
    x:Class="XamlExamples.SimpleSource"
  >
  <Window.Resources>
    <ObjectDataProvider TypeName="XamlExamples.MyStrings, XamlExamples"
                        x:Key="MyStringData" />
  </Window.Resources>  
    <StackPanel>
      <TextBlock HorizontalAlignment="Center" 
               FontWeight="Bold">
      Object DataContext Example
    </TextBlock>
    <Canvas DataContext="{Binding Source={StaticResource MyStringData}}">
      <TextBlock Text="{Binding Path=Length}" />
      <TextBlock Canvas.Top="25" Text="{Binding Path=Item[0]}" />
    </Canvas>
  </StackPanel>
</Window>

使用 对象而不是 XML 只是意味着要使用 Path 表达式而不是 XPath 表达式。

我们在哪?

我们已经了解了如何使用 Binding 对象直接在 XAML 中执行数据绑定,无论是在简写版本还是长手版本中。 通过使用 XML 或对象数据提供程序,我们可以绑定到应用程序中不同类型的对象。 我们还了解了如何使用 Binding 对象的 ElementName 语法绑定到其他控件。 在本系列的下一部分中,我将展示如何使用自己的对象或 .NET 数据容器 ((例如 DataSourcesDataSets) )获取此信息并绑定到实际数据库数据。

参考