Rubrik: Controls · TextBox & RichTextBox | VB-Versionen: VB2010 | 15.04.11 |
Textbox mit Funktion zur Formelberechnung TextboxCompute ist ein von Windows.Forms.Textbox abgeleitetes Control, das Formeln ausgewerten kann (CodeDom). | ||
Autor: Manfred Bohn | Bewertung: | Views: 12.970 |
ohne Homepage | System: Win2k, WinXP, Win7, Win8, Win10, Win11 | Beispielprojekt auf CD |
Die Klasse "TextboxCompute" ist von Windows.Forms.TextBox abgeleitet. Das Control ermöglicht die Eingabe numerischer Werte als Formel und wertet sie aus.
Die Betätigung der Taste "Return" veranlasst die Auswertung des eingegebenen Strings (in der Text-Eigenschaft des Control). Im Erfolgsfall wird die Text-Eigenschaft durch das Ergebnis der Auswertung überschrieben. Im Fehlerfall bleibt die Eigenschaft unverändert.
Nachdem die Klasse dem Projekt hinzugefügt und das Projekt übersetzt worden ist, steht das Control in der Toolbox zur Verfügung.
Der Eigenschaft "Errorlabel" kann ein Label zugewiesen werden. Ggf. wird die erste Fehlermeldung beim Kompilieren des eingegebenen Strings angezeigt. Sinnvoll ist das nur, wenn ausschließlich numerische Eingaben in der TextBoxCompute-Instanz zugelassen sind.
(Es werden im Errorlabel Syntaxfehler oder fehlende Deklarationen gemeldet, falls eine nicht-numerische Eingabe erfolgt ist).
Die Eigenschaft "Digits" erlaubt die Festlegung, auf wie viele Nachkommastellen das Ergebnis der ausgewerteten Formel gerundet werden soll, bevor es in die Text-Eigenschaft übertragen wird.
Der zuletzt eingegebene String (vor erfolgreicher Auswertung) kann durch die schreibgeschützte Eigenschaft "GetLastString" abgefragt werden.
Details:
Die eingegebenen Zahlen werden als Double-Werte verarbeitet. Sollte eine Auswertung scheitern oder ein IEEE-Sonderwert im Ergebnis resultieren, wird der eingegebene String nicht geändert. Das optionale Errorlabel enthält eine entsprechende Information.
Die Exponentialdarstellung von Werten ist möglich. Es werden implizite Erweiterungs-Konvertierungen der eingegebenen Ziffernfolgen in den Datentyp Double vorgenommen - dabei kann es zu Genauigkeitsverlusten kommen. Die Operatoren für Ganzzahl-Division und für Mod-Division stehen zur Verfügung.
Beim Kompilieren der Formel wird der Namespace "System.Math" importiert. Im Textfeld können deshalb mathematische Funktionen wie Abs, Log, Sqrt, Sin oder Truncate (syntaktisch korrekt) verwendet werden.
Funktionen wie INT, FIX, VAL stehen nicht zur Verfügung, weil sie im Namespace Microsoft.VisualBasic" angesiedelt sind. Die entsprechende Import-Anweisung kann bei Bedarf im Code eingefügt werden.
Die Formel wird direkt übersetzt. VB verarbeitet als Dezimalpunkt den US-Dezimalpunkt. Aus diesem Grund wird der kulturspezifische Dezimalpunkt vor dem Kompilieren entsprechend umgewandelt. Es werden BEIDE Zeichen intern als Dezimalpunkt verarbeitet. (Wegen dieser Ersetzung ist z.B. die Pow-Funktion in der Eingabe nicht verwendbar!)
Bei Zuweisung eines Auswertungs-Resultats auf die Text-Eigenschaft wird das "TextChanged"-Event der Instanz ausgelöst.
Das Control kann auch als Multiline-Textbox verwendet werden, aber eingefügte Zeilenumbrüche werden als Fehler gewertet. Das VB-Zeilenfortsetzungszeichen wird akzeptiert.
Option Strict On Option Explicit On Option Infer Off Imports System.CodeDom.Compiler Public Class TextboxCompute Inherits Windows.Forms.TextBox Const cNL As String = Microsoft.VisualBasic.vbNewLine Private _dezimalpunkt As String = _ Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator Const cStd_Dezimale As String = "." ' VB-Standard-Dezimalpunkt Private _errorlabel As Windows.Forms.Label ' Anzeige der ersten Fehlermeldung Private _digits As Integer = 4 ' Ergebnisrundung, Nachkommastellen Private _LastString As String = String.Empty ' Text-Eigenschaft vor Auswertung Protected Overrides Sub OnKeyDown(ByVal e As System.Windows.Forms.KeyEventArgs) _LastString = String.Empty If _errorlabel IsNot Nothing Then _errorlabel.Text = String.Empty If e.KeyCode = Keys.Return Then If Not String.IsNullOrWhiteSpace(Me.Text) Then Dim itext As String = Me.Text ' Standard-Dezimalpunkt einrichten itext = itext.Replace(_dezimalpunkt, cStd_Dezimale) ' Zusammenstellung des Codes der Assembly für die Berechnung Dim CCode As String = _ "Option Strict On : Option Explicit On : Option Infer Off" & cNL & _ "Imports System.Math" & cNL & _ "NameSpace nsComputeFormula" & cNL & _ "Public Class ComputeFormula" & cNL & _ "Public Function Compute() as Double?" & cNL & _ "Dim erg? as Double = " & itext & cNL & _ "Return erg" & cNL & _ "End Function" & cNL & "End Class" & cNL & "End NameSpace" Dim myAsm As System.Reflection.Assembly = _ CreateAssemblyFromCode(CCode) If Not myAsm Is Nothing Then Dim myCls As Object = _ myAsm.CreateInstance("nsComputeFormula.ComputeFormula") ' Zugriff auf die Berechnungsmethode in der Klasse Dim myMethod As System.Reflection.MethodInfo = _ myCls.GetType.GetMethod("Compute") Dim erg? As Double = CType(myMethod.Invoke(myCls, Nothing), Double?) If erg.HasValue Then Dim ierg As Double = erg.Value If Not Double.IsNaN(ierg) And Not Double.IsInfinity(ierg) Then _LastString = Me.Text ' Originalstring speichern Me.Text = CStr(Math.Round(ierg, _digits)) Me.SelectionStart = Me.TextLength ElseIf _errorlabel IsNot Nothing Then _errorlabel.Text = "Ergebnis enthält IEEE-Sonderwert: " & CStr(ierg) End If End If ' HasValue? End If ' ASM <> nothing? End If ' String.Isnullorwhitespace? End If ' Keycode = Keys.Return? MyBase.OnKeyDown(e) End Sub Private Function CreateAssemblyFromCode(ByVal Code As String) As _ System.Reflection.Assembly ' Assembly aus VB.NET-Sourcecode in Codestring erstellen Dim VB_CP As CodeDomProvider = _ New Microsoft.VisualBasic.VBCodeProvider() Dim ComParams As New CompilerParameters() _ With {.GenerateInMemory = True} ' Code Compilieren und das Compiler-Ergebnis ermitteln Dim ComResults As CompilerResults = _ VB_CP.CompileAssemblyFromSource(ComParams, Code) VB_CP.Dispose() If ComResults.Errors.Count > 0 Then If _errorlabel IsNot Nothing Then _errorlabel.Text = ComResults.Errors(0).ErrorText End If Return Nothing Else Return ComResults.CompiledAssembly End If End Function ''' <summary>Optional: Label für die erste Compiler-Fehlermeldung</summary> Public Property Errorlabel() As Label Set(ByVal value As Label) _errorlabel = value End Set Get Return _errorlabel End Get End Property ''' <summary>Optional: Nachkommastellen für Ergebnisrundung</summary> Public Property Digits As Integer Set(ByVal value As Integer) If value < 0 Or value > 12 Then value = 4 _digits = value End Set Get Return _digits End Get End Property ''' <summary>Inhalt der Text-Eigenschaft vor der erfolgreichen Auswertung</summary> Public ReadOnly Property GetLastString() As String Get Return _LastString End Get End Property End Class