Fragensteller
Frage zum RowState einer DataTable

Frage
-
Hallo,
in meinem in VB2008 geschriebenen Programm werden die in einer DataTable gespeicherten über eine DataGridView angezeigt. Die User können diese Datan dann zum Laufzeit selbst manuell ändern. Da dabei wie immer Fehler passieren können, möchte ich die Möglichkeit schaffen die letzten Änderungen wieder rückgägngig zu machen. Ich könnte das über Table.RejectChanges erreichen, dabei werden aber alle Änderungen seit letzter Speicherung rückgängig gemacht. Ich möchte aber schrittweise jeweils nur die letzte Änderung wieder rückgängig machen. Ich habe ein bischen mit DataRowState rumexperimentiert, komme da aber nicht weiter! Hätte vieleicht jemand eine Tipp für mich?
Gruß Chris
Alle Antworten
-
Hallo,
ich kenne mich zwar wenig mit dem DataTable usw. aus, aber bei anderen Dingen würde ich eine Liste der Änderungen in eine List(Of T) speichern und entsprechend alles nach und nach zurück setzen. T muss, soweit ich jetzt weiß, ein eigenes Objekt einer Klasse sein, welche den Index des Items und die Änderungen auffassen kann.
Ich kann nicht garantieren das das die effektivste Variante ist, aber funktionieren würde es.
Koopakiller [kuːpakɪllɐ] | Webseite | Code Beispiele | Facebook | Snippets
-
Hallo Chris,
man kann bei der DataView einen RowStateFilter setzen, womit auch die Anzeige im DataGridView angepasst werden kann.
Über die Einstellung ModifiedCurrent würden nur die veränderten Zeilen angezeigt, weitere Kombinationen sind möglich. Entsprechendes geht DataTable.Select.
Dann kann über DataRow.RejectChanges eine einzelne Zeile zurückgesetzt werden - neue Zeilen würden entfernt, gelöschte in den Originalzustand versetzt werden.
Gruß Elmar
-
Das mit der Liste der Änderungen könnte so aussehen. Es wird dann beim Click auf den Undo Button immer nur eine Zelle zurückgesetzt. Also das würde mit der DataGridView arbeiten nicht mit der DataTable.
Das Löschen von Zeilen ist im Beispiel nicht berücksichtigt. Das Update Kommando ist nur zum Testen. Das müsste über ein eigenes mit einem Primary Key optimiert werden.
Public Class Form1
Class DGVValue
Public ReadOnly Cell As DataGridViewCell
Public ReadOnly Value As Object
Public Sub New(pCell As DataGridViewCell)
Cell = pCell
Value = Cell.Value
End Sub
End Class
Dim lstDGV_OldValues As New SortedList(Of Integer, DGVValue)
Dim blnData_Changed As Boolean
Private Sub DataGridView1_CellBeginEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
lstDGV_OldValues.Add(lstDGV_OldValues.Count + 1,
New DGVValue(DataGridView1(e.ColumnIndex, e.RowIndex)))
blnData_Changed = True
End Sub
Private Sub btnUndo_Click(sender As Object, e As System.EventArgs) Handles btnUndo.Click
Dim objDGVValue As DGVValue
If lstDGV_OldValues.Count > 0 Then
objDGVValue = lstDGV_OldValues(lstDGV_OldValues.Count)
lstDGV_OldValues.Remove(lstDGV_OldValues.Count)
DataGridView1.CurrentCell = objDGVValue.Cell
DataGridView1.CurrentCell.Value = objDGVValue.Value
' Speicherung in der Datenbank.
mobjSqlDataAdapter.Update(mobjDataSet.Tables("Test2"))
End If
End Sub
Private Sub DataGridView1_RowLeave(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.RowLeave
' Speicherung in der Datenbank.
If blnData_Changed Then
mobjSqlDataAdapter.Update(mobjDataSet.Tables("Test2"))
blnData_Changed = False
End If
End Sub
' Form.
Private mobjSqlConnection As System.Data.SqlClient.SqlConnection
Private mstrSqlConnectionString As String = _
"Initial Catalog=InfoItems_Test;Data Source=(local)\InfoItemsDB;User ID=sa;" & _
"Password=Aa1!!22222;MultipleActiveResultSets=True"
Private mobjDataSet As New DataSet
Private mobjSqlDataAdapter As SqlClient.SqlDataAdapter
Private mobjbuilder As SqlClient.SqlCommandBuilder
Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
mobjSqlConnection = New System.Data.SqlClient.SqlConnection(Me. _
mstrSqlConnectionString)
mobjSqlConnection.Open()
mobjDataSet.Clear()
If mobjSqlDataAdapter IsNot Nothing Then
mobjSqlDataAdapter.Dispose()
End If
mobjSqlDataAdapter = New SqlClient.SqlDataAdapter _
("Select * From Test2", _
mobjSqlConnection)
mobjSqlDataAdapter.Fill(mobjDataSet, "Test2")
DataGridView1.DataSource = mobjDataSet.Tables("Test2")
' if INSERT, UPDATE, or DELETE statements have not been specified,
' the Update method generates an exception.
' However, you can create a SqlCommandBuilder or OleDbCommandBuilder object
' to automatically generate SQL statements for single-table updates
' if you set the SelectCommand property of a .NET Framework data provider.
' Then, any additional SQL statements that you do not set are generated by the
' CommandBuilder.
' This generation logic requires key column information to be present in the DataSet.
' For more information see Generating Commands with CommandBuilders (ADO.NET).
mobjbuilder = New SqlClient.SqlCommandBuilder(mobjSqlDataAdapter)
End Sub
Private Sub Form2_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.DisposedIf mobjDataSet IsNot Nothing Then
' Ausstehende Änderungen speichern.
mobjSqlDataAdapter.Update(mobjDataSet.Tables("Test2"))
mobjDataSet.Dispose()
mobjDataSet = Nothing
End If
If mobjSqlDataAdapter IsNot Nothing Then
mobjSqlDataAdapter.Dispose()
mobjSqlDataAdapter = Nothing
End If
If mobjSqlConnection IsNot Nothing Then
mobjSqlConnection.Close()
mobjSqlConnection = Nothing
End If
End Sub
End Class
- Bearbeitet Markus222 Mittwoch, 20. Februar 2013 22:06
-
Hallo Markus,
der Code ist nett, aber von der Funktion eher redundant, denn DataTable bzw. DataView enthalten bereits alle notwendigen Daten / Funktionen.
Zudem mindestens speicherintensiv - u. U., nicht probiert, sogar problematisch - ist es, die DataGridViewCells zwischenzuspeichern. Denn das DataGridView versucht die Zellen zu recyceln bzw. nicht mehr zu verwenden als für die Anzeige notwendig ist.
Siehe u. a. Empfohlene Vorgehensweisen für das Skalieren des DataGridView-Steuerelements in Windows Forms
Gruß Elmar
-
Hallo Elmar,
es ging doch um die Möglichkeit des schrittweisen Undo. Das man als Anwender Änderungen die man im DataGridView gemacht hat Schritt für Schritt zurücknehmen kann. Also Zelle für Zelle.
Und mit dem Beispiel würde das ja erreicht?
- Bearbeitet Markus222 Mittwoch, 20. Februar 2013 21:53
-
Hallo Markus,
ich habe aus "letzten Änderungen" kein mehrstufiges Undo rausgelesen.
Wenn man das machen wollte, so würde ich auch da bei der DataTable ansetzen. Grober Ansatz: DataTable.GetChanges() und die Differenz der DataRows in einer Auflistung verwalten.
Steuerelemente (oder ihre Bestandteile) sind dabei immer ungünstig, da viel zu schwergewichtig und i. a. gar nicht darauf ausgelegt.
Gruß Elmar
-
Hallo Markus, Hallo Elmar,
ich habe das Problem so gelöst: bei jeder Änderung in der DataTable wird die Nummer der geänderten Zeile in eine Variable gespeichert:
Dim DataTableChanges as String
DataTableChanges = ZeilenNummer & "," & DataTableChanges
Die zuletzt geänderte Zeile steht also am Anfang des Strings.
Die Undo-Prozedur sieht dann so aus:
Dim AktZeile As String
Dim TmpStr As String
If DataTableChanges <> "" Then
AktZeile = Strings.Left(DataTableChanges, InStr(DataTableChanges, ",") - 1)
DataTable.Rows(AktZeile).RejectChanges()
TmpStr = Replace(M_dgvUrlPlnChanges, AktZeile & ",", "")
DataTableChanges = TmpStr
End If
So werden die Änderungen in der entsprechenden Reihefolge wieder rückgängig gemacht. Wenn man wollte könnte man so auch Änderungen in einzelnen Feldern der DataTable verwalten!
-
Hallo Christoph,
welche Zeilennummer?
Wenn die vom DataGridView / DataSource stammt, so könnte sie sich durch Umsortieren ändern.Anstatt "rumzustringen", wäre eine List(Of DataRow) oder ein Stack(Of DataRow) einfacher und eindeutiger.
Solange die DataTable existiert, ist die Referenz eine DataRow unveränderlich. Beim kompletten Neuladen müsste nur die Historie mit gelöscht werden.
Gruß Elmar