none
Frage zum RowState einer DataTable RRS feed

  • 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

    Samstag, 16. Februar 2013 19:49

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

    Samstag, 16. Februar 2013 20:06
  • 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

    Sonntag, 17. Februar 2013 10:44
  • 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.Disposed

        If 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
    Mittwoch, 20. Februar 2013 19:01
  • 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

    Mittwoch, 20. Februar 2013 19:58
  • 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
    Mittwoch, 20. Februar 2013 20:07
  • 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

    Mittwoch, 20. Februar 2013 21:24
  • 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!

    Donnerstag, 21. Februar 2013 06:54
  • 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


    Donnerstag, 21. Februar 2013 08:28