Laden...

SortableBindingList + Virutal Mode im DGV + Sotierprobleme

Erstellt von Siedlerchr vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.751 Views
S
Siedlerchr Themenstarter:in
178 Beiträge seit 2009
vor 11 Jahren
SortableBindingList + Virutal Mode im DGV + Sotierprobleme

Hallo zusammen,

ich habe ne SortableBindingList an ein DGV gehängt. Das funktioniert auch soweit alles.
Das DGV ist im Virtual Mode und da kriege ich beim sortieren - teilweise- Fehlermeldungen:

Fehlermeldung:

Standard-Fehlerdialogfeld für DataGridView

DataGridView-Ausnahme:
System.IndexOutOfRangeException: Der Index 6 hat keinen Wert.

bei System.Windows.Forms.CurrencyManager.get_Item(Int32 index)

bei System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex

Behandeln Sie das DataError-Ereignis, um dieses Standarddialogfeld zu ersetzen.

Das eiinzige was mir zu diesem Fehler einfällt, ist das das für jedes Element im DGV/Liste kommt, der IndexWert entspricht der Row.

Was vielleicht noch interessant ist: Die Liste die als DataSource am DGV hängt wird von einem Thread in 1000 Schritten befüllt und nach jedem Schritt wird die DataSource wieder auf die Liste gesetzt.

Hier noch mein CellValueNeeded Event:

private void dgvDeleteRuns_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            e.Value = allRuns[e.RowIndex];
        }

Wenn ich die Liste z.B: per Where einschränke und das Ergebnis als neue Liste ans DGV hänge (auch eine SortableBindingList), dann funktioniert das Sortieren einwandfrei....

Ich habe auch schon probiert. die LIste ans DGV als DataSource zu setzen, wenn alle Daten geladen sind. Aber da geht das sortieren ebenfalls nicht.

Hat jemand ne Idee warum das nur bei alllen nicht geht? Ich hab mit dem VirtaulMode noch nicht so viel Erfahrung gesammelt.

F
10.010 Beiträge seit 2004
vor 11 Jahren

Wenn du den IndexOutOfRangeException bekommst ist die Liste anscheinend noch nicht bis dahin gefüllt.
Setzt du evtl RowCount bevor du die Daten gelesen hast?

Die Liste die als DataSource am DGV hängt wird von einem Thread in 1000 Schritten befüllt und nach jedem Schritt wird die DataSource wieder auf die Liste gesetzt.

Was denn nun, Virtual mode oder DataSource, beides macht keinen sinn.

S
Siedlerchr Themenstarter:in
178 Beiträge seit 2009
vor 11 Jahren

Was denn nun, Virtual mode oder DataSource, beides macht keinen sinn.

o.O - Hm, dann muss ich mich doch nochmal genauer einlesen. Ich hatte das bisher so verstanden, dass das Virtual Mode nur dann funktioniert, wenn ich auch ne DataSource habe und meine Items DataBound sind

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Siedlerchr,

Die Liste die als DataSource am DGV hängt wird von einem Thread in 1000 Schritten befüllt

das ist nicht erlaubt und kann zu abstrusen Fehlern führen, siehe [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke).

herbivore

S
Siedlerchr Themenstarter:in
178 Beiträge seit 2009
vor 11 Jahren

Genau das war mein Problem. Habe das Problem jetzt gelöst, in dem ich die Daten vorher nochmal in eine neue Liste übertrag und diese dann komplett ans DGV hänge.
So klappt es. Ist zwar performancemäßig nicht so toll, aber erstmal ein Anfang

Gruß
Siedlerchr

Y
238 Beiträge seit 2005
vor 11 Jahren

Hallo Siedlerchr,

Die Liste die als DataSource am DGV hängt wird von einem Thread in 1000 Schritten befüllt
das ist nicht erlaubt und kann zu abstrusen Fehlern führen...

Das mit "nicht erlaubt" finde ich etwas zu scharf formuliert... Sagen wir es mal so, wenn deine (Sortable)BindingList Implementierung sich intern mit dem UI-Thread synchronisiert, dann würde herbivore es erlauben, oder? 😉

Siedlerchr, was ich irgendwie nicht ganz verstehe ist die Kombination aus VirtualMode und einer DataSource... Macht so etwas Sinn, bzw schliesst es sich nicht gegenseitig aus? Kannst du eventuell mal einen Link posten wo man über diese Art der Datenbindung nachlesen kann?

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo yngwie,

falls eine andere Synchronisation mit dem GUI-Thread möglich wäre, wäre das ausreichend, aber sie ist nicht möglich. Deshalb ist es zwingend, die Änderung von gebundenen Daten im GUI-Thread durchzuführen. Ich bleibe also dabei. Es ist nicht erlaubt, die gebundenen Daten in einem anderen Thread zu ändern.

herbivore.

Y
238 Beiträge seit 2005
vor 11 Jahren

Hallo herbivore,

...falls eine andere Synchronisation mit dem GUI-Thread möglich wäre, wäre das ausreichend, aber sie ist nicht möglich...

Wieso ist sie denn so kategorisch nicht möglich? Ich habe eine BindingList implementiert die neben Suchen und Sortieren sich bei Bedarf auch noch mit dem UI-Thread synchronisiert. Diese verwende ich fürs Databinding, sogar im Produktivumfeld... funktioniert sehr gut.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo yngwie,

ok, wenn du eine eigene thread-safe BindingList verwendest und auch alle direkt und indirekt in dieser BindingList enthaltenen Objekte, deren Properties, Methoden, Events usw. von sich heraus thread-safe sind, würde es vielleicht gehen. Wobei ich es für wirklich schwierig halte, dies alles (und insbesondere den Enumerator) wasserdicht hinzubekommen und bei Änderungen und Erweiterungen wasserdicht zu halten. [EDIT]Selbst wenn man die Synchronisation der Daten perfekt hinbekommt, ist damit immer noch nicht verhindert, dass gleichzeitige Zugriffe auf das Control selber erfolgen und dieses dadurch inkonsistent wird.[/EDIT]

Was dagegen nicht reicht - und davon war ich ausgegangen -, ist alle Zugriffe auf die Daten aus dem Worker-Thread zu locken. Das reicht nicht, weil das Control von sich aus kein lock beim Zugriff auf die Daten benutzt. Und wenn nur einer von zwei Threads das lock benutzt und der andere nicht, hat man überhaupt nichts gewonnen.

Es mag sein, dass ich meine Aussage in der Absolutheit nicht aufrecht erhalten kann (zumindest wenn du wirklich aufzeigen kannst, wie man den Enumerator thread-safe hinbekommt), aber ich denke, dass deiner Alternative in der Praxis keine größere Bedeutung zu kommt. Vereinfacht würde ich daher immer noch, bei "das ist nicht erlaubt" bleibend, wohl wissend, dass es so gut wie keine Regel ohne Ausnahme gibt. [EDIT]Ich halte die Aussage in ihrer Absolutheit aufrecht[/EDIT]

herbivore

Y
238 Beiträge seit 2005
vor 11 Jahren

Hallo herbivore,

... ok, wenn du eine eigene thread-safe BindingList verwendest und auch alle direkt und indirekt in dieser BindingList enthaltenen Objekte, deren Properties, Methoden, Events usw. von sich heraus thread-safe sind, würde es vielleicht gehen...

ach nein, so aufwändig muss man es wirlich nicht machen. Zum "aufzeigen" bin ich zu faul, ich sage nur dass der Enumerator in einem typischen Databinding-Scenario keine Rolle spielt. Du kannst mal nach "AsyncBindingList" googlen und dich von der Demoapp überzeugen lassen 😉 Und was die Bedeutung für die Praxis angeht... nun nicht selten steht man vor der Aufgabe ein sogenanntes "responsive UI" zu realisieren. Mit einer angepassten BindingList kann man diese Arbeit schneller erledigen und dabei das Komfort von Databinding geniessen. Finde ich äußerst praxisrelevant 😉

Gruß

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo yngwie,

der Beitrag Updating DataBound Controls asynchronously: AsyncBindingList<T> von Daniel Herling bestätigt in jeder Hinsicht meine Meinung:

However, Windows Forms controls can only be updated on the UI thread. So, if SQL Server changes the string on a different thread than the UI thread updating the applications title on the same thread would be a problem. Luckily, Windows Forms controls have two methods which can be called from any thread in order to update the controls: BeginInvoke and Invoke.

Der Trick der AsyncBindingList ist nun, dass sie sich von selbst darum kümmert, dass das passiert. Es ist also keineswegs so, dass die Datenänderung im Worker-Thread erfolgt, sondern sie wird, wie ich das von Anfang an empfohlen bzw. gefordert habe, an den GUI-Thread delegiert. Ob das Delegieren nun explizit durch den Code des Worker-Thread erfolgt oder implizit durch die AsyncBindingList ändert nichts daran, dass es passieren muss.

The philosophy behind the AsyncBindingList<T> is to get the updates from the background thread and forward them to the UI thread using WindowsFormsSynchronizationContext.

Dass WindowsFormsSynchronizationContext benutzt wird, tut nichts zu Sache, denn intern benutzt WindowsFormsSynchronizationContext einfach Control.Invoke bzw. Control.BeginInvoke.

ok, wenn du eine eigene thread-safe BindingList verwendest und auch alle direkt und indirekt in dieser BindingList enthaltenen Objekte, deren Properties, Methoden, Events usw. von sich heraus thread-safe sind, würde es vielleicht gehen

Diese Aussage ziehe ich - nachdem ich nochmal darüber nachgedacht habe - auch wieder zurück. Damit würde man es vielleicht schaffen, dass die Daten selbst konsistent bleiben, aber damit verhindert man nicht, dass direkte Zugriffe auf das Control aus dem Worker-Thread erfolgen, was eben nicht erlaubt ist. Mindestens das Control selbst könnte somit selbst trotz perfekter Synchronisierung der Daten immer noch inkonsistent werden.

Ich komme also wieder zu meiner absoluten Haltung zurück: Zugriffe auf gebundene Daten sind aus dem Worker-Thread nicht erlaubt. Punkt! Sie müssen aus dem GUI-Thread erfolgen.

Dass einem die AsyncBindingList<T> diese Aufgabe abnehmen kann, ist sicher praktisch. Aber ich habe nur gesagt, dass diese Aufgabe ausgeführt werden muss. Darüber, wie man die Aufgabe am besten durchführt, also explizit oder per AsyncBindingList<T>, habe ich keine Aussage getroffen.

herbivore