Rubrik: .NET | VB-Versionen: VB.NET | 01.10.03 |
Vererbungslehre für absolute Anfänger (VB.NET) Die häufigste Frage, die mir in den verschieden News-Groups über .NET aufgefallen ist, war die grundsätzliche Frage wie funktioniert die Vererbung nun an einem praktischen und kleinen Beispiel wirklich. Dabei wurde immer wieder gefragt: "...Ich habe eine Textbox und möchte dieser bestimmte Dinge beibringen, wie z.B. nur numerische Eingaben entgegenzunehmen, oder den Text immer in einem bestimmten Format anzeigen. Wie muss ich dabei vorgehen ?" | ||
Autor: Frank Pacher | Bewertung: | Views: 41.530 |
Die häufigste Frage, die mir in den verschieden News-Groups über .NET aufgefallen ist, war die grundsätzliche Frage wie funktioniert die Vererbung nun an einem praktischen und kleinen Beispiel wirklich. Dabei wurde immer wieder gefragt: "...Ich habe eine Textbox und möchte dieser bestimmte Dinge beibringen, wie z.B. nur numerische Eingaben entgegenzunehmen, oder den Text immer in einem bestimmten Format anzeigen. Wie muss ich dabei vorgehen ?"
Um diese Frage soll es hier gehen. Doch dies soll kein Aufsatz für Profi-Programmierer, sondern eine einfache Einführung für den absoluten VB.NET-Anfänger sein.
Zuvor müssen jedoch dem angehenden Programmierer vier Dinge (zumindest in den Grundzügen) klar sein:
- Was ist eigentlich Vererbung ?
- Was ist ein Objekt ?
- Was sind eigentlich Eigenschaften (Propertys) ?
- Was sind eigentlich Methoden ?
Dazu ebenfalls vier kurze (einfache) Antworten:
- Wenn ich eine Klasse (auch dieser Begriff sollte bekannt sein), welche schon existiert, nehme und daraus eine neue Klasse erstelle. (Dabei wird die Ursprungsklasse nicht geändert.) Beispiel: Ich habe die Klasse "Fahrrad", ich erzeuge eine neue Klasse vom Typ "Fahrrad" aber diesmal lasse ich die Gangschaltung weg. Voilá - wir haben vererbt.
- Aus dieser Klasse schaffen wir dann die jeweiligen Objekte. Hier z.B. "Fahrrad von Horst" oder "Fahrrad von Franz". Objekte sind also die konkreten Abbilder der jeweiligen Klasse.
- Wie der Name schon sagt, eine Eigenschaft eben. Bei unserem "Fahrrad" zum Beispiel die Farbe (Die ja bei jedem Objekt "Fahrrad" anders sein kann.)
- Eine Methode ist etwas, das eine Änderung eines Zustandes hervorruft. Bei unserem "Fahrrad" nehmen wir die Methode "schalten", die die Gangschaltung anweist in den jeweiligen Gang umzusetzen - also ihren Zustand zu ändern. Und das bei jedem einzelnen Objekt ohne dass sich die einzelnen Objekte gegenseitig beeinflussen.
Ich weiß, dass das eine sehr extrem vereinfachte Darstellung ist, aber für den Anfang reicht das erst mal.
Doch nun zurück zu unserer Textbox. Ich möchte am Beispiel dieser Textbox zeigen, wie man mit einer neuen "NumericTextbox (-klasse)", welche von der ursprünglichen "Textbox (-klasse)" geerbt hat, das grundlegende Eingabeverhalten aller "NumericTextbox-Objekte" in einem Programm beeinflussen kann.
Also, Aufgabe soll sein, eine numerische Textbox zu erstellen, die:
- nur Zahlen, sowie +/- akzeptiert;
- wahlweise negative Zahlen zulässt oder ablehnt;
- die Anzeige der Nachkommastellen soll variabel sein (oder auch gar keine Nachkommastellen);
- automatische Rundung je nach Nachkommastellen (wird eine 8stellige Zahl eingegeben, aber es sollen nur 4 Stellen angezeigt werden, wird gerundet);
- der eingegebene Wert soll aber trotzdem in der Textbox erhalten bleiben (also alle 8 Stellen oder eben der gerundete);
- mit diesem Wert soll direkt gerechnet werden können (ohne Umwandlungsfunktionen von Text in Zahl nutzen zu müssen). Dies ist erforderlich, da das Textboxobjekt die Eingabe im Datentyp Text hält, aber für Berechnungen ja der Datentyp Zahl notwendig ist (in unserem Beispiel als Zahlentyp Double);
- es sollen Maximal- und Minimalwerte nutzbar sein;
- dazu noch entsprechende Farbeinstellungen (z.B. negative Zahlen erscheinen rot u.ä.) und Fehlermeldungen bei Unzulässigkeiten (z.B. bei Min-/Max-Werten)
- bei der Eingabe von "=" soll der Windows-Rechner aufgerufen werden;
- und bei der Eingabe von "?" eine kleine Infobox mit Erläuterungen.
Soweit die Anforderungen an die NumericTextbox-Klasse.
Das Grundgerüst
Doch nun "In Medias Res". (Eine Anmerkung sei noch gestattet: VB.NET nimmt Fehler in der Groß-/Kleinschreibung übel. Es ist also nicht egal ob der Name oder die Methode groß oder klein geschrieben wird.)
Starten Sie die Visual Studio Entwicklungsumgebung und erstellen ein neues Projekt vom Typ Klassenbibliothek. Als Projektname geben Sie NumValueInputLib ein.
Abb.1: Neues Projekt "Klassenbibliothek"
Klicken Sie anschließend im Projektmappen-Explorer mit der rechten Maustaste auf "Class1.vb" und benennen die neue Klasse NumValueInput.vb. Die Bezeichnung "Class1" im Codefenster ändern Sie ebenfalls:
Public Class NumValueInput
Da wir eine Klasse bilden wollen, welche den Ursprung in der Textbox hat, müssen wir zuerst festlegen wo das System nach dieser "Ursprungsklasse" suchen soll und dass sie von dieser erbt.
Public Class NumValueInput ' Name der neuen Klasse ' erben von System.Windows.Forms.TextBox Inherits System.Windows.Forms.TextBox End Class ' Abschließen der Klassendefinition
Um die TextBox der Windows.Forms Klasse verwenden zu können, muss die Komponente System.Windows.Forms dem Projekt als Verweis hinzugefügt werden. Klicken Sie im Projektmappen-Explorer hierzu mit der rechten Maustaste auf den Eintrag Verweise und wählen den Befehl Verweis hinzufügen. Im Dialogfeld Verweise hinzufügen doppelklicken Sie anschließend auf den Eintrag System.Windows.Forms.dll und klicken abschließend auf OK.
Eigentlich haben wir jetzt schon das Grundgerüst für eine neue Textboxklasse fertig. Die würde sogar schon funktionieren, da sie ja alle Eigenheiten der originalen Textboxklasse geerbt hat. Aber wir wollen ja unsere eigenen Erweiterungen einführen.
Also definieren wir uns eine Region "Propertys". So haben wir es später leichter, den Quelltext zu lesen. Danach die Variablen, welche innerhalb der Klasse die Werte der Eigenschaften aufnehmen sollen.
#Region "Propertys" Private varValueNegativ As Boolean ' negative Zahlen erlaubt Ja/Nein #End Region
Den Region-Block fügen Sie bitte direkt nach Inherits System.Windows.Forms.TextBox ein.
Nun können wir mit der Definition der ersten Eigenschaft beginnen. (Die weiteren sind dem Quelltext der Klasse zu entnehmen.)
Eine Eigenschaft besteht aus zwei Teilen, dem Teil der den Wert entgegen nimmt, und dem Teil der den Wert der Eigenschaft zurückgibt. Soll der Eigenschaft ein Anfangswert zugewiesen werden, so kann dies später, in der "NEW()-Methode" geschehen.
Eingeleitet wird die Eigenschaftsdefinition mit dem Schlüsselwort Property. Davor wird festgelegt, ob die Eigenschaft nach außen sichtbar ist ("Public") oder nicht ("Private"). Außerdem wird der Typ der Eigenschaft bestimmt.
Wir werden jetzt als Beispiel die Eigenschaft anlegen, welche die Eingabe von negativen Zahlen erlaubt ("True") oder verbietet ("False"). Sie hat also den Datentyp Boolean. Der "GET"-Teil gibt den Inhalt der Variablen varValueNegativ zurück, während der "SET"-Teil diese Variable mit dem Wert füllt.
#Region "Propertys" Private varValueNegativ As Boolean ' negative Zahlen erlaubt Ja/Nein ' Negativ-Werte erlaubt? Public Property NegativValue() As Boolean Get Return varValueNegativ End Get Set(ByVal Value As Boolean) varValueNegativ = Value End Set End Property #End Region
Unser neues Control hat jetzt eine neue Eigenschaft erhalten. Doch jetzt müssen wir diese auch noch in einer Methode entsprechend nutzen.
Erstellen von Methoden
Für die einzelnen Methoden fügen wir zur besseren Übersicht wiederum eine eigene Region ein und benennen diese My Code. In dieser Region erstellen wir eine Methode die, je nach der Einstellung in der Eigenschaft "NegativValue", die Eingabe negativer Zahlen akzeptiert oder ablehnt.
#Region " My Code " Public Function CheckValue() As Boolean ' ***************************************************** ' * RETURN Boolean ' * True: Eingabe ist OK ' * False: Eingabe ist keine Zahl, oder die Vorgabe ' * von "NegativValue" wird nicht eingehalten ' ***************************************************** Dim lResume As Boolean Dim lNeg As Boolean lNeg = False If IsNumeric(Me.Text) Then If Not Me.NegativValue Then If System.Math.Sign(CDbl(Me.Text)) = -1 Then lNeg = True lResume = False Else lResume = True End If Else lResume = True End If Else lResume = False End If If lResume Then CheckValue = True Else If MsgBox(IIf(lNeg, "Negative Zahlen nicht erlaubt", _ "Nur Zahlen eingeben") & _ Chr(10) & Chr(13) & _ "Auf 0 setzen und weiter ?", MsgBoxStyle.YesNo + _ MsgBoxStyle.Exclamation, "Fehler") = MsgBoxResult.Yes Then Me.Text = "0" CheckValue = True Else CheckValue = False End If End If End Function #End Region
Diese Methode binden wir nun an die OnValid-Methode. Hierbei wird mit Protected zuerst festgelegt, dass der Zugriff nur in der Klasse und davon abgeleiteten Klassen erfolgen darf. Danach wird mit Overrides die Originale Methode überschrieben. Am Ende sollte jedoch nicht der Aufruf der Original-Methode fehlen. Mit e wird das Abbruch-Ereignis festgelegt.
#Region " My Code " ... ' Ruft die Check-Methode auf Protected Overrides Sub OnValidating( _ ByVal e As System.ComponentModel.CancelEventArgs) Dim lResume As Boolean ' Aufruf der Prüf-Methode lResume = Me.CheckValue ' Bei e.Cancel = False kann später das Objekt ' normal beendet werden, bei e.Cancel = True ' kann das Objekt nicht verlassen werden e.Cancel = Not lResume ' TextBox neuzeichnen Me.Refresh() ' Die Original-Methode von TextBox aufrufen (!) MyBase.OnValidating(e) End Sub #End Region
Der komplette Code der Klasse
Nachfolgend der komplette Quellcode der neuen Klasse mit allen eingangs erfüllten Anforderungen.
Public Class NumValueInput ' Name der neuen Klasse ' erben von System.Windows.Forms.TextBox Inherits System.Windows.Forms.TextBox
Eigenschaften
#Region " Propertys " Private varCheckTemp As Boolean ' wird intern benötigt Private varValueDecimale As Integer ' Anzahl der Dezimalstellen Private varValueNumValue As Double ' eingegebener Wert als Double Private varValueExact As Boolean ' exakten Wert speichern Ja/Nein Private varValueToolTip As String ' Text für die Tooltips Private varValueErrorColor As System.Drawing.Color ' Farbe bei Falscheingaben Private varValueNegativColor As System.Drawing.Color ' Farbe bei negativen Zahlen Private varValueErrorMsg As String ' Text bei Fehleingaben Private varValueNegativ As Boolean ' negative Zahlen erlaubt Ja/Nein Private varValueMinimum As Double ' Minimalwert für Eingabe Private varValueMaximum As Double ' Maximalwert für Eingabe Private varValueErrMinMaxMsg As String ' Text für Fehleingabe bei Min/Max Private varValueErrNegativMsg As String ' Text für Fehleingabe bei negativ Private varValueErrQuestMsg As String ' Fragetext bei Fehleingaben Private varValueErrMsgTitle As String ' Überschrift für Fehlermeldungen ' Maximal Wert ? Public Property MaximumValue() As Double Get Return varValueMaximum End Get Set(ByVal Value As Double) varValueMaximum = Value End Set End Property ' Minimal Wert ? Public Property MinimumValue() As Double Get Return varValueMinimum End Get Set(ByVal Value As Double) varValueMinimum = Value End Set End Property ' Negative Werte erlaubt ? Public Property NegativValue() As Boolean Get Return varValueNegativ End Get Set(ByVal Value As Boolean) varValueNegativ = Value End Set End Property ' Allgemeine Fehlermeldung z.B.: ' "Auf 0 setzen und weiter ?" Public Property ErrorMsgPart() As String Get Return varValueErrQuestMsg End Get Set(ByVal Value As String) varValueErrQuestMsg = Value End Set End Property ' Fehlermeldung für falsche Werte Public Property ErrorMsg() As String Get Return varValueErrorMsg End Get Set(ByVal Value As String) varValueErrorMsg = Value End Set End Property ' Titel der Fehlermeldungen Public Property ErrorMsgTitle() As String Get Return varValueErrMsgTitle End Get Set(ByVal Value As String) varValueErrMsgTitle = Value End Set End Property ' Fehlermeldung bei nicht erlaubten negativen Zahlen Public Property ErrorNegativMsg() As String Get Return varValueErrNegativMsg End Get Set(ByVal Value As String) varValueErrNegativMsg = Value End Set End Property ' Fehlermeldung bei Nichteinhalten von Min/Max Public Property ErrorMinMaxMsg() As String Get Return varValueErrMinMaxMsg End Get Set(ByVal Value As String) varValueErrMinMaxMsg = Value End Set End Property ' Farbe für negative Zahlen Public Property NegativColor() As System.Drawing.Color Get Return varValueNegativColor End Get Set(ByVal Value As System.Drawing.Color) varValueNegativColor = Value End Set End Property ' Farbe bei fehlerhaften Eingaben Public Property ErrorColor() As System.Drawing.Color Get Return varValueErrorColor End Get Set(ByVal Value As System.Drawing.Color) varValueErrorColor = Value End Set End Property ' Anzahl der Dezimalstellen Public Property Decimale() As Integer Get Return varValueDecimale End Get Set(ByVal Value As Integer) varValueDecimale = Value End Set End Property ' ToolTipText Public Property ToolTip() As String Get Return varValueToolTip End Get Set(ByVal Value As String) varValueToolTip = Value End Set End Property ' Numerischer Wert (Typ ist Double) von TextBox.Text ' in dieser Eigenschaft steht der Wert als Double direkt ' zu Ihrer Verfügung, z.B. um damit direkt zu rechnen ' ohne vorher diesen konvertieren zu müssen Public Property NumValue() As Double Get Return varValueNumValue End Get Set(ByVal Value As Double) varValueNumValue = Value End Set End Property ' Sollen die mit auf Decimale gerundeten Werten ' oder die kompletten Eingabewerte in <NumValue> gespeichert ' werden (zur weiteren Nutzung bei Berechnungen / ' und die gerundeten (Decimale) werden nur angezeigt ' Achtung !! Es ist nur eine Formatierungs-Rundung ' KEINE mathematische Rundung!!! Public Property CalculateExact() As Boolean Get Return varValueExact End Get Set(ByVal Value As Boolean) varValueExact = Value End Set End Property #End Region
Standard-Vorgaben (Default-Werte)
#Region " Vom Windows Form Designer generierter Code " Public Sub New() MyBase.New() ' Dieser Aufruf ist für den Windows Form-Designer erforderlich. InitializeComponent() ' Initialisierungen nach dem Aufruf InitializeComponent() hinzufügen Me.varValueErrorColor = System.Drawing.Color.Red Me.varValueNegativColor = System.Drawing.Color.Red Me.varValueNumValue = 0 Me.Text = "0" Me.TextAlign = Windows.Forms.HorizontalAlignment.Right Me.CalculateExact = True Me.varValueErrorMsg = "Input only numeric value!" Me.varValueNegativ = True Me.varValueToolTip = "Input numeric value" Me.varValueErrMinMaxMsg = "Value is not in allow range !" Me.varValueErrNegativMsg = "Negativ Value is not allow!" Me.varValueErrQuestMsg = "Continue (and set the value to 0)? " Me.varValueErrMsgTitle = "Input-Error" End Sub ' UserControl1 überschreibt den Löschvorgang zur Bereinigung der ' Komponentenliste. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Für Windows Form-Designer erforderlich Private components As System.ComponentModel.IContainer ' HINWEIS: Die folgende Prozedur ist für den Windows Form-Designer ' erforderlich ' Sie kann mit dem Windows Form-Designer modifiziert werden. ' Verwenden Sie nicht den Code-Editor zur Bearbeitung. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() components = New System.ComponentModel.Container End Sub #End Region
Methoden
#Region " My Code " Public Function CheckValue() As Boolean ' **************************************************** ' * Teste ob es eine numerische Eingabe ist ' * - < Min-Property ' * - > Max-Property ' * ' * RETURN Boolean: ' * True: Eingabe ist OK ' * False: keine Numerische Eingabe oder die Vorgabe ' * für negative zahlen oder die Vorgabe für ' * Min/Max- wird nicht eingehalten Dim lResume As Boolean Dim lNeg As Boolean lNeg = False Me.varCheckTemp = False If IsNumeric(Me.Text) Then If Not Me.NegativValue Then If System.Math.Sign(CDbl(Me.Text)) = -1 Then lNeg = True lResume = False Else lResume = True End If Else lResume = True End If Else lResume = False End If If lResume Then If Me.CalculateExact Then Me.NumValue = CDbl(Me.Text) End If Me.Text = Format(CDbl(Me.Text.Trim), Me.BuildFormatString()) If Not Me.CalculateExact Then Me.NumValue = CDbl(Me.Text) End If If System.Math.Sign(CDbl(Me.Text)) = -1 Then Me.ForeColor = varValueNegativColor Else Me.ForeColor = Me.DefaultForeColor End If CheckValue = True Else If MsgBox(IIf(lNeg, Me.ErrorNegativMsg, Me.ErrorMsg) & _ Chr(10) & Chr(13) & _ Me.ErrorMsgPart, MsgBoxStyle.YesNo + _ MsgBoxStyle.Exclamation, _ Me.ErrorMsgTitle) = MsgBoxResult.Yes Then Me.Text = "0" Me.NumValue = CDbl(Me.Text) Me.Text = Format(CDbl(Me.Text), Me.BuildFormatString()) Me.ForeColor = varValueErrorColor Me.varCheckTemp = True CheckValue = True Else Me.ForeColor = varValueErrorColor CheckValue = False End If End If End Function Private Function BuildFormatString() As String ' Bildet den Format-String für die Anzeige Dim sTemp As String Dim iLauf As Integer sTemp = "" If Me.Decimale > 0 Then sTemp = "." For iLauf = 0 To Me.Decimale - 1 sTemp = sTemp + "0" Next Else sTemp = " " End If sTemp = sTemp + " " BuildFormatString = "##,##0" & sTemp End Function Protected Overrides Sub OnValidating( _ ByVal e As System.ComponentModel.CancelEventArgs) ' Ruft Check-Methode Dim lResume As Boolean lResume = Me.CheckValue If lResume Then If Me.MinimumValue <> Me.MaximumValue Then If (Me.NumValue > Me.MaximumValue) Or _ (Me.NumValue < Me.MinimumValue) Then If Me.varCheckTemp Then Me.Text = Me.MinimumValue Me.NumValue = Me.MinimumValue Else MsgBox(Me.ErrorMinMaxMsg, MsgBoxStyle.Critical + _ MsgBoxStyle.OKOnly, Me.ErrorMsgTitle) lResume = False End If End If End If End If e.Cancel = Not lResume Me.Refresh() MyBase.OnValidating(e) End Sub Protected Overrides Sub OnKeyPress( _ ByVal e As Windows.Forms.KeyPressEventArgs) ' bei "?" zeige die Info-Box ' bei "=" wird CALC.EXE aufgerufen ' bei {ENTER} wird der Cursor weitergesetzt Dim notepadID As Integer Select Case e.KeyChar Case "?" Me.Info() Case Chr(13) Windows.Forms.SendKeys.Send("{TAB}") Case "=" notepadID = Shell("CALC.EXE", AppWinStyle.NormalFocus) ' Neue Instanz von Notepad aktivieren AppActivate(notepadID) End Select MyBase.OnKeyPress(e) End Sub Public Sub Info() MsgBox("TextBox-Class for numeric inputs only." & _ ControlChars.NewLine & _ "This Inputbox accept only numeric Values." & _ ControlChars.NewLine & _ ControlChars.NewLine & _ "(c) by F.Pacher", _ MsgBoxStyle.Information + MsgBoxStyle.OKOnly, "Info") End Sub #End Region
End Class ' Abschließen der Klassendefinition
Erstellen der DLL und Verwenden in VB.NET
Um die neue "NumValueInput"-Komponente nun in eigenen VB.NET Anwendungen nutzen zu können, muss diese als DLL kompiliert werden. Wählen Sie hierzu den Befehl NumValueInputLib erstellen im Menü Erstellen.
Die DLL selbst finden Sie jetzt im Unterverzeichnis Bin des Projekt-Ordners.
Registrieren der Komponente
Erstellen Sie als nächstes ein neues Projekt vom Typ Windows-Anwendung. Bevor das Control genutzt werden kann muß es allerdings einmalig im Framework registriert werden. Dazu klicken Sie mit der rechten Maustaste auf die Toolbox und wählen Element hinzufügen/entfernen (VS.NET 2003) bzw. Toolbox anpassen, wenn Sie mit Visual Studio .NET 2002 arbeiten.
Abb.2: Toolbox anpassen
Im darauf erscheinenden Dialog aktivieren Sie die Registerkarte .NET Framework Komponenten, klicken auf die Schaltfläche Durchsuchen und doppelklicken auf den Eintrag NumValueInputLib.dll im Bin-Verzeichnis des Projektordners der NumValueInput-Klasse.
Abb.3: Komponente hinzufügen
Schließen Sie den Dialog Toolbox anpassen, indem Sie auf OK klicken (Achten Sie darauf, dass das Häckchen der NumValueInputLib.dll aktiviert ist).
Verwenden der Komponente
Jetzt können Sie die neue Komponente in Ihrer Anwendung verwenden. Klicken Sie hierzu auf den Eintrag NumValueInput in der Toolbox der Entwicklungsumgebung und ziehen das Control auf die Form.
Abb.3: Verwenden der Komponente