Dynamisches Erstellen einer Prüflogik

    • VB.NET

    Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von BiedermannS.

      Dynamisches Erstellen einer Prüflogik

      Wenn man eine Form mit vielen Eingabefeldern hat, welche die selbe Form von Daten annehmen, dann kann das Aufbauen einer Ordentlichen Prüfungslogik ganz schon nervenaufreibend sein. Da ich in einem Thread mit eben diesem Problem konfrontiert war, hier mal ein "kleines" Snippet dazu.

      Spoiler anzeigen

      VB.NET-Quellcode

      1. Option Strict On
      2. Imports System.Windows.Forms
      3. ''' <summary>
      4. ''' Used for automated registering of Events and controls, to validate the input.
      5. ''' </summary>
      6. ''' <typeparam name="T">Restricts the type of controls, which can be registered</typeparam>
      7. ''' <remarks></remarks>
      8. Public Class Checker(Of T As Control)
      9. Private _CheckControls As New Dictionary(Of T, Boolean)
      10. Private _CheckFailedReason As New Dictionary(Of T, String)
      11. Protected _CheckTagFunction As Func(Of Control, String, Boolean) = Function(c As Control, TagString As String) As Boolean
      12. Return c.Tag IsNot Nothing AndAlso c.Tag.ToString = TagString
      13. End Function
      14. Private Class RegisteredEvent
      15. Public Property TagString As String
      16. Public Property EventString As String
      17. Public Property EventHandler As [Delegate]
      18. End Class
      19. Private _RegisteredEvents As New Dictionary(Of T, RegisteredEvent)
      20. ''' <summary>
      21. ''' Creates a new instance
      22. ''' </summary>
      23. ''' <remarks></remarks>
      24. Sub New()
      25. MyBase.New()
      26. End Sub
      27. ''' <summary>
      28. ''' Creates a new instance
      29. ''' </summary>
      30. ''' <param name="CheckTagFunction">Defines how the .Tag Property is validated for registration</param>
      31. ''' <remarks></remarks>
      32. Sub New(ByRef CheckTagFunction As Func(Of Control, String, Boolean))
      33. _CheckTagFunction = CheckTagFunction
      34. End Sub
      35. ''' <summary>
      36. ''' Gets all registered controls in a List(Of T)
      37. ''' </summary>
      38. ''' <returns>List(Of T)</returns>
      39. ''' <remarks></remarks>
      40. Public Function GetRegisteredControls() As List(Of T)
      41. Return _CheckControls.Keys.ToList
      42. End Function
      43. ''' <summary>
      44. ''' Unregisters eventhandlers and controls
      45. ''' </summary>
      46. ''' <param name="parent">The parent, which will be searched for controls</param>
      47. ''' <param name="TagString">When set, only unregister Controls with the given Tag</param>
      48. ''' <remarks></remarks>
      49. Public Sub UnRegisterEvents(ByVal parent As Control, Optional ByVal TagString As String = "")
      50. Dim x As Control
      51. Try
      52. If String.IsNullOrWhiteSpace(TagString) Then
      53. For Each c As Control In _CheckControls.Keys
      54. x = c
      55. Dim RegEvent As RegisteredEvent = _RegisteredEvents(DirectCast(c, T))
      56. c.GetType.GetEvent(RegEvent.EventString, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase).RemoveEventHandler(c, RegEvent.EventHandler)
      57. Next
      58. _CheckControls.Clear()
      59. _CheckFailedReason.Clear()
      60. _RegisteredEvents.Clear()
      61. Else
      62. Dim RegControls As List(Of T) = (From c As T In _CheckControls.Keys Where _CheckTagFunction(c, TagString) Select c).ToList
      63. For Each c As Control In RegControls
      64. Dim RegEvent As RegisteredEvent = _RegisteredEvents(DirectCast(c, T))
      65. c.GetType.GetEvent(RegEvent.EventString, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase).RemoveEventHandler(c, RegEvent.EventHandler)
      66. _CheckControls.Remove(DirectCast(c, T))
      67. _CheckFailedReason.Remove(DirectCast(c, T))
      68. _RegisteredEvents.Remove(DirectCast(c, T))
      69. Next
      70. End If
      71. Catch ex As Exception
      72. Throw New RegistrationException(String.Format("Failed to UnRegister Control: {0}{1}{0}", Chr(34), x.Name), ex, x)
      73. End Try
      74. End Sub
      75. ''' <summary>
      76. ''' Registers all Controls with the given tag and the type of the baseclass
      77. ''' </summary>
      78. ''' <param name="Event">The name of the event, for which the handler will be registered</param>
      79. ''' <param name="TagString">The tag string, for chosing which control to register</param>
      80. ''' <param name="parent">The parent, which will be searched for controls</param>
      81. ''' <param name="EventHandler">The Eventhandler which will be registered with the event</param>
      82. ''' <param name="DefaultValue">The default value of the validation after registering</param>
      83. ''' <param name="DefaultReason">The default fail reason, when no validation has run</param>
      84. ''' <remarks></remarks>
      85. Public Sub RegisterEvents(ByVal [Event] As String, ByVal TagString As String, ByVal parent As Control, ByRef EventHandler As [Delegate], Optional ByVal DefaultValue As Boolean = False, Optional ByVal DefaultReason As String = "")
      86. RegisterEvents(Of T)([Event], TagString, parent, EventHandler, DefaultValue, DefaultReason)
      87. End Sub
      88. ''' <summary>
      89. ''' Registers all Controls with the given tag
      90. ''' </summary>
      91. ''' <typeparam name="T2">The type of the controls, which will be registered</typeparam>
      92. ''' <param name="Event">The name of the event, for which the handler will be registered</param>
      93. ''' <param name="TagString">The tag string, for chosing which control to register</param>
      94. ''' <param name="parent">The parent, which will be searched for controls</param>
      95. ''' <param name="EventHandler">The Eventhandler which will be registered with the event</param>
      96. ''' <param name="DefaultValue">The default value of the validation after registering</param>
      97. ''' <param name="DefaultReason">The default fail reason, when no validation has run</param>
      98. ''' <remarks></remarks>
      99. Public Sub RegisterEvents(Of T2 As Control)(ByVal [Event] As String, ByVal TagString As String, ByVal parent As Control, ByRef EventHandler As [Delegate], Optional ByVal DefaultValue As Boolean = False, Optional ByVal DefaultReason As String = "")
      100. Dim tmpControls As New Dictionary(Of T, Boolean)
      101. 'Alle Steuerelemente durchlaufen
      102. For Each c As Control In parent.Controls
      103. 'Für alle untergeordneten Steuerelemente (Damit nichts vergessen wird)
      104. If c.HasChildren Then RegisterEvents(Of T)([Event], TagString, c, EventHandler)
      105. Try
      106. 'Wenn der Tag gesetzt wurde und dem Text in TagString entspricht
      107. If _CheckTagFunction(c, TagString) AndAlso Not _CheckControls.ContainsKey(DirectCast(c, T)) Then
      108. If c.GetType() IsNot GetType(T2) Then
      109. Dim o As Object = DirectCast(c, T2)
      110. End If
      111. 'TextBox in einem Dictionary speichern, mit dem Wert false
      112. tmpControls.Add(DirectCast(c, T), DefaultValue)
      113. _CheckFailedReason.Add(DirectCast(c, T), DefaultReason)
      114. _RegisteredEvents.Add(DirectCast(c, T), New RegisteredEvent With {.TagString = TagString, .EventHandler = .EventHandler, .EventString = [Event]})
      115. 'entsprechenden Handler hinzufügen
      116. c.GetType.GetEvent([Event], Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase).AddEventHandler(c, EventHandler)
      117. End If
      118. Catch ex As Exception
      119. Throw New RegistrationException(String.Format("Failed to Register Control: {0}{1}{0}", Chr(34), c.Name), ex, c)
      120. End Try
      121. Next
      122. For Each kv As KeyValuePair(Of T, Boolean) In tmpControls.Reverse
      123. _CheckControls.Add(kv.Key, kv.Value)
      124. Next
      125. End Sub
      126. Private _ex As New List(Of RegistrationException)
      127. ''' <summary>
      128. ''' Tries to register all Controls with the given tag and type of the baseclass.
      129. ''' This Method doesn't throw an Exception, instead it will store the Exceptions in a list and returns a value of success
      130. ''' </summary>
      131. ''' <param name="Event">The name of the event, for which the handler will be registered</param>
      132. ''' <param name="TagString">The tag string, for chosing which control to register</param>
      133. ''' <param name="parent">The parent, which will be searched for controls</param>
      134. ''' <param name="EventHandler">The Eventhandler which will be registered with the event</param>
      135. ''' <param name="Exceptions">When a Exception is thrown, it will be stored in this list</param>
      136. ''' <param name="DefaultValue">The default value of the validation after registering</param>
      137. ''' <param name="DefaultReason">The default fail reason, when no validation has run</param>
      138. ''' <returns>If the registration of all controls was successfull</returns>
      139. ''' <remarks></remarks>
      140. Public Function TryRegisterEvents(ByVal [Event] As String, ByVal TagString As String, ByVal parent As Control, ByRef EventHandler As [Delegate], ByRef Exceptions As List(Of RegistrationException), Optional ByVal DefaultValue As Boolean = False, Optional ByVal DefaultReason As String = "") As Boolean
      141. Return TryRegisterEvents(Of T)([Event], TagString, parent, EventHandler, Exceptions, DefaultValue, DefaultReason)
      142. End Function
      143. Private Sub TryRegisterEvents(Of T2 As Control)(ByVal [Event] As String, ByVal TagString As String, ByVal parent As Control, ByRef EventHandler As [Delegate], Optional ByVal DefaultValue As Boolean = False, Optional ByVal DefaultReason As String = "")
      144. Dim tmpControls As New Dictionary(Of T, Boolean)
      145. 'Alle Steuerelemente durchlaufen
      146. For Each c As Control In parent.Controls
      147. 'Für alle untergeordneten Steuerelemente (Damit nichts vergessen wird)
      148. If c.HasChildren Then TryRegisterEvents(Of T2)([Event], TagString, c, EventHandler)
      149. Try
      150. 'Wenn der Tag gesetzt wurde und dem Text in TagString entspricht
      151. If _CheckTagFunction(c, TagString) AndAlso Not _CheckControls.ContainsKey(DirectCast(c, T)) Then
      152. If c.GetType() IsNot GetType(T2) Then
      153. Dim o As Object = DirectCast(c, T2)
      154. End If
      155. 'TextBox in einem Dictionary speichern, mit dem Wert false
      156. _CheckControls.Add(DirectCast(c, T), DefaultValue)
      157. _CheckFailedReason.Add(DirectCast(c, T), DefaultReason)
      158. _RegisteredEvents.Add(DirectCast(c, T), New RegisteredEvent With {.TagString = TagString, .EventHandler = .EventHandler, .EventString = [Event]})
      159. 'entsprechenden Handler hinzufügen
      160. c.GetType.GetEvent([Event], Reflection.BindingFlags.Instance Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.IgnoreCase).AddEventHandler(c, EventHandler)
      161. End If
      162. Catch ex As Exception
      163. _ex.Add(New RegistrationException(String.Format("Failed to Register Control: {0}{1}{0}", Chr(34), c.Name), ex, c))
      164. End Try
      165. Next
      166. For Each kv As KeyValuePair(Of T, Boolean) In tmpControls.Reverse
      167. _CheckControls.Add(kv.Key, kv.Value)
      168. Next
      169. End Sub
      170. ''' <summary>
      171. ''' Tries to register all Controls with the given tag
      172. ''' This Method doesn't throw an Exception, instead it will store the Exceptions in a list and returns a value of success
      173. ''' </summary>
      174. ''' <typeparam name="T2">The type of the controls, which will be registered</typeparam>
      175. ''' <param name="Event">The name of the event, for which the handler will be registered</param>
      176. ''' <param name="TagString">The tag string, for chosing which control to registerd</param>
      177. ''' <param name="parent">The parent, which will be searched for controls</param>
      178. ''' <param name="EventHandler">The Eventhandler which will be registered with the event</param>
      179. ''' <param name="Exceptions">When a Exception is thrown, it will be stored in this list</param>
      180. ''' <param name="DefaultValue">The default value of the validation after registering</param>
      181. ''' <param name="DefaultReason">The default fail reason, when no validation has run</param>
      182. ''' <returns>If the registration of all controls was successfull</returns>
      183. ''' <remarks></remarks>
      184. Public Function TryRegisterEvents(Of T2 As Control)(ByVal [Event] As String, ByVal TagString As String, ByVal parent As Control, ByRef EventHandler As [Delegate], ByRef Exceptions As List(Of RegistrationException), Optional ByVal DefaultValue As Boolean = False, Optional ByVal DefaultReason As String = "") As Boolean
      185. _ex.Clear()
      186. TryRegisterEvents(Of T2)([Event], TagString, parent, EventHandler, DefaultValue, DefaultReason)
      187. Exceptions = _ex
      188. If _ex.Count > 0 Then Return False Else Return True
      189. End Function
      190. ''' <summary>
      191. ''' Gets the first control which failed validation.
      192. ''' </summary>
      193. ''' <returns>first failed control or nothing, if there are no failed validations</returns>
      194. ''' <remarks></remarks>
      195. Public Function GetFirstFalse() As T
      196. If Not CheckAllValues() Then
      197. Return _CheckControls.First(Function(kv As KeyValuePair(Of T, Boolean)) Not kv.Value).Key
      198. Else
      199. Return Nothing
      200. End If
      201. End Function
      202. ''' <summary>
      203. ''' Gets the fail reason for the given control, if there is one
      204. ''' </summary>
      205. ''' <param name="c">The control, which will be searched for</param>
      206. ''' <returns></returns>
      207. ''' <remarks></remarks>
      208. Public Function GetReason(ByVal c As T) As String
      209. If _CheckFailedReason.ContainsKey(c) Then Return _CheckFailedReason(c) Else Return String.Empty
      210. End Function
      211. ''' <summary>
      212. ''' Value to store the success of validation
      213. ''' </summary>
      214. ''' <param name="c">The control for which the value will be stored</param>
      215. ''' <param name="Reason">The reason for failed validation</param>
      216. ''' <value>Defines if validation is successful</value>
      217. ''' <remarks></remarks>
      218. Default Public WriteOnly Property Controls(ByVal c As T, Optional ByVal Reason As String = "") As Boolean
      219. Set(value As Boolean)
      220. Dim IsReasonSet As Boolean = _CheckFailedReason.ContainsKey(c)
      221. If value AndAlso IsReasonSet Then
      222. _CheckFailedReason.Remove(c)
      223. ElseIf IsReasonSet Then
      224. _CheckFailedReason(c) = Reason
      225. Else
      226. _CheckFailedReason.Add(c, Reason)
      227. End If
      228. _CheckControls(c) = value
      229. End Set
      230. End Property
      231. ''' <summary>
      232. ''' Checks if all validations passed
      233. ''' </summary>
      234. ''' <returns></returns>
      235. ''' <remarks></remarks>
      236. Function CheckAllValues() As Boolean
      237. Return _CheckControls.Values.All(Function(value As Boolean) value)
      238. End Function
      239. End Class
      240. ''' <summary>
      241. ''' Used for automated registering of Events and controls, to validate the input.
      242. ''' </summary>
      243. ''' <remarks></remarks>
      244. Public Class Checker
      245. Inherits Checker(Of Control)
      246. ''' <summary>
      247. ''' Creates a new instance
      248. ''' </summary>
      249. ''' <remarks></remarks>
      250. Sub New()
      251. MyBase.New()
      252. End Sub
      253. ''' <summary>
      254. ''' Creates a new instance
      255. ''' </summary>
      256. ''' <param name="CheckTagFunction">Defines how the .Tag Property is validated for registration</param>
      257. ''' <remarks></remarks>
      258. Sub New(ByRef CheckTagFunction As Func(Of Control, String, Boolean))
      259. _CheckTagFunction = CheckTagFunction
      260. End Sub
      261. End Class
      262. ''' <summary>
      263. ''' Custom Exception, holds the control which failed to register and the reason within InnerException
      264. ''' </summary>
      265. ''' <remarks></remarks>
      266. Public Class RegistrationException
      267. Inherits ApplicationException
      268. Sub New(ByVal Message As String, ByVal InnerException As Exception, ByVal FailedControl As Control)
      269. MyBase.New(Message, InnerException)
      270. _FailedControl = FailedControl
      271. End Sub
      272. Private _FailedControl As Control
      273. ''' <summary>
      274. ''' The Control which failed to register
      275. ''' </summary>
      276. ''' <value></value>
      277. ''' <returns></returns>
      278. ''' <remarks></remarks>
      279. Public ReadOnly Property FailedControl As Control
      280. Get
      281. Return _FailedControl
      282. End Get
      283. End Property
      284. End Class


      Die Verwendung möchte ich an einem kleinen Beispiel demonstrieren.

      Wir haben eine klein Form mit 4 TextBoxen und einem Button. Bei jeder dieser TextBoxen soll nach der Eingabe überprüft werden, ob eine Zahl eingegeben wurde und ob diese im Bereich 20-100 liegt. Wenn ja, darf der Button gedrückt werden. Wenn nein, soll beim drücken des Buttons eine entsprechende MessageBox erscheinen und der Focus soll auf die entsprechende Textbox gelegt werden.

      Zuerst legt man die Form mit den Controls an und setzt das Tag-Feld auf einen bestimmten Textwert.

      Danach kommt folgender Code:

      VB.NET-Quellcode

      1. Public Class Form1
      2. 'Man kann auch nur New CheckAutomation verwenden, dann wird das Ganze auf den Typ Control beschränkt, nicht wie hier auf Textbox
      3. Private Check As New CheckAutomation.Checker(Of TextBox)
      4. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
      5. 'Hier werden der Eventhandler allen Objekten zugewiesen, die im Tag "this" (Ohne Anführungszeichen) stehen haben
      6. 'Tritt eine Exception auf, wird diese in eine Liste geschrieben und über den Parameter Exceptions zurückgegeben.
      7. Dim Exceptions As New List(Of CheckAutomation.RegistrationException)
      8. If Not Check.TryRegisterEvents("TextChanged", "this", Me,
      9. New EventHandler(AddressOf CheckTextbox), Exceptions,
      10. DefaultReason:="Value not set!") Then
      11. For Each ex As CheckAutomation.RegistrationException In Exceptions
      12. MsgBox(ex.Message)
      13. Next
      14. End If
      15. End Sub
      16. 'Die Werte für die Überprüfung
      17. Private Const MINVAL As Integer = 20
      18. Private Const MAXVAL As Integer = 100
      19. 'Die Prüfroutine (Eventhandler)
      20. Private Sub CheckTextbox(sender As System.Object, e As System.EventArgs)
      21. Dim tmp As TextBox = DirectCast(sender, TextBox)
      22. If String.IsNullOrWhiteSpace(tmp.Text) Then
      23. 'Hier wird entsprechend der Überprüfung der Wert auf True oder False gesetzt.
      24. 'Optional kann man noch eine Begründung angeben, warum die Überprüfung fehlgeschlagen ist
      25. Check(tmp, "Value not set!") = False
      26. Else
      27. Dim value As Integer
      28. If Integer.TryParse(tmp.Text, value) Then
      29. Select Case value
      30. Case Is < MINVAL
      31. Check(tmp, "Value is too low!") = False
      32. Case Is > MAXVAL
      33. Check(tmp, "Value is too high!") = False
      34. Case Else
      35. Check(tmp) = True
      36. End Select
      37. Else
      38. Check(tmp, "Value is not a number!") = False
      39. End If
      40. End If
      41. End Sub
      42. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      43. 'Beim drücken des Buttons wird gecheckt, ob alle Werte auf True sind.
      44. If Check.CheckAllValues Then
      45. MsgBox("Berechnen :D")
      46. Else
      47. 'Wenn nicht, holen wir uns das erste Fehlerhafte Control
      48. Dim c As TextBox = Check.GetFirstFalse
      49. 'Geben die Begründung aus
      50. MsgBox(Check.GetReason(c))
      51. 'Und setzen den Fokus
      52. c.Focus()
      53. c.SelectAll()
      54. End If
      55. End Sub
      56. End Class


      Wenn nun eine TextBox hinzukommt, muss bei dieser nur der Tag richtig ausgefüllt werden, damit diese mitüberprüft wird.

      Man kann auch verschiedene Prüfroutinen erstellen und diese über verschiedene Tags zuordnen. Das Schema bleibt das gleiche und wenn etwas ergänzt wird, wird es automatisch miteinbezogen.

      Und wenn dynamisch ein Control entsteht, kann man die Registrierung erneut durchführen lassen ohne das irgendetwas doppelt ist.

      Alle registrierten Controls können dann direkt bei der Berechnung über die Funktion GetregisteredControls() als Liste geholt und abgearbeitet werden.


      //Edit:
      Auf Wunsch von @ErfinderDesRades hab ich mal ein kleines DemoProjekt erstellt. Das könnte man zwar auch anders lösen, allerdings soll es ja nur die Funktionsweise veranschaulichen.

      Zum Testen, Zip herunterladen, extrahieren, im VS aufmachen und starten. In die Textboxen links dürfen nur Werte klein/gleich 100 ind die Textboxen rechts dürfen nur Werte Größer hundert. Das Programm prüft dies und berechnet dann die Summe der linken Textboxen und multipliziert diese mit der Summe der rechten Textboxen.

      Nun zur eigentlichen Funktionalität meines Codes:
      Zum Testen einfach mal eine TextBox löschen und erneut ausführen. Die Prüfung und Berechnung funktioniert noch immer, ohne dass am Code was geändert wurde.
      Fügt man eine neue TextBox hinzu, muss nur im Tag-Feld chk1 (für Werte <= 100) oder chk2 (für Werte > 100) eingetragen werden und schon ist die neue TextBox in die Berechnung integriert.

      Man kann also, wie unten schon geschrieben, Controls hinzufügen ohne den Code anpassen zu müssen.

      //Edit2: Habe nun den Code um eine XML-Dokumentation, die Möglichkeit die Registrierung wieder aufzuheben, eine dynamische Prüfung für das Tag-Feld und einer besseren Methode für das interne Speichern der Controls erweitert.

      Aktuelles Projekt (inkl. Testprogramm):
      CheckAutomation01.zip

      P.S.: @ErfinderDesRades ich hab mir mal deinen SolutionExplorer ausgeborgt :D
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D

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

      So ungefähr kann ich folgen - aber scheint mir doch kompliziert. Kannst du vlt. (d)eine Test-Anwendung anhängen?

      Es gibt ja auch bereits sehr erprobte Prüflogik-Konzepte - man spricht üblicherweise von "Validierung", oder "Daten-Validierung".
      Der simpelste Ansatz ist das Validating-Event - gugge etwa Control.Validating + ErrorProvider
      Aber die Datenklassen selbst können auch das IDataErrorInfo-Interface implementieren, und dann eine standardisierte Selbst-Überprüfung bereitstellen, mit der die umgebende Business-Logik und Oberfläche ihren Anforderungen entsprechend umgeht.
      Es gibt sogar ganze Frameworks zum Thema - insgesamt angerissen ist das hier: IDataErrorInfo (allerdings c#)

      ErfinderDesRades schrieb:

      aber scheint mir doch kompliziert. Kannst du vlt. (d)eine Test-Anwendung anhängen?

      Hab mir das mit Control.Validating + ErrorProvider angesehen und das ist eig auch nur ein EventHandler, welcher die Eingabe überprüft.

      Man kann auch das Validating Event zum prüfen nehmen. Mein Code sorgt nur dafür, dass jedes Steuerelement, welches einen gewissen Tag hat, automatisch den richtigen EventHandler zugewiesen bekommt.

      Also wenn du eine Form hast, die z.B.: 100 TextBoxen zur Eingabe hat (für komplexe Berechnungen), dann schreibst du einen EventHandler und weist beim Designen der Form bereits den Tag zu. Danach schreibst du einmal den Code, der die Eingabe prüft und die Handler zuweist und fertig. Ab dann werden alle neuen FormElemente, die den Tag gesetzt haben, automatisch in die Prüfung mit einbezogen ohne dass du den CodeEditor nochmal aufmachen musst.

      Alle Steuerelemente die durch meinen Code überwacht werden, können nach erfolgreicher Prüfung als List(Of Control) geholt werden und dynamisch in die Berechnung eingebaut werden. Wenn man das ganze gut strukturiert, braucht man die Berechnungslogik bei neuen Steuerelementen auch nicht anpassen.

      Ich werd eine kleine Demo schreiben und sie oben anhängen. :)
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D
      Hier der Gegen-Entwurf (intelligente Business-Objekte, Trennung von Daten und Oberfläche)
      Form2

      VB.NET-Quellcode

      1. Public Class Form2
      2. Private Sub Button_Click(sender As Object, e As EventArgs) Handles btGenerate.Click, btCalculate.Click
      3. With NumberDts
      4. Select Case True
      5. Case sender Is btGenerate
      6. For i = 0 To 10
      7. .Summand1.AddSummand1Row(i * 10)
      8. Next
      9. For i = 0 To 5
      10. .Summand2.AddSummand2Row(100 + i * 10)
      11. Next
      12. Case sender Is btCalculate
      13. If .HasErrors Then
      14. MessageBox.Show("Ausrechnung verweigert", "du DAU!")
      15. Else
      16. MessageBox.Show((.Summand1.Sum(Function(smd) smd.Value) * .Summand2.Sum(Function(smd) smd.Value)).ToString)
      17. End If
      18. End Select
      19. End With
      20. End Sub
      21. End Class
      CodeBehind im typisierten "NumberDts"-Dataset

      VB.NET-Quellcode

      1. Partial Class NumberDts
      2. Partial Class Summand1DataTable
      3. Private Sub Summand1DataTable_Summand1RowChanging(sender As Object, e As Summand1RowChangeEvent) Handles Me.Summand1RowChanging
      4. If e.Row.Value > 100 Then e.Row.SetColumnError(ValueColumn.Ordinal, "value must be <= 100!")
      5. End Sub
      6. End Class
      7. Partial Class Summand2DataTable
      8. Private Sub Summand2DataTable_Summand2RowChanging(sender As Object, e As Summand2RowChangeEvent) Handles Me.Summand2RowChanging
      9. If e.Row.Value < 100 Then e.Row.SetColumnError(ValueColumn.Ordinal, "value must be >= 100!")
      10. End Sub
      11. End Class
      12. End Class
      Dateien
      • SumBuilder.zip

        (22,41 kB, 97 mal heruntergeladen, zuletzt: )

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

      Habs mir noch nicht im Detail angesehen, aber probiert hab ichs. Bis auf einen kleinen Fehler siehts gut aus.


      Aber dennoch nicht ganz das, an was ich gedacht habe. Denn wenn du ein neues Steuerelement hinzufügst, welches die selbe Prüflogik haben soll, dann musst du doch erst wieder den Code umschreiben oder?

      Und was ist wenn du verschiedene Berechnungen und verschiedene Prüflogiken hast und diese dynamisch zuweisen willst, allerdings die Berechnung und die Prüflogik nicht auf einzelne Steuerelemente legen willst.

      z.B.:
      Ich hab zwei Prüflogiken: PL1 & PL2
      Ich hab zwei Berechnugslogiken: BR1 & BR2
      Und ich hab 4 Controls mit folgender Zuweisung:

      Control1 = PL1 & BR1
      Control2 = PL1 & BR2
      Control3 = PL2 & BR1
      Control4 = PL2 & BR2

      Und hier soll nun ein weiteres Steuerelement eingefügt werden, welches eine willkürliche Kombination aus PL und BR bekommen soll.

      Da wirds mit normalen Mitteln schwer. Mit meinem Code braucht man nur das neue Steuerelement setzen und den Tag anpassen. Fertig.
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D
      nanu? Wie hast du das hingekriegt? 8|
      Ich krieg das nicht hin.
      Edit: ok - fixed

      BiedermannS schrieb:

      Denn wenn du ein neues Steuerelement hinzufügst, welches die selbe Prüflogik haben soll, dann musst du doch erst wieder den Code umschreiben oder?
      nein überhaupt nicht. Das Dataset kennt selbst die Fehler, die es enthält, und die kann ich mit Leichtigkeit abrufen und bemeckern - egal welche und wieviele Controls da dran hängen.

      Ich bin nur grad genervt, weil sieht so aus, dass nur DGV die ErrorProvider so hübsch eingebaut hat. Bei gebundenen Textboxen muss man dem EP dann doch erzählen, welches Control welchen Fehler hat - also wie in Control.Validating + ErrorProvider gezeigt.
      Wpf ist da scheints besser - da schreit die Textbox, sobald die Datenquelle "Selbstanzeige" erstattet.

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

      ErfinderDesRades schrieb:

      nanu? Wie hast du das hingekriegt? 8|

      Ich hab den Wert auf eine Zahl kleiner 100 gesetzt, damit er den Fehler anzeigt und danach wieder auf eine Zahl größer 100 gesetzt.

      Liegt wahrscheinlich daran, dass bei der Prüfung bei falschen Werten der Error gesetzt, allerdings bei richtigen Werten nichts gemacht wird.
      SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

      Weil einfach, einfach zu einfach ist! :D