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

 

Shawn Wildermuth

2006 年 5 月

适用于:
   Microsoft Windows Presentation Foundation

总结:本系列的第 2 部分继续说明如何使用基于 XAML 的数据绑定在 Microsoft Windows Presentation Foundation 项目中执行数据操作。 (21 个打印页)

目录

简介
绑定到数据库数据
我们在哪?
参考

简介

在社区中制造噪音的大多数Windows Presentation Foundation (WPF) 示例都是关于图形引擎的杂音。 对于大多数用户界面开发人员来说,他们的大部分工作是在企业开发世界中开发日常数据输入表单。 WPF 是否具有解决其问题的解决方案? 确实...

绑定到数据库数据

在本系列文章的第一部分中,我们深入研究了原始绑定语法,并检查了如何将简单对象绑定到 XAML 对象。 虽然这是难题的重要组成部分,但在大多数情况下,真正的要求是绑定到存储在数据库中的数据。 在大多数情况下,这支持两种不同方案中的绑定:数据库数据 (例如 DataSetsDataTablesDataRows) 和自定义业务对象。

绑定到数据库数据

数据库仍然是当今大多数开发(尤其是企业开发)的中心。 为了举例说明这一点,我们可以使用 WPF 对话框的简单示例,该对话框允许用户浏览数据库中的员工。 我们希望能够在浏览器中显示少量信息,包括员工的图片。 我们需要加载一个表,其中包含我们需要的所有信息。 为此,可以使用数据库中的信息创建新的 DataTable ,如下所示。

C#

DataTable theTable = new DataTable();
string connString = 
  ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
string query = @"SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
                 FROM Employees";

// Fill the Set with the data
using (SqlConnection conn = new SqlConnection(connString))
{
  SqlDataAdapter da = new SqlDataAdapter(query, conn);
  da.Fill(theTable);
}

Visual Basic .NET

Dim theTable As DataTable =  New DataTable() 
String connString = 
  ConfigurationManager.ConnectionStrings("Northwind").ConnectionString
String query = "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
               "  FROM Employees"
 
' Fill the Set with the data
Using conn as New SqlConnection(connString))
  Dim da As SqlDataAdapter =  New SqlDataAdapter(query,conn) 
  da.Fill(theTable)
End Using

获得数据后,可以使用它来设置 DataContext,以便将其绑定在 XAML 中。

C#

// Set the Data Context
DataContext = theTable;

Visual Basic .NET

' Set the Data Context
DataContext = theTable

获取数据并将其提供给窗口后,可以在 XAML 中执行数据绑定。 ComboBox 中的 Binding 只是指示绑定从父 (的 DataContext 中获取数据,在这种情况下,它会向上浏览控件树,直到在 Window) 中找到 DataContext

    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True"                                 ItemsSource="{Binding}" 
              ... />

IsSynchronizedWithCurrentItem 属性非常重要,因为当所选内容发生更改时,就窗口而言,“当前项”会发生变化。 这会告知 WPF 引擎,此对象将用于更改当前项。 如果没有此属性, DataContext 中的当前项将不会更改,因此文本框将假定它仍位于列表中的第一项上。

为了在组合框中显示员工姓名,我们在 ItemsTemplate 中创建绑定以显示 DataTable 中的 FirstNameLastName

    <DataTemplate x:Key="EmployeeListTemplate">
      <StackPanel Orientation="Horizontal">
      <TextBlock Text="{Binding Path=FirstName}" />
      <TextBlock Text=" " />
      <TextBlock Text="{Binding Path=LastName}" />
      </StackPanel>
    </DataTemplate>

接下来,我们将添加文本框来保存姓名、职务和雇用日期。

      <TextBlock Canvas.Top="5">First Name:</TextBlock>
      <TextBox Canvas.Top="5" Text="{Binding Path=FirstName}" />
      <TextBlock Canvas.Top="25">Last Name:</TextBlock>
      <TextBox Canvas.Top="25" Text="{Binding Path=LastName}" />
      <TextBlock Canvas.Top="45">Title:</TextBlock>
      <TextBox Canvas.Top="45" Text="{Binding Path=Title}" />
      <TextBlock Canvas.Top="65">Hire Date:</TextBlock>
      <TextBox Canvas.Top="65" Text="{Binding Path=HireDate}" />

由于我们也需要照片,因此我们需要将图像添加到 XAML。

      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75"/>

图像的唯一问题是它不支持自动将照片数据绑定到图像。 为此,我们可以处理 ComboBoxSelectionChanged 事件以填充我们的图像

    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True"                   
              Width="200" 
              ItemsSource="{Binding}" 
              ItemTemplate="{StaticResource EmployeeListTemplate}"
              SelectionChanged="theCombo_OnSelectionChanged" />

在代码中,我们需要从 DataTable 加载图像,并创建 BitmapImage 对象以填充 Image 标记。 请注意,这不是 GDI+ (System.Drawing) 的位图,而是 WPF 中的新 位图 对象。

C#

// Handler to show the image
void theCombo_OnSelectionChanged(object sender, RoutedEventArgs e)
{
  ShowPhoto();
}

// Shows the Photo for the currently selected item
void ShowPhoto()
{
  object selected = theCombo.SelectedItem;
  DataRow row = ((DataRowView)selected).Row;
  
  // Get the raw bytes of the image
  byte[] photoSource = (byte[])row["Photo"];

  // Create the bitmap object
  // NOTE: This is *not* a GDI+ Bitmap object
  BitmapImage bitmap = new BitmapImage();
  MemoryStream strm = new MemoryStream();

  // Well-known work-around to make Northwind images work
  int offset = 78;
  strm.Write(photoSource, offset, photoSource.Length - offset);

  // Read the image into the bitmap object
  bitmap.BeginInit();
  bitmap.StreamSource = strm;
  bitmap.EndInit();

  // Set the Image with the Bitmap
  theImage.Source = bitmap;
  
}

Visual Basic .NET

' Handler to show the image
Sub theCombo_OnSelectionChanged(ByVal sender As Object, ByVal e As RoutedEventArgs)

  ShowPhoto();

End Sub

// Shows the Photo for the currently selected item
Sub ShowPhoto()

  Dim selected As Object =  theCombo.SelectedItem 
  Dim row As DataRow = (CType(selected, DataRowView)).Row 
 
  ' Get the raw bytes of the image
  Dim photoSource() As Byte = CType(row("Photo"), Byte())
 
  ' Create the bitmap object
  ' NOTE: This is *not* a GDI+ Bitmap object
  Dim bitmap As BitmapImage =  New BitmapImage() 
  Dim strm As MemoryStream =  New MemoryStream() 
 
  ' Well-known work-around to make Northwind images work
  Dim offset As Integer =  78 
  strm.Write(photoSource, offset, photoSource.Length - offset)
 
  ' Read the image into the bitmap object
  bitmap.BeginInit()
  bitmap.StreamSource = strm
  bitmap.EndInit()
 
  ' Set the Image with the Bitmap
  theImage.Source = bitmap
 
End Sub

我们从 ComboBox 中获取 SelectedItem,并将其转换为 DataRow,以便获取数据。 然后,从 “照片 ”列中获取字节数组。 这是存储在 Northwind 数据库中的照片。 可以使用内存中流将照片字节流式传输到 BitmapImage 对象中。 唯一的更改是跳过 Northwind 图像标头的前 78 个字节的常用解决方法,因为它不再使用。 将流读入位图后,即可将其分配给 Image 对象作为源。

我们希望确保数据绑定是双向的,因此让我们创建一个显示当前信息的按钮,以便我们知道它位于 DataRow 中

C#

void SaveButton_OnClick(object sender, RoutedEventArgs e)
{
  object selected = theCombo.SelectedItem;
  DataRow row = ((DataRowView)selected).Row;

  MessageBox.Show(string.Format("{0} {1} {2} - {3:d}", 
    row["Title"], row["FirstName"], row["LastName"],  row["HireDate"]));
}

Visual Basic .NET

Sub SaveButton_OnClick(ByVal sender As Object, ByVal e As RoutedEventArgs)

  Dim selected As Object =  theCombo.SelectedItem 
  Dim row As DataRow = (CType(selected, DataRowView)).Row 
 
  MessageBox.Show(String.Format("{0} {1} {2} - {3:d}", _
    row("Title"), row("FirstName"), row("LastName"),  row("HireDate")))

End Sub

整个 XAML 文件最终查找如下。

<Window x:Class="ExampleCS.EmployeeBrowser"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    Title="Employee Browser"
    Loaded="OnLoaded" 
    Width="300"
    Height="170" 
    WindowStartupLocation="CenterScreen"
    >
  <Window.Resources>
    <DataTemplate x:Key="EmployeeListTemplate">
      <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Path=FirstName}" />
        <TextBlock Text=" " />
        <TextBlock Text="{Binding Path=LastName}" />
      </StackPanel>
    </DataTemplate>
  </Window.Resources>
  <Window.Background>
    <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
      <LinearGradientBrush.GradientStops>
        <GradientStop Color="DarkGray" Offset="0" />
        <GradientStop Color="White" Offset=".75" />
        <GradientStop Color="DarkGray" Offset="1" />
      </LinearGradientBrush.GradientStops>
    </LinearGradientBrush>
  </Window.Background>
  <StackPanel Name="theStackPanel" 
              VerticalAlignment="Top">
    <ComboBox Name="theCombo" 
              IsSynchronizedWithCurrentItem="True" 
              Width="200" 
              ItemsSource="{Binding}" 
              ItemTemplate="{StaticResource EmployeeListTemplate}"
              SelectionChanged="theCombo_OnSelectionChanged" />
    <Canvas>
      <Canvas.Resources>
        <Style TargetType="{x:Type TextBox}">
          <Setter Property="Canvas.Left" Value="160" />
          <Setter Property="Padding" Value="0" />
          <Setter Property="Height" Value="18" />
          <Setter Property="Width" Value="120" />
        </Style>
        <Style TargetType="{x:Type TextBlock}">
          <Setter Property="Canvas.Left" Value="90" />
          <Setter Property="Padding" Value="0" />
          <Setter Property="Height" Value="18" />
          <Setter Property="FontWeight" Value="Bold" />
        </Style>
      </Canvas.Resources>
      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75"/>
      <TextBlock Canvas.Top="5">First Name:</TextBlock>
      <TextBox Canvas.Top="5" Text="{Binding Path=FirstName}" />
      <TextBlock Canvas.Top="25">Last Name:</TextBlock>
      <TextBox Canvas.Top="25" Text="{Binding Path=LastName}" />
      <TextBlock Canvas.Top="45">Title:</TextBlock>
      <TextBox Canvas.Top="45" Text="{Binding Path=Title}" />
      <TextBlock Canvas.Top="65">Hire Date:</TextBlock>
      <TextBox Canvas.Top="65" Text="{Binding Path=HireDate}" />
      <Button Canvas.Top="85" Canvas.Left="90" Width="190" 
              Name="SaveButton" Click="SaveButton_OnClick">Save</Button>
    </Canvas>
  </StackPanel>
</Window>

现在,当我们运行浏览器时,我们得到一个类似于图 1 所示的界面。

Aa480226.wpfdabndpt201 (en-us,MSDN.10) .gif

图 1. 员工浏览器

这个简单的示例相当简单,但如果我们在数据集中使用相关的数据表呢? 让我们看看它是否同样简单。

让我们扩展员工浏览器,以包括员工是销售人员的订单。 为此,我们需要获取订单信息。 每次切换用户时,我们都可以使用新查询来执行此操作,但是,让我们将数据与 Employee 一起加载到 DataSet 中,并使用 DataRelation 来关联这两条信息。

C#

DataSet theSet = new DataSet();

string connString = ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
string employeeQuery = @"
  SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
  FROM Employees
";
string orderQuery = @"
  SELECT o.OrderID, EmployeeID, CompanyName, OrderDate, SUM((UnitPrice * Quantity)* (1-Discount)) as OrderTotal
  FROM Orders o
  JOIN [Order Details] od on o.OrderID = od.OrderID
   JOIN Customers c on c.CustomerID = o.CustomerID
  GROUP BY o.OrderID, o.EmployeeID, o.OrderDate, CompanyName";

// Fill the Set with the data
using (SqlConnection conn = new SqlConnection(connString))
{
  SqlDataAdapter da = new SqlDataAdapter(employeeQuery, conn);
  da.Fill(theSet, "Employees");
  da.SelectCommand.CommandText = orderQuery;
  da.Fill(theSet, "Orders");
}

// Create the relationship
DataTable empTable = theSet.Tables["Employees"];
DataTable ordTable = theSet.Tables["Orders"];
theSet.Relations.Add("Emp2Ord", 
                     empTable.Columns["EmployeeID"], 
                     ordTable.Columns["EmployeeID"], 
                     false);

// Set the Context of the Window to be the 
// DataTable we've created
DataContext = empTable;

Visual Basic .NET

Dim theSet As DataSet =  New DataSet() 
 
Dim connString As String = _
    ConfigurationManager.ConnectionStrings("Northwind").ConnectionString 
String employeeQuery = _
  "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
  "  FROM Employees"

String orderQuery = _
  "SELECT o.OrderID, EmployeeID, CompanyName, OrderDate, " + _
  "       SUM((UnitPrice * Quantity)* (1-Discount)) as OrderTotal " + 
  "FROM Orders o " +
  "JOIN (Order Details) od on o.OrderID = od.OrderID " +
  "JOIN Customers c on c.CustomerID = o.CustomerID " +
  "GROUP BY o.OrderID, o.EmployeeID, o.OrderDate, CompanyName"
 
' Fill the Set with the data
Using conn as New SqlConnection(connString)

  Dim da As SqlDataAdapter =  New SqlDataAdapter(employeeQuery,conn) 
  da.Fill(theSet, "Employees")
  da.SelectCommand.CommandText = orderQuery
  da.Fill(theSet, "Orders")

End Using
 
' Create the relationship
Dim empTable As DataTable =  theSet.Tables("Employees") 
Dim ordTable As DataTable =  theSet.Tables("Orders") 
theSet.Relations.Add("Emp2Ord", 
                     empTable.Columns("EmployeeID"), 
                     ordTable.Columns("EmployeeID"), 
                     False)

' Set the Context of the Window to be the 
' DataTable we've created
DataContext = empTable

此代码将创建包含两个表的 数据集EmployeesOrders。 这些表通过名为 Emp2Ord的 RelationEmployeeID 相关。 我们仍然可以绑定到 Employee DataTable ,以便 XAML 中的原始数据绑定正常工作。 与Windows 窗体或 ASP.NET 数据绑定非常类似,我们可以绑定到 Relation 的名称,以便绑定到一组相关记录。

        <ListBox Name="OrderList" Width="280" Height="200"
               ItemsSource="{Binding Emp2Ord}" 
               ItemTemplate="{StaticResource OrderListTemplate}" />

此列表框仍使用与员工浏览器的其余部分相同的 DataContext ;它只是通过关系指定绑定。 将列表框绑定到 Relation 后,我们可以绑定到 ItemTemplate 中的单个字段,就像在员工组合框中所做的那样。

    <DataTemplate x:Key="OrderListTemplate">
      <StackPanel Orientation="Horizontal">
        <TextBlock VerticalAlignment="Top" Width="100" 
                   Text="{Binding Path=CompanyName}" />
        <StackPanel>
          <TextBlock Text="{Binding Path=OrderID}" />
          <TextBlock Text="{Binding Path=OrderDate}" />
          <TextBlock Text="{Binding Path=OrderTotal}" />
        </StackPanel>
      </StackPanel>
    </DataTemplate>

通过此附加数据绑定,我们现在显示仅与所选用户相关的订单信息的列表框,如图 2 所示。

Aa480226.wpfdabndpt202 (en-us,MSDN.10) .gif

图 2. 我们改进的员工浏览器

这使我们能够绑定到更复杂的数据,而不仅仅是简单的矩形数据片段。 在许多组织中,他们使用自定义 .NET 类型 (或业务对象) 来保存其数据和业务逻辑。 WPF 能否像 DataSet 一样轻松地绑定到这些对象?

绑定到业务对象

在 .NET 的原始化身(包括Windows 窗体和 ASP.NET)中,DataSet 及其相关对象是一等公民。 它们将数据绑定简单且效果良好。 但是,如果选择生成对象模型或业务对象来保存数据,则需手动将数据从对象绑定到控件。 在 .NET 2.0 中,对象被提升为一等公民,从而简化了对对象的绑定。 在 WPF 中,这一点仍属实。 在 WPF 中,绑定到对象和 数据集一样容易。

若要使用业务对象创建我们最喜欢的 Employee Browser,让我们首先创建一个类来保存 员工

C#

public class Employee
{
  // Fields
  int _employeeID;
  string _firstName;
  string _lastName;
  string _title;
  DateTime _hireDate;
  BitmapImage _photo;

  // Constructor
  public Employee(IDataRecord record)
  {
    _employeeID = (int) record["EmployeeID"];
    _firstName = (string) record["FirstName"];
    _lastName = (string)record["LastName"];
    _title = (string)record["Title"];
    _hireDate = (DateTime)record["HireDate"];
    CreatePhoto((byte[])record["Photo"]);
  }

  // BitmapImage creation
  void CreatePhoto(byte[] photoSource)
  {
    // Create the bitmap object
    // NOTE: This is *not* a GDI+ Bitmap object
    _photo = new BitmapImage();
    MemoryStream strm = new MemoryStream();

    // Well-known hack to make Northwind images work
    int offset = 78;
    strm.Write(photoSource, offset, photoSource.Length - offset);

    // Read the image into the bitmap object
    _photo.BeginInit();
    _photo.StreamSource = strm;
    _photo.EndInit();

  }
}

Visual Basic .NET

Public Class Employee

  ' Fields
  Dim _employeeID As Integer
  Dim _firstName As String
  Dim _lastName As String
  Dim _title As String
  Dim _hireDate As DateTime
  Dim _photo As BitmapImage
 
  ' Constructor
  Public  Sub New(ByVal record As IDataRecord)

    _employeeID = CType(record("EmployeeID"), Integer)
    _firstName = CType(record("FirstName"), String)
    _lastName = CType(record("LastName"), String)
    _title = CType(record("Title"), String)
    _hireDate = CType(record("HireDate"), DateTime)
    CreatePhoto(CType(record("Photo"), Byte()))

  End Sub
 
  ' BitmapImage creation
  Private  Sub CreatePhoto(ByVal photoSource() As Byte)

    ' Create the bitmap object
    ' NOTE: This is *not* a GDI+ Bitmap object
    _photo = New BitmapImage()
    Dim strm As MemoryStream =  New MemoryStream() 
 
    ' Well-known hack to make Northwind images work
    Dim offset As Integer =  78 
    strm.Write(photoSource, offset, photoSource.Length - offset)
 
    ' Read the image into the bitmap object
    _photo.BeginInit()
    _photo.StreamSource = strm
    _photo.EndInit()
 
  End Sub
End Class

此类采用来自 DataReader 的单个结果 (IDataRecord 类,但我们将在一分钟内) ,并填充本文前面用于 DataTable 示例的相同字段。 请注意,我们已将 BitmapImage 的创建移动到业务对象,以便更轻松地在 UI 类中使用员工。

接下来,我们需要字段的属性访问器。

C#

// Read-Only
public int EmployeeID
{
  get { return _employeeID; }
}

public string FirstName
{
  get { return _firstName; }
  set { _firstName = value; }
}

public string LastName
{
  get { return _lastName; }
  set { _lastName = value; }
}

public string Title
{
  get { return _title; }
  set { _title = value; }
}

public DateTime HireDate
{
  get { return _hireDate; }
  set { _hireDate = value; }
}

// Read-Only
public BitmapImage Photo
{
  get { return _photo; }
}

Visual Basic .NET

' Read-Only
Public ReadOnly Property EmployeeID() As Integer
  Get 
     Return _employeeID
  End Get
End Property
 
Public Property FirstName() As String
  Get 
     Return _firstName
  End Get
  Set (ByVal Value As String) 
     _firstName = value
  End Set
End Property
 
Public Property LastName() As String
  Get 
     Return _lastName
  End Get
  Set (ByVal Value As String) 
     _lastName = value
  End Set
End Property
 
Public Property Title() As String
  Get 
     Return _title
  End Get
  Set (ByVal Value As String) 
     _title = value
  End Set
End Property
 
Public Property HireDate() As DateTime
  Get 
     Return _hireDate
  End Get
  Set (ByVal Value As DateTime) 
     _hireDate = value
  End Set
End Property
 
' Read-Only
Public ReadOnly Property Photo() As BitmapImage
  Get 
     Return _photo
  End Get
End Property

在这些部分中,我们只是允许读写 (或只读) 访问 类中的字段。

现在,我们可以编写一个集合来保存员工。

C#

public class EmployeeList : ObservableCollection<Employee>
{
  public EmployeeList()
  {
    string connString =
           ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
    string query = @"
      SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo 
      FROM Employees
    ";

    // Fill the Set with the data
    using (SqlConnection conn = new SqlConnection(connString))
    {
      try
      {
        SqlCommand cmd = conn.CreateCommand();
        cmd.CommandText = query;

        conn.Open();
        SqlDataReader rdr = cmd.ExecuteReader();
        while (rdr.Read())
        {
          Add(new Employee(rdr));
        }
      }
      finally
      {
        if (conn.State != ConnectionState.Closed) conn.Close();
      }
    }
  }
}

Visual Basic .NET

Public Class EmployeeList
   Inherits ObservableCollection<Employee>
  Public  Sub New()
    String connString =
           ConfigurationManager.ConnectionStrings("Northwind").ConnectionString

    String query = _
      "SELECT EmployeeID, FirstName, LastName, Title, HireDate, Photo " + _
      "  FROM Employees"

    ' Fill the Set with the data
    Using conn as New SqlConnection(connString)
    
      Try
        Dim cmd As SqlCommand =  conn.CreateCommand() 
        cmd.CommandText = query
 
        conn.Open()
        Dim rdr As SqlDataReader =  cmd.ExecuteReader() 
        While rdr.Read()
          Add(New Employee(rdr))
        End While
      Finally
        If conn.State <> ConnectionState.Closed Then
            conn.Close()
        End If
      End Try
    
    End Using

  End Sub
End Class

集合的基类是 ObservableCollection 类,该类提供一种机制,允许 UI 知道是否向集合添加新成员。 我们已将数据访问从 UI 页移到集合类。 创建此类时,我们将查询数据库,并将新员工从 DataReader 添加到集合中。

有了集合和单个对象后,可以使用映射 (将类导入 XAML, 本系列) 的第一部分 对此进行了详细介绍。

<Window
    ...
    xmlns:e="Example"    DataContext="{StaticResource EmployeeList}"
    >
  <Window.Resources>
    <e:EmployeeList x:Key="EmployeeList" />
    ...
  </Window.Resources>
  ...
</Window>

我们使用 ? 将 类导入 XAML 文档 映射 声明。 此外,我们在“资源”中指定 EmployeeList,以便可以将其用作窗口的 DataContext。 这样,XAML 文件的其余部分与原始 Employee Browser 相同,因为我们仍与 DataSet 示例中的字段名称相同。 我们可以进行的唯一更改是在 XAML 文档中绑定 BitmapImage ,而不是在代码隐藏中执行此操作。

...
      <Image Name="theImage" Canvas.Top="5" Canvas.Left="5" Width="75" 
             Source="{Binding Path=Photo}"/>
...

我们现在有一个行为完全相同的员工浏览器 (见图 3) 。

Aa480226.wpfdabndpt203 (en-us,MSDN.10) .gif

图 3. 基于业务对象的员工浏览器

除了使用类型映射之外,还可以使用 ObjectDataProvider 将对象引入 XAML。 正如 本系列文章的第一部分所示,只需指定键和类型名称即可。

    <ObjectDataProvider x:Key="EmployeeList" 
                        TypeName="Example.Data.EmployeeList, ExampleCS"/>

x:Key 只是绑定中使用的名字对象,Typename 是类名和程序集 (在本例中是 UI 驻留在) 中的同一程序集。 XAML 的其余部分保持不变,因为我们在相同的数据中加载。

我们在哪?

现在,可以使用数据集或自定义对象从数据库加载 数据 ,并将数据直接绑定到 WPF 对象。 现在,你应该已准备好深入了解第一个 WPF 数据库项目。

参考