Entwerfen großartiger Datenquellen mit Änderungsbenachrichtigung (Windows Forms .NET)
Eines der wichtigsten Konzepte der Windows Forms-Datenbindung ist die Änderungsbenachrichtigung. Um sicherzustellen, dass Ihre Datenquelle und gebundenen Steuerelemente immer über die neuesten Daten verfügen, müssen Sie eine Änderungsbenachrichtigung für die Datenbindung hinzufügen. Insbesondere möchten Sie sicherstellen, dass gebundene Steuerelemente über Änderungen benachrichtigt werden, die an ihrer Datenquelle vorgenommen wurden. Die Datenquelle wird über Änderungen benachrichtigt, die an die gebundenen Eigenschaften eines Steuerelements vorgenommen wurden.
Je nach Art der Datenbindung gibt es verschiedene Arten von Änderungsbenachrichtigungen:
Einfache Bindung, bei der eine einzelne Steuerelementeigenschaft an eine einzelne Instanz eines Objekts gebunden ist.
Listenbasierte Bindung, die eine einzelne Steuerelementeigenschaft enthalten kann, die an die Eigenschaft eines Elements in einer Liste oder eine Steuerelementeigenschaft gebunden ist, die an eine Liste von Objekten gebunden ist.
Wenn Sie außerdem Windows Forms-Steuerelemente erstellen, die Sie für die Datenbindung verwenden möchten, müssen Sie das PropertyNameChanged-Pattern auf die Steuerelemente anwenden. Das Anwenden eines Musters auf die Steuerelemente führt zu Änderungen an der gebundenen Eigenschaft eines Steuerelements, die an die Datenquelle weitergegeben werden.
Änderungsbenachrichtigung für einfache Bindung
Bei einfacher Bindung müssen Geschäftsobjekte Änderungsbenachrichtigungen bereitstellen, wenn sich der Wert einer gebundenen Eigenschaft ändert. Sie können eine Änderungsbenachrichtigung bereitstellen, indem Sie für jede Eigenschaft Ihres Geschäftsobjekts ein PropertyNameChanged-Ereignis bereitstellen. Außerdem muss das Geschäftsobjekt entweder an Steuerelemente mit der BindingSource-Methode oder mit der bevorzugten Methode gebunden werden, in der Ihr Geschäftsobjekt die INotifyPropertyChanged-Schnittstelle implementiert und ein PropertyChanged-Ereignis auslöst, wenn sich der Wert einer Eigenschaft ändert. Wenn Sie Objekte verwenden, die die INotifyPropertyChanged
Schnittstelle implementieren, müssen Sie die BindingSource
nicht verwenden, um das Objekt an ein Steuerelement zu binden. Die Verwendung der BindingSource
wird jedoch empfohlen.
Änderungsbenachrichtigung für listenbasierte Bindung
Windows Forms hängt von einer gebundenen Liste ab, um Informationen über Eigenschaftsänderungen und Listenänderungen an gebundene Steuerelemente bereitzustellen. Die Eigenschaftsänderung ist eine Änderung des Listenelementeigenschaftswerts, und die Listenänderung bedeutet, dass ein Element gelöscht oder der Liste hinzugefügt wird. Daher müssen Listen, die für die Datenbindung verwendet werden, das IBindingListimplementieren, das beide Arten von Änderungsbenachrichtigungen bereitstellt. Die BindingList<T> ist eine generische Implementierung von IBindingList
und wurde für die Verwendung mit Windows Forms-Datenbindung konzipiert. Sie können ein BindingList
erstellen, das einen Geschäftsobjekttyp enthält, der INotifyPropertyChanged implementiert. Die Liste konvertiert die PropertyChanged-Ereignisse automatisch in ListChanged-Ereignisse. Wenn die gebundene Liste kein IBindingList
ist, müssen Sie die Objekte mithilfe der Komponente BindingSource an Windows Forms-Steuerelemente binden. Die BindingSource
-Komponente bietet eine Konvertierung von Eigenschaften zu Listen, ähnlich wie bei der BindingList
. Weitere Informationen finden Sie unter So heben Sie Änderungsbenachrichtigungen mithilfe einer BindingSource und der INotifyPropertyChanged-Schnittstelle hervor.
Änderungsbenachrichtigung für benutzerdefinierte Steuerelemente
Schließlich müssen Sie auf der Steuerelementseite ein PropertyNameChanged-Ereignis für jede Eigenschaft verfügbar machen, die an Daten gebunden werden soll. Die Änderungen an der Steuerelementeigenschaft werden dann an die gebundene Datenquelle weitergegeben. Weitere Informationen finden Sie unter Anwenden des PropertyNameChanged-Musters.
Anwenden des PropertyNameChanged-Musters
Im folgenden Codebeispiel wird veranschaulicht, wie das PropertyNameGeändertes Muster auf ein benutzerdefiniertes Steuerelement angewendet wird. Wenden Sie das Muster an, wenn Sie benutzerdefinierte Steuerelemente implementieren, die mit dem Windows Forms-Datenbindungsmodul verwendet werden.
// This class implements a simple user control
// that demonstrates how to apply the propertyNameChanged pattern.
[ComplexBindingProperties("DataSource", "DataMember")]
public class CustomerControl : UserControl
{
private DataGridView dataGridView1;
private Label label1;
private DateTime lastUpdate = DateTime.Now;
public EventHandler DataSourceChanged;
public object DataSource
{
get
{
return this.dataGridView1.DataSource;
}
set
{
if (DataSource != value)
{
this.dataGridView1.DataSource = value;
OnDataSourceChanged();
}
}
}
public string DataMember
{
get { return this.dataGridView1.DataMember; }
set { this.dataGridView1.DataMember = value; }
}
private void OnDataSourceChanged()
{
if (DataSourceChanged != null)
{
DataSourceChanged(this, new EventArgs());
}
}
public CustomerControl()
{
this.dataGridView1 = new System.Windows.Forms.DataGridView();
this.label1 = new System.Windows.Forms.Label();
this.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridView1.ImeMode = System.Windows.Forms.ImeMode.Disable;
this.dataGridView1.Location = new System.Drawing.Point(100, 100);
this.dataGridView1.Size = new System.Drawing.Size(500,500);
this.dataGridView1.TabIndex = 1;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(50, 50);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(76, 13);
this.label1.TabIndex = 2;
this.label1.Text = "Customer List:";
this.Controls.Add(this.label1);
this.Controls.Add(this.dataGridView1);
this.Size = new System.Drawing.Size(450, 250);
}
}
' This class implements a simple user control
' that demonstrates how to apply the propertyNameChanged pattern.
<ComplexBindingProperties("DataSource", "DataMember")>
Public Class CustomerControl
Inherits UserControl
Private dataGridView1 As DataGridView
Private label1 As Label
Private lastUpdate As DateTime = DateTime.Now
Public DataSourceChanged As EventHandler
Public Property DataSource() As Object
Get
Return Me.dataGridView1.DataSource
End Get
Set
If DataSource IsNot Value Then
Me.dataGridView1.DataSource = Value
OnDataSourceChanged()
End If
End Set
End Property
Public Property DataMember() As String
Get
Return Me.dataGridView1.DataMember
End Get
Set
Me.dataGridView1.DataMember = Value
End Set
End Property
Private Sub OnDataSourceChanged()
If (DataSourceChanged IsNot Nothing) Then
DataSourceChanged(Me, New EventArgs())
End If
End Sub
Public Sub New()
Me.dataGridView1 = New System.Windows.Forms.DataGridView()
Me.label1 = New System.Windows.Forms.Label()
Me.dataGridView1.ColumnHeadersHeightSizeMode =
System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.dataGridView1.ImeMode = System.Windows.Forms.ImeMode.Disable
Me.dataGridView1.Location = New System.Drawing.Point(19, 55)
Me.dataGridView1.Size = New System.Drawing.Size(550, 150)
Me.dataGridView1.TabIndex = 1
Me.label1.AutoSize = True
Me.label1.Location = New System.Drawing.Point(19, 23)
Me.label1.Name = "label1"
Me.label1.Size = New System.Drawing.Size(76, 13)
Me.label1.TabIndex = 2
Me.label1.Text = "Customer List:"
Me.Controls.Add(Me.label1)
Me.Controls.Add(Me.dataGridView1)
Me.Size = New System.Drawing.Size(650, 300)
End Sub
End Class
Implementieren der INotifyPropertyChanged-Schnittstelle
Im folgenden Codebeispiel wird veranschaulicht, wie die INotifyPropertyChanged-Schnittstelle implementiert wird. Implementieren Sie die Schnittstelle für Geschäftsobjekte, die in der Windows Forms-Datenbindung verwendet werden. Bei der Implementierung teilt die Schnittstelle einem gebundenen Steuerelement die Eigenschaftsänderungen eines Geschäftsobjekts mit.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
// Change the namespace to the project name.
namespace binding_control_example
{
// This form demonstrates using a BindingSource to bind
// a list to a DataGridView control. The list does not
// raise change notifications. However the DemoCustomer1 type
// in the list does.
public partial class Form3 : Form
{
// This button causes the value of a list element to be changed.
private Button changeItemBtn = new Button();
// This DataGridView control displays the contents of the list.
private DataGridView customersDataGridView = new DataGridView();
// This BindingSource binds the list to the DataGridView control.
private BindingSource customersBindingSource = new BindingSource();
public Form3()
{
InitializeComponent();
// Set up the "Change Item" button.
this.changeItemBtn.Text = "Change Item";
this.changeItemBtn.Dock = DockStyle.Bottom;
this.changeItemBtn.Height = 100;
//this.changeItemBtn.Click +=
// new EventHandler(changeItemBtn_Click);
this.Controls.Add(this.changeItemBtn);
// Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top;
this.Controls.Add(customersDataGridView);
this.Size = new Size(400, 200);
}
private void Form3_Load(object sender, EventArgs e)
{
this.Top = 100;
this.Left = 100;
this.Height = 600;
this.Width = 1000;
// Create and populate the list of DemoCustomer objects
// which will supply data to the DataGridView.
BindingList<DemoCustomer1> customerList = new ();
customerList.Add(DemoCustomer1.CreateNewCustomer());
customerList.Add(DemoCustomer1.CreateNewCustomer());
customerList.Add(DemoCustomer1.CreateNewCustomer());
// Bind the list to the BindingSource.
this.customersBindingSource.DataSource = customerList;
// Attach the BindingSource to the DataGridView.
this.customersDataGridView.DataSource =
this.customersBindingSource;
}
// Change the value of the CompanyName property for the first
// item in the list when the "Change Item" button is clicked.
void changeItemBtn_Click(object sender, EventArgs e)
{
// Get a reference to the list from the BindingSource.
BindingList<DemoCustomer1>? customerList =
this.customersBindingSource.DataSource as BindingList<DemoCustomer1>;
// Change the value of the CompanyName property for the
// first item in the list.
customerList[0].CustomerName = "Tailspin Toys";
customerList[0].PhoneNumber = "(708)555-0150";
}
}
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer1 : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
private DemoCustomer1()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer1 CreateNewCustomer()
{
return new DemoCustomer1();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
}
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Runtime.CompilerServices
Imports System.Windows.Forms
' This form demonstrates using a BindingSource to bind
' a list to a DataGridView control. The list does not
' raise change notifications. However the DemoCustomer1 type
' in the list does.
Public Class Form3
Inherits System.Windows.Forms.Form
' This button causes the value of a list element to be changed.
Private changeItemBtn As New Button()
' This DataGridView control displays the contents of the list.
Private customersDataGridView As New DataGridView()
' This BindingSource binds the list to the DataGridView control.
Private customersBindingSource As New BindingSource()
Public Sub New()
InitializeComponent()
' Set up the "Change Item" button.
Me.changeItemBtn.Text = "Change Item"
Me.changeItemBtn.Dock = DockStyle.Bottom
Me.changeItemBtn.Size = New System.Drawing.Size(100, 100)
AddHandler Me.changeItemBtn.Click, AddressOf changeItemBtn_Click
Me.Controls.Add(Me.changeItemBtn)
' Set up the DataGridView.
customersDataGridView.Dock = DockStyle.Top
Me.Controls.Add(customersDataGridView)
Me.Size = New Size(400, 200)
End Sub
Private Sub Form3_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Me.Load
Me.Top = 100
Me.Left = 100
Me.Height = 600
Me.Width = 1000
' Create and populate the list of DemoCustomer1 objects
' which will supply data to the DataGridView.
Dim customerList As New BindingList(Of DemoCustomer1)
customerList.Add(DemoCustomer1.CreateNewCustomer())
customerList.Add(DemoCustomer1.CreateNewCustomer())
customerList.Add(DemoCustomer1.CreateNewCustomer())
' Bind the list to the BindingSource.
Me.customersBindingSource.DataSource = customerList
' Attach the BindingSource to the DataGridView.
Me.customersDataGridView.DataSource = Me.customersBindingSource
End Sub
' This event handler changes the value of the CompanyName
' property for the first item in the list.
Private Sub changeItemBtn_Click(ByVal sender As Object, ByVal e As EventArgs)
' Get a reference to the list from the BindingSource.
Dim customerList As BindingList(Of DemoCustomer1) =
CType(customersBindingSource.DataSource, BindingList(Of DemoCustomer1))
' Change the value of the CompanyName property for the
' first item in the list.
customerList(0).CustomerName = "Tailspin Toys"
customerList(0).PhoneNumber = "(708)555-0150"
End Sub
End Class
' This class implements a simple customer type
' that implements the IPropertyChange interface.
Public Class DemoCustomer1
Implements INotifyPropertyChanged
' These fields hold the values for the public properties.
Private idValue As Guid = Guid.NewGuid()
Private customerNameValue As String = String.Empty
Private phoneNumberValue As String = String.Empty
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
' This method is called by the Set accessor of each property.
' The CallerMemberName attribute that is applied to the optional propertyName
' parameter causes the property name of the caller to be substituted as an argument.
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
' The constructor is private to enforce the factory pattern.
Private Sub New()
customerNameValue = "Customer"
phoneNumberValue = "(312)555-0100"
End Sub
' This is the public factory method.
Public Shared Function CreateNewCustomer() As DemoCustomer1
Return New DemoCustomer1()
End Function
' This property represents an ID, suitable
' for use as a primary key in a database.
Public ReadOnly Property ID() As Guid
Get
Return Me.idValue
End Get
End Property
Public Property CustomerName() As String
Get
Return Me.customerNameValue
End Get
Set(ByVal value As String)
If Not (value = customerNameValue) Then
Me.customerNameValue = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Property PhoneNumber() As String
Get
Return Me.phoneNumberValue
End Get
Set(ByVal value As String)
If Not (value = phoneNumberValue) Then
Me.phoneNumberValue = value
NotifyPropertyChanged()
End If
End Set
End Property
End Class
Synchronisieren von Bindungen
Bei der Implementierung der Datenbindung in Windows Forms sind mehrere Steuerelemente an dieselbe Datenquelle gebunden. In einigen Fällen kann es erforderlich sein, zusätzliche Schritte auszuführen, um sicherzustellen, dass die gebundenen Eigenschaften der Steuerelemente miteinander und der Datenquelle synchronisiert bleiben. Diese Schritte sind in zwei Situationen erforderlich:
Wenn die Datenquelle keine IBindingListimplementiert und daher ListChanged Ereignisse vom Typ ItemChangedgeneriert.
Wenn die Datenquelle IEditableObjectimplementiert.
Im früheren Fall können Sie eine BindingSource verwenden, um die Datenquelle mit den Steuerelementen zu verbinden. Im letzteren Fall verwenden Sie eine BindingSource
, behandeln das BindingComplete-Ereignis und rufen EndCurrentEdit an der zugeordneten BindingManagerBaseauf.
Weitere Informationen zur Implementierung dieses Konzepts finden Sie auf der BindingComplete-API-Referenzseite.
Siehe auch
.NET Desktop feedback