ObservableCollection

ObservableCollection
ObservableCollection

ObservableCollection

Du möchtest wissen was eine ObservableCollection ist, wie Sie funktioniert und wie Du Sie in deinen Anwendungen einsetzen kannst?

Lerne das alles in meinem heutigen Beitrag und starte jetzt durch, eine ObservableCollection zu verwenden.

Vielleicht interessieren Dich nach diesem Beitrag auch noch andere Themen wie: WPF DataTemplate, WPF StackPanel, Mahapps Metro.

Listen – Jeder braucht Sie

Ein bekanntes Mittel und fast in keinem Programmierer-Alltag wegzudenken, hilfreich für viele Controls und vor allem im Bereich der Datenbindung – Listen.

Denke zum Beispiel an eine Kundenverwaltung, wo Kunden natürlich hinzugefügt, bearbeitet und gelöscht werden müssen.

Schon fast zu einhundert Prozent wird es dort eine Art Übersicht geben, wo Kunden als Tabelle, simplere Liste, oder vielleicht sogar als Kacheln dargestellt werden.

Sofort stehen wir vor dem Problem, Kunden somit auflisten, oder auch enumerieren zu müssen, damit Diese angezeigt werden.

Die „guten“ WinForms-Zeiten – ohne ObservableCollection

Zu den – sagen wir mal vorsichtig – guten alten Windows FormsZeiten, ist man häufig noch den Weg ohne Datenbindung gegangen.

Ebenso war die Trennung der Ansicht (dem View) und den Daten selbst (Model, ViewModel – je nach Belieben) nicht wirklich vorhanden.

Das sah dann unter Umständen wie in den folgenden Beispielen mit 1 ListBox und 3 Buttons so aus.

Simple Auflistung von Strings

Direkte Modifikation der ListBox

Leider arbeiten wir im gezeigten Beispiel direkt mit dem Control, statt mit einer Art Datenquelle, aus Welcher dann die Daten entspringen.

ListBox Items ohne Binding verwalten
ListBox Items ohne Binding verwalten
Public Class Form1

    Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
        Dim rawInput = InputBox("Enter a new customer", "New customer")
        Dim trimmedInput = rawInput.Trim()
        If String.IsNullOrEmpty(trimmedInput) Then
            MessageBox.Show("Please enter a customer name", "Name missing", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        lbCustomers.Items.Add(trimmedInput)
    End Sub

    Private Sub btnEdit_Click(sender As Object, e As EventArgs) Handles btnEdit.Click
        If lbCustomers.SelectedItem Is Nothing Then
            MessageBox.Show("Please select a customer to edit first", "No customer selected", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        Dim selectedIndex = lbCustomers.SelectedIndex
        Dim selectedCustomer = lbCustomers.Items(selectedIndex).ToString()
        Dim rawInput = InputBox($"Edit customer {selectedCustomer}", "Edit customer", selectedCustomer)
        Dim trimmedInput = rawInput.Trim()
        If String.IsNullOrEmpty(trimmedInput) Then
            MessageBox.Show("Please enter a customer name", "Name missing", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        lbCustomers.Items(selectedIndex) = trimmedInput
    End Sub

    Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
        If lbCustomers.SelectedItem Is Nothing Then
            MessageBox.Show("Please select a customer to remove first", "No customer selected", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        Dim selectedIndex = lbCustomers.SelectedIndex
        lbCustomers.Items.RemoveAt(selectedIndex)
    End Sub

End Class
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic;

namespace ListWithoutDataBindingCS
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            var rawInput = Interaction.InputBox("Enter a new customer", "New customer");
            var trimmedInput = rawInput.Trim();
            if (string.IsNullOrEmpty(trimmedInput))
            {
                MessageBox.Show("Please enter a customer name", "Name missing", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            lbCustomers.Items.Add(trimmedInput);
        }

        private void btnEdit_Click(object sender, EventArgs e)
        {
            if (lbCustomers.SelectedItem == null)
            {
                MessageBox.Show("Please select a customer to edit first", "No customer selected", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            var selectedIndex = lbCustomers.SelectedIndex;
            var selectedCustomer = lbCustomers.Items[selectedIndex].ToString();
            var rawInput = Interaction.InputBox($"Edit customer {selectedCustomer}", "Edit customer", selectedCustomer);
            var trimmedInput = rawInput.Trim();
            if(string.IsNullOrEmpty(trimmedInput))
            {
                MessageBox.Show("Please enter a customer name", "Name missing", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            lbCustomers.Items[selectedIndex] = trimmedInput;
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            if(lbCustomers.SelectedItem == null)
            {
                MessageBox.Show("Please select a customer to remove first", "No customer selected", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            var selectedIndex = lbCustomers.SelectedIndex;
            lbCustomers.Items.RemoveAt(selectedIndex);
        }
    }
}

Mit generischer Liste als Datenquelle

Einen ersten Versuch dazu, könnte man mit einer „List“ wagen, welche wie der Name schon sagt in der Lage ist, Elemente aufzulisten.

Indirekte Modifikation der ListBox durch DatenQuelle
Indirekte Modifikation der ListBox durch DatenQuelle

Dafür erstellen wir einfach eine Instanz der generischen List-Klasse mit dem passenden Typenparameter „string“ und schon geht’s los.

Als erstes legen wir eine neue Eigenschaft namens „Customers“ in der Form an:

Public Property Customers As List(Of String)
public List<string> Customers { get; set; }

Danach instanziieren wir diese Liste im Konstruktor der Form und weisen Diese als Datenquelle für die ListBox via „DataSource„-Eigenschaft zu:

Sub New()
    InitializeComponent()
    Customers = New List(Of String)
    lbCustomers.DataSource = Customers
End Sub
public Form1()
{
    InitializeComponent();
    Customers = new List<string>();
    lbCustomers.DataSource = Customers;
}

Nun können wir den Code zum Hinzufügen, Bearbeiten und Entfernen von Elementen wie folgt anpassen, aus

lbCustomers.Items.Add(trimmedInput)

wird nun:

Customers.Add(trimmedInput)

Die Bearbeitung ändern wie wie folgt:

' Customers.Items(selectedIndex) = trimmedInput
' ->
Customers(selectedIndex) = trimmedInput

Und das Entfernen ebenfalls:

' lbCustomers.Items.RemoveAt(selectedIndex)
' ->
Customers.RemoveAt(selectedIndex)

Klappt leider nicht so ganz, oder doch?

Wenn Du das jetzige Programm einmal ausführst, wirst Du feststellen, dass sich leider nichts tut:

DataBinding mit normaler Liste
DataBinding mit normaler Liste

Jedenfalls auf den ersten Blick denkt man, dass kein String hinzugefügt wird, aber das Gegenteil zeigt die anschließende MessageBox.

Darin sieht man, die Anzahl von einem String in der Auflistung, jedoch zeigt die ListBox Diesen nicht an – warum?

Ganz einfach, die ListBox bekommt nichts von den Änderungen der Auflistung mit!

Mit ObservableCollection

Nun werden wir eine Liste verwenden, Welche in der Lage ist, die an Ihr durchgeführten Änderungen nach außen zu kommunizieren.

Dafür implementiert die „ObservableCollectionunter anderem die „INotifyCollectionChanged„-Schnittstelle.

Jedes Mal wenn wir ein Element hinzufügen oder entfernen, löst die „ObservableCollection“ ein „CollectionChanged“-Ereignis aus.

In den Ereignisdaten können wir dann auf die Art der Änderung wie das Hinzufügen, oder Entfernen von Elementen reagieren.

Doch nicht nur das, auch die bestehenden Steuerelemente wie die ListBox, können dieses Ereignis – sogar ohne unser Zutun – verarbeiten!

Achtung! Dies gilt aber bei der ObservableCollection nur für WPF Steuerelemente, bei Windows Forms müssen wir dazu die BindingList verwenden!

Somit würden die ersten Zeilen unseres WinForms Beispiel wie folgt aussehen:

' somewhere above
' imports System.ComponentModel

Public Property Customers As BindingList(Of String)

Sub New()
    InitializeComponent()
    Customers = New BindingList(Of String)
    ' optional
    AddHandler Customers.ListChanged, AddressOf Customers_ListChanged
    lbCustomers.DataSource = Customers
End Sub

Private Sub Customers_ListChanged(sender As Object, e As ListChangedEventArgs)
    Debug.WriteLine(NameOf(Customers_ListChanged))
End Sub
// somewhere above..
// using System;
// using System.ComponentModel;
// using System.Diagnostics;
// using System.Windows.Forms;
// using Microsoft.VisualBasic;

public BindingList<string> Customers { get; set; }

public Form1()
{
    InitializeComponent();
    Customers = new BindingList<string>();
    Customers.ListChanged += Customers_ListChanged;
}

private void Customers_ListChanged(object sender, ListChangedEventArgs e)
{
    Debug.WriteLine(nameof(Customers_ListChanged));
}

Und das WPF-Beispiel so:

' somewhere above
' Imports System.Collections.ObjectModel
' Imports System.Collections.Specialized

Public Property Customers As ObservableCollection(Of String)

Sub New()
    InitializeComponent()
    Customers = New ObservableCollection(Of String)
    AddHandler Customers.CollectionChanged, AddressOf Customers_CollectionChanged
    lbCustomers.DataSource = Customers
End Sub

Private Sub Customers_CollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
    Debug.WriteLine(NameOf(Customers_CollectionChanged))
End Sub
// somewhere above
// using System;
// using System.Collections.ObjectModel;
// using System.Collections.Specialized;
// using System.Diagnostics;
// using System.Windows.Forms;
// using Microsoft.VisualBasic;

public ObservableCollection<string> Customers { get; set; }

public Form1()
{
    InitializeComponent();
    Customers = new ObservableCollection<string>();
    Customers.CollectionChanged += Customers_CollectionChanged;
}

private void Customers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    Debug.WriteLine(nameof(Customers_CollectionChanged));
}

Downloads

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.