Mit einer BindingList<T>
bzw. einer IBindingList
kann in WinForms leicht ein Two-Way-DataBinding an ein DataGridView
ermöglicht werden, indem man dessen DataSource
-Property auf die BindingList
setzt. Vorteil dabei: die Anzeige des DataGridView
wird stets aktuell gehalten mit den Elementen in der BindingList
, zusätzlich auch mit den Daten innerhalb der einzelnen Elemente, wenn diese INotifyPropertyChanged
implementieren.
Doch die DataBinding-Magie hat ein Ende, wenn die BindingList
in einem zweiten Thread parallel zum UI-Thread aktualisiert bzw. verändert wird. Die Änderung im zweiten Thread wird über das ListChanged
-Event der BindingList
an das DataGridView
publiziert, das aber im UI-Thread läuft und daher ein Cross-Thread-Problem bekommt:
Fehlermeldung: "System.InvalidOperationException – Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement ‚xyz‘ erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde."
Bzw. auf Englisch: "System.InvalidOperationException – Cross-thread operation not valid: Control ‚xyz‘ accessed from a thread other than the thread it was created on."
Da die Aktualisierung des Grids automatisch erfolgt, hat man als Entwickler keine Chance in den Prozess einzugreifen und z.B. mit InvokeRequired
zu prüfen, ob die Operation im UI-Thread stattfinden muss.
Abhilfe schafft eine eigene BindingList
, die sich des Threading-Problems mit Hilfe der SynchronizationContext
-Klasse annimmt und Updates so im UI-Thread auslöst. Folgende ThreadedBindingList
(Modifikation einer Lösung von Stack Overflow) setzt dies um:
1: public class ThreadedBindingList<T> : BindingList<T>
2: {
3: private readonly SynchronizationContext m_syncContext = SynchronizationContext.Current;
4:
5: protected override void OnAddingNew(AddingNewEventArgs e)
6: {
7: if (m_syncContext == null)
8: BaseAddingNew(e);
9: else
10: m_syncContext.Send(state => BaseAddingNew(e), null);
11: }
12:
13: protected override void OnListChanged(ListChangedEventArgs e)
14: {
15: if (m_syncContext == null)
16: base.OnListChanged(e);
17: else
18: m_syncContext.Send(state => BaseListChanged(e), null);
19: }
20:
21: private void BaseAddingNew(AddingNewEventArgs e)
22: {
23: base.OnAddingNew(e);
24: }
25:
26: private void BaseListChanged(ListChangedEventArgs e)
27: {
28: base.OnListChanged(e);
29: }
30: }
Eine Instanz dieser Liste muss im UI-Thread erzeugt werden, sodass der SynchronizationContext
korrekt gesetzt werden kann. Alternativ sind andere Implementierungen denkbar, die den SynchronizationContext
per Konstruktor übergeben bekommen.
Sie sehen gerade einen Platzhalterinhalt von Facebook. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr InformationenSie sehen gerade einen Platzhalterinhalt von Instagram. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr InformationenSie sehen gerade einen Platzhalterinhalt von X. Um auf den eigentlichen Inhalt zuzugreifen, klicken Sie auf die Schaltfläche unten. Bitte beachten Sie, dass dabei Daten an Drittanbieter weitergegeben werden.
Mehr Informationen