ObservableCollection
Inhaltsverzeichnis
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 Forms–Zeiten, 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
Leider arbeiten wir im gezeigten Beispiel direkt mit dem Control, statt mit einer Art Datenquelle, aus Welcher dann die Daten entspringen.
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.
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:
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 „ObservableCollection“ unter 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)); }