DataSet / DataRow - HasError inkl. ErrorContent für WPF

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    DataSet / DataRow - HasError inkl. ErrorContent für WPF

    Liebe Community,

    vielen Dank für die unbewusste Hilfe der letzten Jahre, leider wurde mit der letzten Bereinigung mein Account gelöscht weshalb ich nun einen neuen habe.

    Die letzten Wochen versuche ich mich in WPF inkl. den Themen EF (6.2 ModelFirst) und MVVM.

    Aktuell ist aber mein Problem folgendes:
    Ich habe ein DataSet mit der Tabelle Kunden, dazu nur drei Spalten mit den Werten ID(Autoinkrement), Matchcode(String) und Nummer(String).
    In EF CodeFirst habe ich die iDataErrorInfo Schnittstelle implementiert und konnte somit wunderschön die notwendigen Eingaben hervorheben inkl. ErrorContent (im Standard wird die Textbox Rot...).
    Wenn ich im DataSet via Designer in den jeweiligen Spalten eine maximal Länge von z.B. 10 einstelle erhalte ich ebenfalls im WPF-Window meine Rot umrandete Textbox wenn er eingegebene Wert über 10 ist, allerdings kann ich keine minimal Länge einstellen oder irgendwelche Formatierungen prüfen.

    Über eine Partial Class habe ich schon probiert die Eigenschaft zu überschreiben, ohne Erfolg.

    Daher nun meine eigentlichen Fragen:
    1. Ist es möglich die HasError Eigenschaft zu beeinflussen?
    2. Gibt es einen anderen Weg das gewünschte Ergebnis zu erhalten? (ErrorContent, Validate..)


    Vorab vielen Dank und beste Grüße
    Thomas
    Wpf harmoniert nicht gut mit Dataset - es gibt keine Vorschau verfügbarer Bindings.
    Verwende lieber den EF-ObjectContext.

    Evtl. musst du das Binding-Vorschau-Feature auch erst noch kennenlernen - im Netz und in Büchern gibts afaik so gut wie nichts dazu.
    gugge Grundlagen - MVVM: "Binding-Picking" im Xaml-Editor
    Hallo @ThomasM und willkommen (wieder)

    Du schreibst du möchtest MVVM proggen.
    Ich habe mir mal für eine Applikation im MVVM Pattern und ebenfalls mit EF einige Hilfsfunktionen geschrieben welche sowohl das Model als auch das ViewModel betreffen.
    Ziel ist/war es relativ einfach vom VM aus das Model UND das ViewModel zu überprüfen und dabei beim Model die EF Annotation zu verwenden.

    Model:

    VB.NET-Quellcode

    1. Imports System.ComponentModel.DataAnnotations
    2. Public Class ModelValidation(Of T As ModelBase)
    3. Public Function Validate(entity As T) As IEnumerable(Of ValidationResult)
    4. Dim validationResults As List(Of ValidationResult) = New List(Of ValidationResult)()
    5. Dim validationContext As ValidationContext = New ValidationContext(entity, Nothing, Nothing)
    6. Validator.TryValidateObject(entity, validationContext, validationResults, True)
    7. For Each res As ValidationResult In validationResults
    8. Debug.WriteLine(String.Format("Error on validate entity. Message: {0} - Members: {1}, ", res.ErrorMessage, String.Join(", ", res.MemberNames)))
    9. Next
    10. Return validationResults
    11. End Function
    12. End Class


    VB.NET-Quellcode

    1. Imports System.ComponentModel.DataAnnotations
    2. Public Class ModelValidator
    3. Public Shared Function ValidateEntity(Of T As ModelBase)(entity As T) As IEnumerable(Of ValidationResult)
    4. Return New ModelValidation(Of T)().Validate(entity)
    5. End Function
    6. End Class


    Und in der Model-Basisklasse dann:

    VB.NET-Quellcode

    1. Public Overridable Function Validate() As IEnumerable(Of ValidationResult)
    2. Return ModelValidator.ValidateEntity(Me)
    3. End Function
    4. Public Function IsValid() As Boolean
    5. Return Validate() Is Nothing OrElse Validate.Count = 0
    6. End Function


    Für das ViewModel habe ich eine Schnittstelle erstellt:

    VB.NET-Quellcode

    1. Imports System.ComponentModel.DataAnnotations
    2. Public Interface IViewModelInfrastructure
    3. ReadOnly Property IsValid As Boolean
    4. Function ValidationErrors() As List(Of ValidationResult)
    5. End Interface



    In einem ViewModel sieht das ganze dann wie folgt aus:

    VB.NET-Quellcode

    1. #Region "IDataErrorInfo Implementation"
    2. Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
    3. Get
    4. Dim valRes As ValidationResult = ValidationErrors.Where(Function(v) v.MemberNames.Contains(columnName) = True).FirstOrDefault
    5. If valRes Is Nothing Then
    6. Return Nothing
    7. End If
    8. CommandManager.InvalidateRequerySuggested()
    9. Return valRes.ErrorMessage
    10. End Get
    11. End Property
    12. Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
    13. Get
    14. If IsValid Then
    15. Return Nothing
    16. Else
    17. Return "Das ViewModel ist Fehlerhaft"
    18. End If
    19. End Get
    20. End Property
    21. #End Region
    22. #Region "IViewModelInfrastructure Implementation"
    23. Public Function ValidationErrors() As List(Of ValidationResult) Implements IViewModelInfrastructure.ValidationErrors
    24. Dim valRet As New List(Of ValidationResult)
    25. 'Prüfung eines Propertys des ViewModels welches nicht durch eine Annotation des Models geprüft wird
    26. If PropertieFromViewModel Is Nothing Then
    27. valRet.Add("Du must das Feld ausfüllen", New List(Of String) From {NameOf(PropertieFromViewModel)}))
    28. End If
    29. 'gibt es keine Fehler im VM dann Model prüfen mithilfe der IsValid Funktion der Model-Basisklasse
    30. If valRet.Count = 0 AndAlso Not meineModelVariable.IsValid Then
    31. valRet.Add("Fehler im Model", New List(Of String)))
    32. End If
    33. Return valRet
    34. End Function
    35. Public ReadOnly Property IsValid As Boolean Implements IViewModelInfrastructure.IsValid
    36. Get
    37. Return ValidationErrors.Count = 0
    38. End Get
    39. End Property
    40. #End Region


    Beim Binding dann einfach:

    XML-Quellcode

    1. <TextBox Text="{Binding meinProperty, ValidatesOnDataErrors=True}" />


    Der Code ist bei mir in leicht abgewandelter Form im Einsatz und es Arbeitet sich ganz gut damit und er ist auch Testbar (UnitTests).
    Bei Fragen kannst du dich gerne an mich wenden.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo Zusammen,

    vielen Dank für eure Unterstützung.
    Eigentlich war meine Hoffnung alte WinForms Projekte die aktuell mit DataSets laufen mit überschaubaren Aufwand nach WPF zu portieren, daher auch meine Frage bzgl. der HasError Eigenschaft von der DataRow.

    Bleibt mir für vernünftiges WPF scheinbar keine andere Option als das Rand "fast" neu zu erfinden, oder?


    Grüße Thomas
    @Sascha: Nanu - du hast da ein eigenes Interface zum Validieren?
    Ich mach nie was mit Validieren, aber wenn ich recht erinnere gibts dafür mordsmässige Unterstützung in Wpf - Stichwort "ValidationRules".
    Ah - jetzt gesehen, du verwendest die (eher oldfashioned) Alternative IDataErrorInfo.
    Nur den Sinn deines Interfaces versteh ich nicht: IsValid kann man sich eiglich sparen, denn wenn IDataErrorInfo.Errors Nothing zurückgibt, ists fehlerfrei.
    Ah - und du werkelst was mit ValidationResult - das ist sowas, was mit den genannten ValidationRules zu tun hat.
    Aber mir kommt das undurchsichtig vor - soweit ich weiss, baut man entweder auf ValidationRules oder auf IDataErrorInfo.
    Beides vermischen und noch ein weiteres Interface zufügen lässt mich argwöhnen, dasses einfacher ginge.

    Auf jeden Fall ist ein Stand der Technik gegeben, wie gesagt: sogar zwei: ValidationRules oder IDataErrorInfo.

    @ThomasM:
    Portieren ist immer schlecht. Wpf tickt sehr sehr anders als WinForms. Nützlich ist, wenn man in WinForms schon was mit Databinding gemacht hat - fürs grundsätzliche Verstehen einiger Prinzipien.
    Aber dann heißts, die ganz andere Umsetzung davon lernen, und staunen, wie weit das Konzept Databinding in Wpf getrieben wurde.
    Das Rad neu erfinden würde ich das auch nicht nennen, das haben andere mit Wpf ja nun bereits getan (und das Ergebnis kann sich sehen lassen).
    Dieses neue Rad nu fahren lernen - das ist die Herausforderung.
    @ErfinderDesRades
    Heißt unterm Strich ich soll das Arbeiten mit DataSets im WPF unterlassen?
    Wenn dass das Fazit zu meiner eigentlichen Frage ist werde ich wohl oder übel die alten Projekte auf WinForms lassen und meine neuen Projekte mit EF (Code-First) schreiben.
    In EF habe ich die oben geschriebene Variante von IDataErrorInfo verwendet, aber du schreibst das es sich hierbei um die alte Variante handelt und ValidationRules die neue Möglichkeit ist, sehe ich soweit richtig?

    EDIT:
    Vielleicht dazu gesagt, in WPF habe ich bereits ein Demo-Projekt welches mit DataSets arbeitet. Die Binding an z.B. eine ListBox habe ich über eine CollectionViewSource gemacht und konnte bisher ohne Probleme auf all meine typisierten Daten zugreifen.

    Gruß Thomas

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ThomasM“ ()

    Hallo @ErfinderDesRades

    Deine Einwände sind berechtigt. Ich hole etwas aus...

    Du hast ja recht, es gibt die ValidationRules. Man erstellt eine Klasse welche von ValidationRule erbt und gibt dann Nothing oder ein ValidationResult zurück.
    z.b. Return New ValidationResult(False, "Dies ist ein Pflichtfeld")

    Nachteile hier:
    Extrem lange schreibweise im XAML.
    Statt:

    XML-Quellcode

    1. <TextBox x:Name="TextBox1" Text="{Binding BoundProperty1", ValidatesOnDataErrors=True}>

    muss man nun folgendes schreiben:

    XML-Quellcode

    1. <TextBox x:Name="TextBox1">
    2. <TextBox.Text>
    3. <Binding Path="BoundProperty1" UpdateSourceTrigger="PropertyChanged">
    4. <Binding.ValidationRules>
    5. <local:NullOrEmptyValidationRule />
    6. </Binding.ValidationRules>
    7. </Binding>
    8. </TextBox.Text>
    9. </TextBox>

    • Man kann keine gezielte Logik implementieren ohne wieder eine eigene ValidationRule zu erstellen. (z.b. Mandantbezogene Richtlinien)
    • In Verbindung mit EF können die Annotationen des Models nicht berüksichtigt werden. Oder doch, bin mir jetzt nicht sicher, glaube nicht.
    • Ich kann nicht entscheiden WANN validiert wird. Geht ein Fenster mit 10 Textboxen auf welche ausgefüllt werden müssen läuchten alle mal Rot auf. Das sieht vieleicht aus. Daher Validiere ich gerne erst beim klick auf speichern.
    Deshalb der Weg über IDataErrorInfo. Auch in der WPF gibt es Controls wie z.b. das DataGrid welche auf IDataErrorInfo.Error zugreifen um die Rows zu prüfen.

    Warum habe ich ein weiters Interface (IViewModelInfrastructure). Nicht bei jedem ViewModel benötige ich die Validierung, so kann ich eben entscheiden wann ich sie benötige.

    Warum die IsValid Methode? Sparen kann man sie sich, korrekt. So habe ich aber ein Boolean Property welches ich vom View aus Binden kann wenn benötigt, ohne wieder Converter verwenden zu müssen und auch im Code finde ich es irgendwie schöner mit If IsValid Then...... als If ValidationErrors.Count = 0 Then ......

    Und Last but not least.
    Es ist Testbar!!!! Die ValidationRules im XAML kann ich nicht über UnitTests abdecken. Und das ist für mich ein wesentliches Kriterium, warum soll ich MVVM machen und mir eines der besten Vorteile selbst wegnehmen?

    Aber du kennst mich ja, wenn man es verkürzen kann oder es einfacher geht ohne den genannten Luxus zu verlieren dann bitte her damit.
    Du hast immer ganz nette Ideen, nicht zuletzt weil du gerne mal auch etwas um die Ecke denkst, vieleicht hast du ja was tolles im Hinterstübchen ^^ . Würde mich freuen, ich lerne immer gerne dazu.

    Bei meinen Projekten ist es meisst auch so das ich nur den Code schreibe und ein Kollege das UI und das Design macht. Unabhängig von mir. Der kann auch nicht Programmieren. Er bekommt von mir ein Klassendiagramm und schreibt das XAML. Mit meiner Variante muss er mich nicht immer darum bitte jetzt für ein bestimmtes Scenario eine ValidationRule zu schreiben woduch er seine Arbeit unterbrechen und warten muss. Ich mach das ViewModel fertig und er kann darauf los Designen, um solche Dinge muss er sich einfach nicht mehr kümmern. 8o Ich finde ja sowieso das sollte in der Hand des Programmierers bleiben.

    Schöne Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

    wie gesagt, wirklich beschäftigt habich mich nicht damit.
    Die ValidationRules schienen mir auch ziemlich aufgeblasener Kram - aber nur nach dem, was ich bei CodeProject mal runtergeladen hab, und du weisst ja vlt.: manche c#-ler suhlen sich in Interfaces und Pattern, dass ich immer finde, Anderson hatte ja keine Ahnung, wie wahr sein Märchen ist: "Des Kaisers neue Kleider". ;)

    Tendenziell ziehe ich immer den Basis-Klassen-Vorbehalt zu Rate, wenn ich auf selbstebastelte Interfaces stosse.
    Also hier würde ich gucken, ob man nicht eine Viewmodel-Basis-Klasse schaffen kann, die schoma IDataErrorInfo auf überschreibbare Weise implementiert, und die auch gerne schoma die IsValid-Property einbringt.
    Die kann ja Errors Is Nothing OrElse Errors.Count = 0 returnen - kost ja nix, ist einmal gecodet und muss man nicht immer wieder re-implementieren.

    Ich find Interfaces immer lausig zu debuggen.
    Etwa auf Arbeit stosse ich auf die Methode CreateMailItem(), drücke "Gehe zur Definition", und komme bei son blödes Interface raus, und kann immer noch nicht sehen, was da nu eiglich gespielt wird.
    Also Entkopplung ist ja ganz toll, aber Code, der numal gekoppelt ist, der wird durch gewalttätige Entkopplung eben schlechter statt besser.
    Aber ich gerate ins räsonnieren...
    Hallo

    Ja das mit "Gehe zu Definition" ist doof, ärgert mich auch immer wieder *g*
    Mit der Basisklasse gebe ich dir recht. Könnte man auch machen. Dann benötigt man hald mindestens zwei verschiedene denn das mit Errors Is Nothing OrElse Errors.Count = 0 geht für mich gar nicht.
    Ist es ein ViewModel welches keine Valididerung benötigt soll es diese Methoden gar nicht erst haben. Der Designer denkt dann das Validiert wird und implementiert das ins View und wundert sich vieleicht.

    Bei Interfaces finde ich schön das ich mich selbst kontrollieren kann. Sobald ich es implementiere MUSS ich die Methoden mit Code befüllen. Ich kann also nicht wie beim erben von der Basisklasse "vergessen" die Methode zu überschreiben.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo @petaod

    Da hast du natürlich wieder Recht :)
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    denn das mit Errors Is Nothing OrElse Errors.Count = 0 geht für mich gar nicht.
    Ist es ein ViewModel welches keine Valididerung benötigt soll es diese Methoden gar nicht erst haben.
    Wieso nicht? Frisst doch kein Brot.
    Der genannte Ausdruck gibt im Default-Fall ja immer True zurück, also Klassen ohne explizite Validierung sind immer valide.