WPF 数据绑定的基本示例






4.22/5 (6投票s)
如何在 WPF 中执行简单的控件数据绑定。涵盖单向和双向绑定。
引言
本文旨在介绍 WPF 数据绑定的概念,通过示例展示如何使用自定义 `List(Of)` 填充 `DataGrid`,并通过 `TextBox` 和 `DatePicker` 等控件进行修改,这些控件的数据源将来自 Grid 本身。众所周知,Windows Presentation Foundation 引入的主要功能之一是能够轻松地将用户控件(和对象)与不同复杂性的数据源连接起来,并将它们的表示任务留给框架(尽管您始终可以稍后对其进行自定义)。作为一种可能的用法场景示例,我们假设有一个 Grid 公开来自给定自定义结构的数据,并在稍后通过简单的文本控件实现其变化。
示例类
假设我们有一个假想的类 `Articolo`,它允许我们定义要存储的产品,并包含产品代码(`Codice`)、描述(`Descrizione`)和有效期(`DataScadenza`)。我们将这个类(虽然简化,但对我们来说很有用)声明如下:
Public Class Articolo
Dim _codice As String
Dim _descrizione As String
Dim _datascadenza As Date
Public ReadOnly Property Codice As String
Get
Return _codice
End Get
End Property
Public Property Descrizione As String
Get
Return _descrizione
End Get
Set(value As String)
_descrizione = value
End Set
End Property
Public Property DataScadenza As Date
Get
Return _datascadenza
End Get
Set(value As Date)
_datascadenza = value
End Set
End Property
Public Sub New(codart As String, desart As String)
_codice = codart
_descrizione = desart
_datascadenza = New Date(Now.Year + 1, Now.Month, Now.Day)
End Sub
End Class
在类构造函数中,只要求提供代码和描述参数。有效期设置为一年后。定义类的属性是可编辑的,唯一例外是 `Codice`(产品代码),它在创建后无法更改。
接下来,我们将用一些示例数据填充产品列表,这些数据将用于后续步骤。假设我们有一个名为 _MainWindow.xaml_ 的 WPF 窗口。我们将创建一个 `Loaded` 事件的事件管理器,XAML 如下所示:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ERP_test" x:Class="MainWindow"
Title="MainWindow" Height="269"
Width="563" Loaded="Window_Loaded">
</Window>
事件后面的代码将是:
Dim prodotti As List(Of Articolo)
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
prodotti = New List(Of Articolo)
prodotti.Add(New Articolo("Prodotto01", "ARTICOLO TEST"))
prodotti.Add(New Articolo("Prodotto02", "PROVA"))
prodotti.Add(New Articolo("Prodotto03", "NESSUNA DESCRIZIONE"))
End Sub
很简单,我们将有一个全局的 `List(Of Articolo)` 列表,名为 `prodotti`,它在首次执行时包含三个产品。这种结构代表了我们将通过绑定在 `DataGrid` 中公开的数据源。让我们看看如何实现。
DataGrid 上的数据绑定
在我们的窗口中,我们将添加一个名为 `DataGrid1` 的 `DataGrid` 控件。由于我们的结构是在代码中定义的,因此我们必须在此处工作以将控件链接到其数据源。绑定将通过控件的 `ItemsSource` 属性获得。我们的 `Loaded` 事件将修改如下:
Dim prodotti As List(Of Articolo)
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
prodotti = New List(Of Articolo)
prodotti.Add(New Articolo("Prodotto01", "ARTICOLO TEST"))
prodotti.Add(New Articolo("Prodotto02", "PROVA"))
prodotti.Add(New Articolo("Prodotto03", "NESSUNA DESCRIZIONE"))
DataGrid1.ItemsSource = prodotti
End Sub
`ItemsSource` 确实是通过该属性(类型为 `System.Collections.IEnumerable`)来设置(或读取)数据集合,后者用于控件的内容生成(在我们的例子中是 Grid)。`DataGrid` 的绑定已完成:从我们程序第一次执行开始,我们将看到类似以下的输出:
控件之间的数据绑定:Datagrid 到 TextBox 和 DatePicker
正如之前描述的,我们不仅仅需要一个产品列表。我们希望能够通过滚动列表来查看产品详细信息。在实现这种实现时,我们将使用一些 `TextBox` 控件,并在它们(作为接收者)和 `DataGrid`(作为源)之间执行绑定,允许接收控件(`TextBox` 或 `DatePicker`,对于有效期)修改它们的源。让我们将我们的窗口修改如下:
`TextBox` 和 `DatePicker` 中公开控件值的属性分别是 `Text` 和 `SelectedDate`。因此,有必要在这些属性上设置数据绑定。本质上,在识别将接收特定数据的属性后,我们必须指示其源以及其他信息(其中一些是可选的),这些信息将决定这些数据如何交换。在我们的例子中,希望通过接收控件的变化来修改 `DataGrid` 的内容,我们必须使用某种双向方法。
作为要在控件之间执行的操作,可以直接干预 XAML 代码,指示 `TextBox` 和 `DatePicker` 如何链接到 `DataGrid`。与这三个控件相关的代码将修改如下:
<TextBox HorizontalAlignment="Left" Height="23" Margin="445,42,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="181"
Text="{Binding SelectedItem.Codice,ElementName=DataGrid1 , Mode=OneWay }"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="445,70,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="181"
Text="{Binding SelectedItem.Descrizione,ElementName=DataGrid1 , Mode=TwoWay }"/>
<DatePicker HorizontalAlignment="Left" Margin="445,98,0,0"
VerticalAlignment="Top" Width="181"
SelectedDate="{Binding SelectedItem.DataScadenza,ElementName=DataGrid1 ,Mode=TwoWay }"/>
如前所述,公开特定数据的属性必须设置为指示数据源以及检索数据的方式。我们以 #1(公开产品代码 `Codice` 的 `TextBox`)为例:
在这种情况下,我们将看到源控件设置在 `DataGrid1` 上,我们从中读取当前 `SelectedItem` 属于 `Articolo` 类的 `Codice` 属性。绑定的模式是单向的,即从源(`DataGrid1`)到目标(`TextBox`),反之亦然:事实上,我们将 `Codice` 属性定义为只读(参见片段 1),任何尝试更改其值的行为都将引发异常。
类似地,在另外两种情况下,我们将始终以 `DataGrid1` 作为源控件,引用的属性将是 `Descrizione`(描述)和 `DataScadenza`(有效期),但绑定模式将是 `TwoWay`,即双向:修改控件的内容将导致更新其源属性/控件,或 `DataGrid1`(以及扩展到与之关联的列表)。
运行我们的程序,尝试定位到第二个记录(例如)并修改数据,我们可以验证链接控件数据的正确更新:
DataGrid 填充的替代方法
我们在这里看到了一种填充 `DataGrid` 的方法,该方法涉及 `List(Of <Our_Class>)`。说到 WPF 绑定的灵活性,还有其他方法可以填充控件的内容。例如,我们的 `Articolo` 类可以直接在 Grid 的 XAML 中引用。如果我们想让我们的 `DataGrid` 预先加载一些记录,我们可以这样做:
<DataGrid Name="DG1" HorizontalAlignment="Left"
Height="153" Margin="10,10,0,0"
VerticalAlignment="Top" Width="305"
xmlns:local="clr-namespace:WinTest" >
<DataGrid.Columns >
<DataGridTextColumn Header="Codice"
Binding="{Binding Codice}"/>
<DataGridTextColumn Header="Descrizione"
Binding="{Binding Descrizione}"/>
<DataGridTextColumn Header="Data di scadenza"
Binding="{Binding DataScadenza}"/>
</DataGrid.Columns>
<local:Articolo Codice="CODE_01"
Descrizione="MSDN TEST" DataScadenza="2014-12-01" />
</DataGrid>
我们使用 `xmlns:local` 关键字来指定我们的类作为引用(在我的例子中是 `WinTest`)。这样,我们就可以在 XAML 中使用我们命名空间内的每个类(在我的例子中是 `Articolo`)。然后我们定义三个 `DataGridColumns`,每个都与我们类的特定属性绑定。然后,使用 `local` 标签,我们可以定义可变数量的记录,设置每个记录的参数。上面的代码将生成以下窗口:
从这种情况开始,我们可以通过代码添加其他行,使用 `DataGrid` 公开的 `Item` 集合。假设我们的 `DataGrid` 名为 `DataGrid1`,我们可以例如在 `Loaded` 事件中使用以下代码向其中添加第二行:
DataGrid1.Items.Add( New Articolo("CODE_02", "MSDN TEST 2") )
请注意:只要存在动态或静态生成的行(即 `Item` 集合不为空),就无法使用上面讨论的 `ItemsSource` 属性。为了将结构绑定到我们的 `DataGrid`,我们必须首先清空 `Item` 集合(例如,使用类似:`DataGrid1.Items.Clear()` 的代码),然后我们才能成功设置 `ItemsSource` 属性。
历史
- 2015-01-05:已添加源代码
- 2015-01-04:CodeProject 初版发布