VB.NET - Wie kann die Border der aktiven Textbox (Textbox hat Focus) markiert werden?

  • VB.NET

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von Gather.

    VB.NET - Wie kann die Border der aktiven Textbox (Textbox hat Focus) markiert werden?

    Hallo,
    ich versuche in meiner Anwendung, die über Textboxen als benutzerdefiniertes Steuerelement verfügt, die Textbox, die aktuell den Focus erhält/besitzt farbig zu markieren.

    Über den OnPaint-Event habe ich es schon versucht, aber das erfordert in der Sub New() den SetStale auf Userpaint zu setzen. Dies hat den unschönen Nebeneffekt, daß auch der Text in der Textbox neu gezeichnet werden muß. Wenn ich dies wie unten aufgezeigt mache, verändert sich der Text bei der ersten Eingabe auch auf Fett (was er ohne das Neuzeichnen nicht tun würde) und die Caret-Steuerung funktioniert nicht mehr sauber von Zeichen zu Zeichen sondern geht z.B. bei CursorRechts wahllos mal 0,5, mal 1, mal 2 Zeichen weiter.

    Hier der Code im benutzerdefinierten Steuerelement (bringt das oben beschriebene Verhalten und scheidet als Lösung aus):

    VB.NET-Quellcode

    1. Protected Overrides Sub OnPaint(e As PaintEventArgs)
    2. Dim p As Pen = Nothing
    3. If Me.Focused Then
    4. p = New Pen(Brushes.Red)
    5. Else
    6. p = New Pen(Brushes.Gainsboro)
    7. End If
    8. e.Graphics.DrawRectangle(p, 0, 0, Me.ClientSize.Width - 1, Me.ClientSize.Height - 1)
    9. e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), Me.ClientRectangle)
    10. MyBase.OnPaint(e)
    11. End Sub


    Das Textbox-Controls im Formular haben (soweit ich weiß) keine Abweichungen vom VB.NET-Default.

    Weiß jemand Rat?
    Vielen Dank.


    P.S: Dies ist meine erste Frage hier. Daher muß ich auch noch fragen, wie ich hier die Leerzeilen in der Quellcode-Darstellung weg bekomme.

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

    Willkommen im Forum.
    Klingt jetzt aus der Hüfte rausgeschossen, aber dann mal doch im Paint_EventHandler statt in der OnPaint-Methode.

    bzgl. Leerzeilen: Beitrag bearbeiten. Wenn da keine Leerzeilen zu sehen sind, klick in der Symbolleiste über dem Beitrag auf [Quellcode]. Spätestens dann sollten sie auftauchen und löschbar sein.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

    Ah, ich glaub, ich hab mich so ausgedrückt, dass es nicht richtig rüberkommt. Denn wenn Du schon dabei bist, OnPaint zu überschreiben, ist das, was ich meine, noch einige Stufen früher auf der Lernkurve angesiedelt. Wenn das Paint-Event ausgelöst wird, wird ja geschaut, wer es abonniert hat, also wer sich für dieses Event interessiert und einen EventHandler eingerichtet hat (also das gleiche wie wenn man im Designer nen Doppelklick auf nen Button macht und dann in den Button-Click-EventHandler kommt):

    VB.NET-Quellcode

    1. Public Class UserControl1
    2. Private Sub TextBox1_Paint(sender As Object, e As PaintEventArgs) Handles TextBox1.Paint
    3. 'hier malen nach Zahlen bzw. eben Deinen eigenen Rahmen
    4. End Sub
    5. End Class


    EDIT: Es ist tatsächlich ein Schuss aus der Hüfte gewesen.

    MSDN schrieb:


    TextBoxBase.Paint Event
    […]
    Occurs when the control is redrawn. This event is not relevant for this class.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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

    VaporiZed schrieb:

    Paint_EventHandler statt in der OnPaint-Methode.
    Beide Methoden sind völlig äquivalent, nur dass der Eventhandler außerhalb und OnPaint innerhalb der Control-Klasse aufgerufen wird.
    @claus Wenn Du Dein Control selber zeichnest, musst Du tatsächlich alles zeichnen.
    Ggf. gibt es da so was wie DrawFocusRect() oder so.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Danke Rod.
    Ja, so habe ich mir das jetzt auch gedacht. Der Unterschied kam wohl dadurch zustande, daß YaporiZed ein "Benutzersteuerelement" als Basis hat, und ich ein "Benutzerdefiniertes Steuerelement"

    Rod, genau so etwas wie DrawFocusRect() suche ich. Ich will ja nur den Rahmen der Textbox verändern. Das muß wohl über die Win-API geschehen, aber da bin och zu schwach drauf.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Du zeichnest erst, rufst dann aber base.OnPaint() auf, somit zeichnet die Basisklasse über dich drüber. Ruf zuerst base.OnPaint auf, dann zeichnest du dein Rechteck. Das was @RodFromGermany meint gibt es schon gewrappt als ​ControlPaint.DrawFocusRectangle()
    Hmmm, jetzt wird's für mich schwierig.
    @ Rod: Danke für den Link. Mein Problem damit: ich habe kein UserControl (siehe oben) und damit auch kein hwnd davon.

    @ Gonger: Dir auch mein Dank. Mit ControlPaint.DrawFocusRectangle() hab ich jetzt ein wenig probiert. Ja, einen andersfarbigen Rahmen erhalte ich so. Aber nur einmal.
    Danach kann ich die Farbe weder zurückseten noch wechseln.

    Aber jetzt habe ich zumindest einen Ansatz zum weiterdenken. Graphiks war noch nie so das meine. Ich schreibe nur Progs mit DB-Anbindung zur Verwaltung. Da reichen meist die Standard-Werkzeuge, die im grafischen Bereich angeboten werden. Da werde ich wohl noch 'ne frische Portion Hirnschmalz für eine Lösung brauchen. ;)
    Entweder du erstellst dir ein eigenes Textbox Control, was allerding mehr Arbeit ist, oder zu zeichnest simpel im OnPaint-Event des ParentControls der Textbox.
    Heißt, die Textbox liegt direkt auf einer Form, dann zeichnest du im ONPaint-Event der Form.

    VB.NET-Quellcode

    1. ''' <summary>
    2. ''' Gibt das derzeitig fokusierte Control an.
    3. ''' </summary>
    4. Private _FocusedControl As Control = Nothing
    5. Private Sub UpdateForm(sender As Object, e As EventArgs) Handles TextBox3.GotFocus, TextBox3.LostFocus
    6. _FocusedControl = CType(sender, Control)
    7. If Not CType(sender, Control).Focused Then _FocusedControl = Nothing
    8. Me.Invalidate()
    9. End Sub
    10. Protected Overrides Sub OnPaint(e As PaintEventArgs)
    11. Dim g As Graphics = e.Graphics
    12. Using p As New Pen(Color.Red) 'Farbe der Umrandung.
    13. If _FocusedControl IsNot Nothing Then
    14. ' Zeichnet einfaches Rechteck um das fokusierte Steuerelement.
    15. g.DrawRectangle(p, New Rectangle(_FocusedControl.Location.X - 1, _FocusedControl.Location.Y - 1, _FocusedControl.Width + 1, _FocusedControl.Height + 1))
    16. End If
    17. End Using
    18. MyBase.OnPaint(e)
    19. End Sub


    Prinzipiell funktioniert das Ganze von alleine, es muss lediglich der Eventhandler auf die gewünschten Steuerelemente gesetzt werden.
    Entweder mit AddHandler oder wie von mir im Beispiel mittels Handles TextBox3.GotFocus, TextBox3.LostFocus

    Sollte es Verständsnisfragen etc. geben einfach melden.
    Bilder
    • Ergebnis.PNG

      24,84 kB, 656×320, 161 mal angesehen
    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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

    Hi Gather,
    auch Dir sag ich zunächst Danke für die Mühe, die Du aufgewendet hast.

    Ich bin seit gestern ein wenig weiter gekommen. Zunächst muß ich deswegen genauer erklären, daß meine Textbox-Controls kein Benutzersteuerelement (also Basis Userform) sondern ein benutzerdefiniertes Steuelement (also eine class mit Inherit Textbox) ist. Da hatte ich der Unterschied bis heute Nacht nur halbwegs realisiert. Jetzt ist mir klar, daß die Unterscheidung wichtig ist.

    Auf die Lösung, die Du anbietest, Gather, bin ich im Laufe des Abends auch gestoßen. Nur da wäre der farbige Rahmen der Textbox einmalig festgeschrieben. So wollte ich das nicht. Meine Vorstellung war, daß sich die Bordercolor ändert, wenn die Textbox das aktive Control ist (also irgendwo bei OnEnter oder GotFocus) und auf den Standard zurückgesetzt wird, wenn es den Focus veerliert. Zu diesem Zeitpunkt im Programmablauf scheint es aber ein Paint-oder Draw-Ereignis nicht zu geben. Zumindest habe ich keins gefunden.
    In VB6 wäre mein Problem wahrscheinlich über die Win-API und das Handle der Textbox zu lösen. Das bleibt hier wohl auch als letzte Möglichkeit. Aber dafür bin ich nicht fit genug. Wenn sich hier niemand findet, der mir zeigt wie das gehen würde (oder wie eben ein alternativer .NET-Weg aussieht, der zum selben Ziel führt), schiebe ich mein Problemchen, denn mehr ist's ja nicht, auf einen Tag, an dem ich Lust habe mal wieder Neuland zu erforschen.
    Einstweilen bin ich aber noch für jede Hilfe dankbar.

    claus schrieb:

    ich habe kein UserControl (siehe oben) und damit auch kein hwnd davon.
    Sondern?
    Wo malst Du denn hin?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Derzeit eben nirgendwo. Die Controls sind auf der Form, und ich wollte sie beim Enter- oder GotFocus-Event mit einer neuen, farbigen Border beglücken, die spätestens beim Leave-Event zurückgesetzt wird.
    Backcolor, Forecolor und auch Borderstyle lassen sich ja zu diesen 2 Zeitpunkten manipulieren. Nur eine Bordercolor, die gibt es eben nicht.
    In der Class, die von Textbox erbt, und wo ich diese Funktionalität integrieren wollte, werden die Steuerung der Pfeiltasten, zur Eingabe zulässige Zeichen usw. abgearbeitet.

    Ich würde ja gern meinen bisherigen Code der Class posten, aber irgendwie gelingt mir das nicht in einer lesbaren Weise. Wenn man es sieht, wär's vielleicht klarer.

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

    @claus Wenn Du direkt auf die Form malst, ist deren Handle relevant.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    claus schrieb:

    Auf die Lösung, die Du anbietest, Gather, bin ich im Laufe des Abends auch gestoßen
    Ähm das bezweifle ich, da die Lösung von mir oben selber verfasst wurde.
    Bezüglich deines zweiten Punktes:

    claus schrieb:

    Meine Vorstellung war, daß sich die Bordercolor ändert, wenn die Textbox das aktive Control ist (also irgendwo bei OnEnter oder GotFocus) und auf den Standard zurückgesetzt wird, wenn es den Focus veerliert.

    Der von mir bereitgestellte Code macht genau das. Du musst lediglich Handles TextBox3.GotFocus, TextBox3.LostFocus auf die von dir gewünschten Controls anpassen. Z.B. Handles MyTextbox.GotFocus, MyTextbox.LostFocus

    Kurz und knapp habe ich das Gefühl, dass du (sofern ich Sie duzen darf) dir meine Lösung nicht angesehen hast.
    Falls es an einem Verständnis Problem liegt kann ich das Ganze etwas auflösen:

    Die Methode UpdateForm wird (sofern die Handler geändert wurden) von dem gewünschten Control aufgerufen, sobald dieses den Fokus verliert oder bekommt.
    Im Anschluss wird evaluiert ob der Focus vorhanden ist oder nicht. Wenn ja wird das Steuerelement einer Variablen übergeben (_FocusedControl ).
    Nun wird die Form mit Me.Invalidate() neu gezeichnet.

    Beim Neuzeichnen wird die OnPaint Methode aufgerufen. In dieser wird, sofern die Variable _FocusedControl nicht Nothing entspricht,
    ein Rechteck um das Control mit einem vordefinierten roten Pen gezeichnet.


    Du siehst also, dass sobald das Control den Fokus verliert, wird der Effekt (in diesem Fall die Umrandung) wieder rückgängig gemacht.
    Also genau wie von dir oben beschrieben, und gewünscht. Willst du das Ganze mit Enter bzw. Leave machen musst du, wie nun schon mehrmals beschrieben,
    lediglich die Handler der Methode UpdateForm ändern.

    Mfg: Gather
    Private Nachrichten bezüglich VB-Fragen werden Ignoriert!


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

    @Rod und Gather: Habt Dank für Eure Geduld und Mühe.

    Gather, klar darfst Du mich duzen. Auch jetzt als Opa habe ich da keinen falschen Stolz.

    Angesehen habe ich mir Deinen Code. Allerdings war ich im Irrtum über die Sub UpdateForm. Dieser Irrtum entsprang der Tatsache, daß ich die beiden Handles schlichtweg überlesen/falsch gelesen habe. Ich habe bei den Handles phantasievollerweise Form statt Textbox3 gelesen :S . Immer wieder muß ich bei mir feststellen, daß ich um die Bedeutung der Handles zwar weiß, aber ihre Bedeutung und Tragweite noch immer nicht verinnerlicht habe. In den letzten 15 Jahren mit VB6/VBA-Programmierung gab's das bei mir ja nicht. Und davor erst recht nicht. Darum Danke daß Ihr mich nochmals "mit der Nase" draufgestoßen habt.
    Ich werde Gather's Lösung mal einbauen und dann schon sehen - hoffe ich. Im Moment stoß ich mich noch daran, daß ich wohl eine ziemlich lange Liste bei den Handles erhalten werde, denn da muß ja dann jede Textbox registriert werden. Und übergreifend dann in jeder Form. Mir wäre es lieber gewesen, es könnte in meiner benutzerdefinierten Textbox-Class abgehandelt werden. Aber das geht wohl nicht.

    Dochmals Danke.
    Stop. Bevor Du mit ganz vielen Handles-Klauseln rumhantierst: es gibt das AddHandler-(und RemoveHandler-) Schlüsselwort. Wenn Du sehr viele CEs mit einer Prozedur verdrahten willst, ist das ggf. der bessere Weg. Und der einzige, wenn Du CEs dynamisch erstellst.

    Ein Beispiel:

    VB.NET-Quellcode

    1. Private Sub LinkAllMyBeautifulTextBoxesWithMySpecialTextChangedEventHandlerProcedure()
    2. AddHandler TextBox1.TextChanged, AddressOf MySpecialSub
    3. AddHandler TextBox2.TextChanged, AddressOf MySpecialSub
    4. AddHandler TextBox3.TextChanged, AddressOf MySpecialSub
    5. AddHandler TextBox4.TextChanged, AddressOf MySpecialSub
    6. AddHandler TextBox5.TextChanged, AddressOf MySpecialSub
    7. End Sub
    8. Private Sub MySpecialSub(sender as Object, e as EventArgs)
    9. 'hier eben, was passieren soll, wenn in irgendeiner der o.g. TextBoxen sich der Text ändert
    10. 'über DirectCast(sender, TextBox) kommst Du dann auch an die TextBox ran, bei der sich was geändert hat
    11. End Sub

    Das kann Dir - vernünftig eingesetzt (z.B. mit einer durchdachten Schleife oder eben bei der Erstellung von TextBoxern und anderen CEs zur Laufzeit) - sehr viel Code sparen und Dein Programm flexibler gestalten.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Probier's aus ;)
    Klar, geht schon. Aber bedenke, dass Du dann keinen weiteren Zugriff auf andere UserForm1-Eigenschaften (wenn die TextBoxen in UserForm1 wären) oder-CEs hast. Musst Du wissen, ob das zu Deinem Programm past.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.