如何使用 LINQ to XML 执行 WPF 数据绑定





4.00/5 (8投票s)
本文将介绍如何使用 LINQ to XML 将数据绑定到 WPF 控件。
System.Xml.Linq
在将 XML 数据与 WPF 控件绑定之前,我将为您概述 System.Xml.Linq 命名空间。它包含 LINQ to XML 的类。LINQ to XML 是一个内存 XML 编程接口,可让您高效且轻松地修改 XML 文档。
使用 LINQ to XML,您可以:
- 从文件或流加载 XML。
- 将 XML 序列化到文件或流。
- 使用函数式构造从头开始创建 XML 树。
- 使用 LINQ 查询查询 XML 树。
- 操作内存 XML 树。
- 使用 XSD 验证 XML 树。
- 结合使用这些功能将 XML 树从一种形状转换为另一种形状。
对于数据绑定工作,我们需要关注此命名空间中的两个类,即:XAttribute 和 XElement。这些类的动态属性有助于我们将控件精确地绑定到 XML 数据中的相应元素。
XAttribute 类派生自 XObject 类,表示 XML 属性。属性不是从 XNode 派生的;它们不是 XML 树中的节点。相反,它们仅仅是与元素关联的名称/值对。
XElement 类派生自 XContainer 类,而 XContainer 又派生自 XNode 类,它表示 XML 元素,这是基本的 XML 构造。元素具有 XName,可选地有一个或多个属性,并且可以选择性地包含 XElement、XComment、XText 和 XProcessingInstruction 等内容。
每个 XElement 都包含该元素的属性列表。属性必须具有对该元素唯一的限定名称。
例如,以下 C# 代码显示了创建包含属性的元素的常见任务:
XElement phone = new XElement("Phone", 
  new XAttribute("Type", "Home"), "555-555-5555");
Console.WriteLine(phone);
WPF 中的动态数据绑定和动态属性
了解了可用的 XLinq 类之后,下一步是理解如何使用这些类执行数据绑定。
默认情况下,数据绑定仅在目标 UI 元素初始化时发生。这称为一次性绑定。对于大多数用途,这不够;通过双向绑定,源中的更改会自动传播到目标,目标中的更改也会自动传播到源。
要实现单向或双向绑定,源必须实现更改通知机制,例如,通过实现 INotifyPropertyChanged 接口,或为支持的每个属性使用 PropertyNameChanged 模式。
大多数 LINQ to XML 类不符合 WPF 动态数据源的要求。为了支持 WPF 数据绑定,LINQ to XML 公开了一组动态属性。这些动态属性是 XAttribute 和 XElement 类中的特殊运行时属性,用于充当 WPF 的动态数据源并实现更改通知。
XAttribute 和 XElement 类中的动态属性不能像标准属性那样访问。在 C# 中,只能通过 System.ComponentModel 命名空间提供的功能在运行时访问动态属性。但是,在 XML 源中,可以通过以下形式的简单表示法访问动态属性:object.dynamic-property。
这些类的动态属性要么解析为可直接使用的值,要么解析为必须提供索引的索引器(如 XElement 的 Elements and Descendants 属性),以获取结果值或值集合。
为了实现 WPF 动态绑定,动态属性将与 System.Windows.Data 命名空间提供的功能结合使用,其中最值得注意的是 Binding 类。
XAML 中的 XML 作为资源数据
现在,让我们看看 XAML 中可用于定义和使用 XML 数据的各种元素。此外,本节将解释定义资源、模板以及随后将它们绑定到 WPF 控件所需的元素。
在 Windows.Resources 标签内,我们可以使用 <ObjectDataProvider> 声明到 XML 数据的源,以及一个遵循特定数据元素模式的 <DataTemplate>。
<ObjectDataProvider> 标签声明了一个名为 LoadedBooks 的 ObjectDataProvider 类的实例,它使用 XElement 作为源。此 XElement 通过解析嵌入的 XML 文档(CDATA 元素)来初始化。此外,在声明嵌入的 XML 文档及其解析时会保留空格。原因是用于显示原始 XML 的 TextBlock 控件没有特殊的 XML 格式化功能。
定义了一个名为“BookTemplate”的 DataTemplate,用于在 Book List UI 部分的相应控件中显示条目。它使用数据绑定和 LINQ to XML 动态属性通过以下赋值检索书籍 ID 和书籍名称:
Text="{Binding Path=Attribute[id].Value}"
Text="{Binding Path=Value}"
<--<Window.Resources> tag with both 
  of these definitions is given below:-->
<Window.Resources>
    <!-- Books provider and inline XML data -->
    <ObjectDataProvider x:Key="LoadedBooks" 
      ObjectType="{x:Type xlinq:XElement}"
      MethodName="Parse">
        <ObjectDataProvider.MethodParameters>
            <system:String xml:space="preserve">
<![CDATA[
<books xmlns="http://www.mybooks.com">
<book id="1">Seven ways to Success</book>
<book id="2">My Experiments with Truth</book>
<book id="3">Leadership Skills</book>
<book id="4">India and Politics Today</book>
</books>
]]>                
            </system:String>
            <xlinq:LoadOptions>PreserveWhitespace</xlinq:LoadOptions>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <!-- Template for use in Books List listbox. -->
    <DataTemplate x:Key="BookTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="3" Text="{Binding Path=Attribute[id].Value}"/>
            <TextBlock Margin="3" Text="-"/>
            <TextBlock Margin="3" Text="{Binding Path=Value}"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
准备好 DataTemplate 作为数据资源后,就可以在用户界面中使用 WPF 控件进行数据绑定了。
在 <StackPanel> 标签中,DataContext 属性设置为“LoadedBooks”数据提供程序。
DataContext="{Binding Source={StaticResource LoadedBooks}}
这使得名为 tbRawXml 的 TextBlock 能够通过绑定到此数据提供程序的 Xml 属性来显示原始 XML。
Text="{Binding Path=Xml}"
Book List UI 部分的 ListBox 将其显示项的模板设置为 BookTemplate 资源,并且书籍的实际值被绑定到列表框中的项。
<ListBox Name="lbBooks" Height="200" 
  Width="415" ItemTemplate ="{StaticResource BookTemplate}">
    <ListBox.ItemsSource>
        <Binding Path="Elements[{http://www.mybooks.com}book]"/>
    </ListBox.ItemsSource>
</ListBox>
在列表框中选择一本书后,我们需要将其相应详细信息显示在文本框中以供查看和编辑。这需要绑定到列表框中的父数据源,并进行双向数据绑定,以便这两个文本框中显示和更新书籍元素的当前值。绑定到动态属性的方法与 BookTemplate 数据模板中的方法类似。
   <StackPanel Margin="1" 
     DataContext="{Binding ElementName=lbBooks, Path=SelectedItem}">
....
....
        <TextBox Name="editAttributeTextBox" 
          Width="410" Text="{Binding Path=Attribute[id].Value}">
...
...
          <TextBox Name="editValueTextBox" Width="410" 
            Text="{Binding Path=Value}" Height="25">
演练:创建显示内嵌 XML 数据 LINQ to XML 数据绑定的 WPF 应用程序
现在,让我们创建一个完整的应用程序来利用上述讨论的功能。此示例程序允许用户查看和操作存储为嵌入式 XML 元素的书籍列表。
启动 Visual Studio 并创建一个名为 WPFDataBindingLinqToXml 的 C# WPF 应用程序。项目必须使用 .NET Framework 3.5(或更高版本)。
在 Window1.Xaml 中,将 UI 设计为下面的截图所示的 XAML 标记。

同样,修改 Window1.Xaml.cs 文件中的代码,如下所示:
XNamespace mybooks = "http://www.mybooks.com";
XElement bookList;
public Window1()
{
    InitializeComponent();
    bookList = (XElement)((ObjectDataProvider)Resources["LoadedBooks"]).Data;
}
void OnRemoveBook(object sender, EventArgs e)
{
    int index = lbBooks.SelectedIndex;
    if (index < 0) return;
    XElement selBook = (XElement)lbBooks.SelectedItem;
    //Get next node before removing element.
    XNode nextNode = selBook.NextNode;
    selBook.Remove();
    //Remove any matching newline node.
    if (nextNode != null && nextNode.ToString().Trim().Equals(""))
    { nextNode.Remove(); }
    //Set selected item. 
    if (lbBooks.Items.Count > 0)
    { lbBooks.SelectedItem = lbBooks.Items[index > 0 ? index - 1 : 0]; }
}
void OnAddBook(object sender, EventArgs e)
{
    if (String.IsNullOrEmpty(tbAddID.Text) || 
        String.IsNullOrEmpty(tbAddValue.Text))
    {
        MessageBox.Show("Please supply both a Book ID and a Value!", "Entry Error!");
        return;
    }
    XElement newBook = new XElement(mybooks + "book", 
             new XAttribute("id", tbAddID.Text),
                        tbAddValue.Text);
    bookList.Add("  ", newBook, "\r\n");
}
基本上,上面的代码使用 XElement、XNode 和 XNamespace 对象来操作 XML 数据,同时绑定控件。
最后,按 Ctrl+Shift+B 生成解决方案,然后按 F5 运行。该项目应能成功编译并作为通用 WPF 应用程序运行。
使用 WPFDataBindingLinqToXml 示例应用程序及其用户界面
UI 的顶部显示表示书籍列表的原始 XML。它使用 WPF TextBlock 控件显示,该控件不允许通过鼠标或键盘进行交互。
第二个垂直部分(标记为 Book List)以纯文本有序列表的形式显示书籍。它使用 ListBox 控件,该控件允许通过鼠标或键盘进行选择。
在此 UI 中可以执行以下任务:
- 要从列表中删除现有书籍,请在 Book List 部分中选择它,然后单击 Remove Selected Book 按钮。请注意,书籍条目已从书籍和原始 XML 源列表中删除。
- 要向列表中添加新书籍,请在 Add New Book 的最后一节中的 ID 和 ValueTextBox 控件中输入值,然后单击 Add Book 按钮。请注意,书籍已附加到书籍和 XML 列表。此程序不验证输入值。
- 在第二个 Book List 部分中选择书籍条目。其当前值应显示在第三部分中。
- 要编辑选定的书籍,请使用键盘编辑值。一旦任何 TextBox控件失去焦点,更改就会自动传播到 XML 源和书籍列表。
结论
本文解释了开发可以绑定到使用 LINQ to XML 类内联存储的 XML 数据的 WPF 应用程序的理论和步骤。同样的操作也可以用于存储在文件系统中的 XML 数据。本文的下一部分将介绍如何通过对此示例进行少量修改来绑定到外部 XML 数据。希望您喜欢并觉得本文很有用。


