Freigeben über


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 IBindingListist, 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:

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