none
Duplikate verhindern. RRS feed

  • Frage

  • Hallo Zusammen,
    ich habe eine Anwendung in VB 2005 mit einem numerischen Datenfeld, das keine doppelten Nummern enthalten darf. Das Feld im Dataset ist nicht als Unique eingestellt. Kann mir jemand sagen, wie ich bereits bei der Eingabe verhindern kann, dass Duplikate angelegt werden? Irgendwie haut das bei mir nicht hin. Ich habe schon eine Validation im Dataset.vb versucht und auch in der Form Klasse. Beides funktioniert nicht wirklich sauber.

    Vielen Dank vorab
    Christina
    Mittwoch, 2. Dezember 2009 15:24

Antworten

  • Hi Christina,
    nachfolgend mal ein Beispiel, wie man so etwas im Parse-Ereignis des Binding-Objektes lösen kann:

    Public Class Form1
    
      Private dt As DataTable = Testdata.Getdata ' Testdaten laden
      Private bs As New BindingSource(dt, "")
      Private sc As New SplitContainer With {.Dock = DockStyle.Fill, _
                                             .SplitterDistance = 100}
      Private dgv As New DataGridView With {.Dock = DockStyle.Fill, _
                                            .AllowUserToAddRows = False, _
                                            .ReadOnly = True, .DataSource = bs}
      Private lbl1 As New Label With {.Top = 10, .Text = "Nummer"}
      Private tb1 As New TextBox With {.top = 20}
      Private ep As New ErrorProvider
      Private lbl2 As New Label With {.Top = 55, .Text = "col2"}
      Private tb2 As New TextBox With {.Top = 75}
      Private WithEvents btn As New Button With {.Top = 100, .Text = "OK"}
    
      Private Sub Form1_Load(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Width = 600 ' Formulargröße erhöhen, um alles anzuzeigen
        Me.Controls.AddRange(New Control() {sc}) ' SplitContainer im Formular platzieren
        sc.Panel1.Controls.Add(dgv) ' im linken teil des Splitcontainers das Grid füllend platzieren
        sc.Panel2.Controls.AddRange(New Control() {btn, tb2, lbl2, tb1, lbl1}) ' Steuerelement im rechten Teil
        AddHandler tb1.DataBindings.Add("Text", bs, "Unikat").Parse, AddressOf tb1_Parse ' Parse-Handler binden
        tb2.DataBindings.Add("Text", bs, "col2") ' 2. TextBox für Text
      End Sub
    
      Private Sub btn_Click(ByVal sender As Object, _
                            ByVal e As System.EventArgs) Handles btn.Click
        bs.EndEdit() ' Editiervorgang beenden und Binding-Objekte zum Datenaustausch auffordern
      End Sub
    
      Private Sub tb1_Parse(ByVal sender As Object, ByVal e As ConvertEventArgs)
        ep.SetError(tb1, "") ' ErrorProvider rücksetzen
        Dim nr = CType(e.Value, Integer) ' neu eingegebenen Wert holen
        Dim res1 = Aggregate el In dt.Rows _
                   Let rnr = CType(CType(el, DataRow).Item("Unikat"), Integer) _
                   Where rnr = nr And el IsNot CType(bs.Current, DataRowView).Row _
                   Into Count() ' Auftreten in vorhandenen Datenobjekten zählen
        If res1 > 0 Then
          Dim res2 = Aggregate el In dt.Rows _
                     Let rnr = CType(CType(el, DataRow).Item("Unikat"), Integer) _
                     Into Max(rnr) ' Maximalwert aus vorhandenen datenobjekten bestimmen
          e.Value = res2 + 1 ' neuen wert festlegen
          ep.SetError(tb1, String.Format("Falsche Eingabe: {0}, neuer Wert {1} gesetzt", _
                                         nr, res2 + 1)) ' ErrorProvider aktivieren
        End If
      End Sub
    
    End Class
    
    Friend Class Testdata
    
      Public Shared Function Getdata() As DataTable
        Dim dt As New DataTable("Tab1")
        With dt
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("Unikat", GetType(Integer)).Unique = True
            .Add("col2", GetType(String))
          End With
          For i = 1 To 100
            Dim r As DataRow = .NewRow
            r.Item("Unikat") = i * 2
            r.Item("col2") = "Zeile " & i.ToString
            .Rows.Add(r)
          Next
        End With
        Return dt
      End Function
    
    End Class
    --
    Peter
    Donnerstag, 17. Dezember 2009 08:28

Alle Antworten

  • Hi Christina,
    warum kannst du die Spalte in der DataTable im DataSet nicht unikat machen?

    Wenn du bereits bei der Eingabe (beim Tastenanschlag) verhindern willst, dann musst du schon beim KeyPress eingreifen, Ich würde das aber nicht machen, sondern erst im Validate prüfen, um z.B. auch Einfügeoperationen zu berücksichtugen. Im ungebundenen Modus kann dann ein ErrorProvider gesetzt werden, damit der Anwender weiß, dass er falsche Daten eingegeben hat. Und erst, wenn die Prüfung erfolgreich war, dann werden die Daten in die Datenquelle eingetragen. Wenn gebunden gearbeitet werden soll, dann kann man im Parse-Ereignis des Binding-Objektes den Transfer der Daten in die Datenwuelle verhindern, wenn die Daten nicht passen.

    Bevor aber mit einer Lösung begonnen wird, sollte erst einmal genau die Bedientechnologie beschrieben werden: was soll passieren und wie soll der Bediener im Problemfall informiert und weiter geführt werden?

    --
    Peter

    Mittwoch, 2. Dezember 2009 20:01
  • Hallo Peter,
    danke fuer die Infos. Ich bin noch ziemlich neu in VB.NET und habe die Anwendung wie in den "How Do I" Videos von Microsoft Beth Massy erstellt. Ich habe bei einer frueheren Google-Suche ein Beispiel gefunden, das mit .FIND und .EXISTS arbeitet. Ich dachte, das merke ich mir, und jetzt hab ich es vergessen und finde den Link nicht mehr :-(
    Wenn ich das Feld unique setze, bekomme ich bereits beim Ausfuehren des Codes mit F5 eine Fehlermeldung vom Debugger, wenn versucht wird, eine bereits existierende Nummer zu speichern. "ConstraintException wurde nicht behandelt"

    Ich moechte, dass beim Verlassen des Eingabefeldes geprueft wird, ob die Nummer schon existiert. Trifft dies zu, soll eine Fehlermeldung ausgegeben werden, z. B. "Die Nummer 999 existiert bereits"
    Dann soll bei einer Neuanlage die naechsthoehere freie Nummer als Vorschlag im Eingabefeld erscheinen.
    Wurde ein bestehender Datensatz geaendert, so soll die Nummer wieder angezeigt werden, bevor sie geaendert wurde.

    Viele Gruesse
    Christina

    Donnerstag, 10. Dezember 2009 15:42
  • Hi Christina,
    nachfolgend mal ein Beispiel, wie man so etwas im Parse-Ereignis des Binding-Objektes lösen kann:

    Public Class Form1
    
      Private dt As DataTable = Testdata.Getdata ' Testdaten laden
      Private bs As New BindingSource(dt, "")
      Private sc As New SplitContainer With {.Dock = DockStyle.Fill, _
                                             .SplitterDistance = 100}
      Private dgv As New DataGridView With {.Dock = DockStyle.Fill, _
                                            .AllowUserToAddRows = False, _
                                            .ReadOnly = True, .DataSource = bs}
      Private lbl1 As New Label With {.Top = 10, .Text = "Nummer"}
      Private tb1 As New TextBox With {.top = 20}
      Private ep As New ErrorProvider
      Private lbl2 As New Label With {.Top = 55, .Text = "col2"}
      Private tb2 As New TextBox With {.Top = 75}
      Private WithEvents btn As New Button With {.Top = 100, .Text = "OK"}
    
      Private Sub Form1_Load(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) Handles MyBase.Load
        Me.Width = 600 ' Formulargröße erhöhen, um alles anzuzeigen
        Me.Controls.AddRange(New Control() {sc}) ' SplitContainer im Formular platzieren
        sc.Panel1.Controls.Add(dgv) ' im linken teil des Splitcontainers das Grid füllend platzieren
        sc.Panel2.Controls.AddRange(New Control() {btn, tb2, lbl2, tb1, lbl1}) ' Steuerelement im rechten Teil
        AddHandler tb1.DataBindings.Add("Text", bs, "Unikat").Parse, AddressOf tb1_Parse ' Parse-Handler binden
        tb2.DataBindings.Add("Text", bs, "col2") ' 2. TextBox für Text
      End Sub
    
      Private Sub btn_Click(ByVal sender As Object, _
                            ByVal e As System.EventArgs) Handles btn.Click
        bs.EndEdit() ' Editiervorgang beenden und Binding-Objekte zum Datenaustausch auffordern
      End Sub
    
      Private Sub tb1_Parse(ByVal sender As Object, ByVal e As ConvertEventArgs)
        ep.SetError(tb1, "") ' ErrorProvider rücksetzen
        Dim nr = CType(e.Value, Integer) ' neu eingegebenen Wert holen
        Dim res1 = Aggregate el In dt.Rows _
                   Let rnr = CType(CType(el, DataRow).Item("Unikat"), Integer) _
                   Where rnr = nr And el IsNot CType(bs.Current, DataRowView).Row _
                   Into Count() ' Auftreten in vorhandenen Datenobjekten zählen
        If res1 > 0 Then
          Dim res2 = Aggregate el In dt.Rows _
                     Let rnr = CType(CType(el, DataRow).Item("Unikat"), Integer) _
                     Into Max(rnr) ' Maximalwert aus vorhandenen datenobjekten bestimmen
          e.Value = res2 + 1 ' neuen wert festlegen
          ep.SetError(tb1, String.Format("Falsche Eingabe: {0}, neuer Wert {1} gesetzt", _
                                         nr, res2 + 1)) ' ErrorProvider aktivieren
        End If
      End Sub
    
    End Class
    
    Friend Class Testdata
    
      Public Shared Function Getdata() As DataTable
        Dim dt As New DataTable("Tab1")
        With dt
          With .Columns
            With .Add("ID", GetType(Integer))
              .AutoIncrement = True
              .AutoIncrementSeed = 1
              .AutoIncrementStep = 1
            End With
            .Add("Unikat", GetType(Integer)).Unique = True
            .Add("col2", GetType(String))
          End With
          For i = 1 To 100
            Dim r As DataRow = .NewRow
            r.Item("Unikat") = i * 2
            r.Item("col2") = "Zeile " & i.ToString
            .Rows.Add(r)
          Next
        End With
        Return dt
      End Function
    
    End Class
    --
    Peter
    Donnerstag, 17. Dezember 2009 08:28
  • Hallo Peter,
    sorry, dass ich so lange fuer meine Antwort gebraucht habe.
    Ich werde deinen Vorschlag testen und melde mich dann.
    Vielen Dank und beste Gruesse
    Christina
    Donnerstag, 14. Januar 2010 16:33