Fragensteller
DataGridView: Exception in CellFormatting Event führt zum Anwendungsabsturz

Allgemeine Diskussion
-
Hi,
ich benutze häufig das DataGridView aus den Windows Forms. Exceptions in den entsprechenden Windows Forms Anwendung Fange ich in dem Application.ThreadException Event. So weit so gut. Das Problem ist, wenn ich in einem CellFormatting Event eine Ausnahme auslöse, diese nicht nach weitergereicht wird. Vielmehr wird jedesmal wieder für den gleichen Eintrag das CellFormatting Event ausgelöst und dann bricht die Anwendung einfach weg (denke mal Stack Overflow). Gibt es da eine Möglichkeit das Abstürzen der Anwendung zu verhindern!?
Gruß
Martin
- Typ geändert Robert BreitenhoferModerator Dienstag, 8. November 2011 14:10 Keine Rückmeldung des Fragenstellender
Alle Antworten
-
Hallo Martin,
Vermutlich ist nicht das Werfen der Ausnahme an sich der Grund für die StackOverflowException, sondern das, was dein Code dabei sonst noch ausführt. Wenn Du z.B. auch nur einfach statt currentCell.Value den Wert von currentCell.FormattedValue im CellFormatting-Handler ausliest, führt das unweigerlich zu einer StackOverflowException, da dies einen rekursiven Aufruf von OnCellFormatting zur Folge hat. Die CLR beendet dabei den Prozess noch bevor der Debugger eine Chance hat, sich einzuschalten. Was genau steht denn in deinem CellFormatting-Handler?
Edit: Als erste Maßnahme würde ich den Code im CellFormatting-Handler in einem try/catch-Block einschließen. Überprüfe bitte auch, ob Du value.ToString() o.ä. aufrufst.
Gruß
Marcel
- Bearbeitet Marcel RomaModerator Montag, 17. Oktober 2011 10:38
-
Hallo Martin,
im CellFormatting Ereignis sollte man IMO keine Ausnahme selbst auslösen und auftretende Ausnahmen lokal behandeln.
Und wenn aufgrund der Daten kein "vernünftiges" Ergebnis einen Standardwert zurückliefern und sei es etwas wie das
aus Excel bekannte "#ERROR". Denn irgendwas muss schließlich am Ende in der Zelle stehen, wenn es weitergehen soll.Was die Ausnahmebehandlung angeht:
Man sollte man das ThreadException sowie das AppDomain.UnhandledException Ereignis als letzte Maßnahme
vor dem Anwendungstod betrachten. Ausnahmen sollte aber i. a. dort behandelt werden, wo sie auftreten.
Denn in allgemeinen Behandlungsroutinen kann man bestenfalls noch erahnen, wo das Problem liegt.
Und dort eine Ausnahme zu "schlucken" kann schnell dazu führen, dass später (unerklärlicher) Datenverlust / -fehler auftreten.Beim DataGridView wäre das DataError-Ereignis für eine verallgemeinerte Fehlerbehandlung vorzuziehen.
Das Auslösen von Ausnahmen zur Steuerung sollte man grundsätzlich vermeiden.
Nicht nur das es relativ teuer ist, es schafft auch unnötige Abhängigkeiten zum Aufrufer,
der diese Ausnahmen als "Sonderfall" behandeln muss.
Sinnvoller ist ein Try... Muster, wie es z. B. beim Parsen von Daten verwendet wird, z. B. Int32.TryParse
oder auch beim Zugriff auf Auflistungen, z. B. Dictionary.TryGetValue.Einige Ausnahmen wie die StackoverflowException lassen sich wiederum gar nicht behandeln, sondern führen
immer zum Anwendungsabbruch, da danach kein konsistenter Prozesszustand herzustellen ist.
Eine StackOverflow Ausnahme ist zudem i. a. als Programmierfehler anzusehen.Gruß Elmar
-
Hallo Elmar,
ich gucke eigentlich schon, dass in dem CellFormatting Event keine Ausnahmen auftreten. Aber kann ja immer mal passieren und dann ist es natürlich blöd, wenn die Anwendung einfach wegbricht. Und da try/catch Anweigen doch schon einen gewissen Overhead verursachen, möchte ich die nun ungern in einem Handler einbauen, der sehr häfig aufgerufen wird.
Mein Stand mit dem ThreadException Event ist allerdings, das dass die 1. Wahl ist und das man nicht die Anwendung mit try/catch Blöcken vollbauen soll, weil diese eben einen recht hohen Overhead produzieren. Dort resete ich dann die Anwendung und setzte sie auf den Start Zustand zurück.
Das DataError Event wird aber leider nicht aufgerufen, wenn man eine Exception in CellFormatting produziert. Hatte ich bereits ausprobiert. Es ist ja eigentlich auch kein Datenfehler.
Der Stackoverflow wird ja vom Grid selber prodoziert. Allerdings im externen Code, man sieht da leider im Debugger nichts. Irgendwie scheint das Grid die Ausnahmen selbst zu fangen und dann zu verwerfen.
Gruß
Martin
-
Hallo Martin,
was die Ausnahme angeht ist das Gegenteil der Fall.
Der größte Overhead entsteht zum einen bereits beim Erzeugen der Ausnahme,
da die CLR dafür den Stack sichern muß usw.Wenn die Ausnahme aufgetreten ist und die lokale Methode über keine Ausnahmebehandlung verfügt,
so muss die CLR den Aufrufstack nach einer solchen durchsuchen und erst wenn sie keine findet,
so wird das ThreadException Ereignis ausgelöst.
Und es dürfte offensichtlich sein, dass diese Suche / Behandlung mehr Zeit in Anspruch nimmt.Und so ist ein lokaler Try Catch Block immer vorzuziehen - und die Ausnahme sollte ihrem Namen genügen,
d. h. der Entwickler sollte die gängigen Problemfälle bereits vorher behandeln.Eine simple Behandlung - bei der ich eine Ausnahme provoziert habe:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { var grid = sender as DataGridView; if (e.RowIndex >= 0 && e.ColumnIndex >= 0) { // Console.WriteLine("({0}, {1}) = {2}", e.RowIndex, e.ColumnIndex, grid.Columns[e.ColumnIndex].DataPropertyName); if (grid.Columns[e.ColumnIndex].DataPropertyName == "PostalCode") { try { // geht garantiert schief if (e.Value is DBNull) { e.Value = Int32.Parse((string)e.Value); } } catch (Exception) { e.Value = "#ERROR#"; } } } }
Das DataError Ereignis wird nicht aufgerufen, wenn die Größe für TextBoxCell (GetPreferredSize) ermittelt wird.
Dazu wird auch der formatierte Wert abgerufen, dabei aber keine Fehlerbehandlung via DataError vorgenommen.
(Was zugegeben etwas ungeschickt an der Stelle ist, aber auch verdeutlicht, dass man dort nicht mit Ausnahmen
um sich werfen sollte).Die StackOverflowException ist an der Stelle keine Regelausnahme, da dürften noch einige Dinge in Deinem Code dazukommen.
Und Du um die Stelle leichter zu lokalisieren, kannst Du Dir - wie oben auskommentiert -
einige Infos über Console/Debug.WriteLine ins Ausgabefenster ausgeben lassen.Es wäre hilfreich, wenn Du einen Ausschnitt Deiner Formatierung zeigst - soweit sie der Fehlerverursacher ist.
Gruß Elmar
-
Hi,
also der Code sieht folgendermaßen aus (nicht wundern, da sind einige Erweiterungsmethoden drin):
private void dataGridViewStammData_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if ( this.dataGridViewStammData.IsValidCell(e) ) { MaintenanceItem lMaintenanceItem = this.dataGridViewStammData.GetDataBoundItem<MaintenanceItem>(e); e.CellStyle.ForeColor = MaintenanceStates.toColor((MaintenanceStates.EditState) lMaintenanceItem.State); if ( this.dataGridViewStammData.Columns.IsColumn(e, "Key") ) { DataVersion lDataVersion = DataVersion.parse(lMaintenanceItem.Key); PTermin lPTermin = PTerminCache.Instance.getTerminOrDefaultByID(lDataVersion.PTerminID); e.Value = lPTermin.ToString(); } else if ( this.dataGridViewStammData.Columns.IsColumn(e, "Version") ) { DataVersion lDataVersion = DataVersion.parse(lMaintenanceItem.Key); e.Value = lDataVersion.Version; } else if ( this.dataGridViewStammData.Columns.IsColumn(e, "State") ) { e.Value = MaintenanceStates.toString((MaintenanceStates.EditState) lMaintenanceItem.State); } } }
Gekracht hatte es an der Stelle, wo nun die Methode getTerminOrDefaultByID steht. Die andere Methode getTerminByID hatte eine Exception geworfen, weil jemand einen Eintrag aus der DB löschen lies, auf den hier verwiesen wurde. Wie gesagt, der Code in der Methode warf die Exception und kurz darauf stand man wieder an der gleichen Stelle mit der gleichen Zeile und Spalte. Also entweder kam es dabei dann zum Stack Overflow oder Out of Memory. Das Grid war wohl der Meinung, dass diese Zelle noch Formatiert werden muss. Evtl. fehlte da das FormattingApplied!?
Gruß
Martin
-
Hallo Martin,
dadurch dass sich die Logik in den Methoden "versteckt", kann ich daraus nichts ableiten.
Wenn GetTerminById auf die Nase fällt es aber mit der Default-Variante geht,
solltest Du die Klasse genauer untersuchen, denn wahrscheinlich ist da nicht alles sauber.FormattingApplied hat nur eine Bedeutung:
Wird in Value etwas geliefert, der nicht FormattedValueType (identisch mit DesiredType in den Ereignisargumenten)
zuweisbar ist, so wird versucht darauf die Standard-Konvertierung anzuwenden, wenn die Eigenschaft false ist,
also auf dem Wege noch das gewünschte Ergebnis zu erzielen.Sinnvollerweise setzt man den Wert auf true, wenn man den gewünschten Typ liefert (was anderes wäre eh ein Problem) und lässt sie nur false, wenn man die Spalte im Ereignis nicht behandelt.
Eine Erklärung für die StackOverflowExcption findet man da nicht -
ausgenommen Du hast einen eigenen, fehlerhaften TypeConverter.Beachte: Das CellFormatting-Ereignis wird u. U. sehr oft aufgerufen, gecacht wird da nichts
und - wie bereits erwähnt - es auch für die Berechnung von Spaltenbreiten uam. verwendet.Gruß Elmar
-
****************************************************************************************************************
Dieser Thread wurde mangels weiterer Beteiligung des Fragestellenden ohne bestätigte Lösung abgeschlossen.
Neue Rückfragen oder Ergänzungen zu diesem Thread bleiben weiterhin möglich.
****************************************************************************************************************