Tab zum Einrücken in einer Textbox

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von petaod.

    Tab zum Einrücken in einer Textbox

    Hi,

    ich habe eine Multilne Textbox txtData. In dieser Textbox erfasse ich z.B. HTML Code. Dazu muss man häufig einrücken. Das würde ich gern per Tastendruck ermöglichen.

    Das hab ich wie folgt kodiert:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. TabIndent()
    3. End Sub
    4. Private Sub TabIndent()
    5. 'Get caret postition
    6. Dim myCaretPosition As Integer = txtData.SelectionStart
    7. If myCaretPosition < 0 Then Exit Sub 'Just to make sure
    8. 'Insert indentation
    9. Dim indent As String = "xxxx" 'Insert four blanks (test=xxxx)
    10. txtData.Text = txtData.Text.Insert(myCaretPosition, indent)
    11. 'Set new caret position
    12. txtData.Select(myCaretPosition + indent.Length, 0)
    13. txtData.Focus()
    14. End Sub


    Das funktionier auch sehr gut. Aber natürlich würde ich das gern (wie in Editoren üblich) nicht mit einem Button Click sondern mit der TAB Taste erreichen.

    Die TAB Taste ist aber von Windows belegt. Und deshalb muss ich das KeyPreview Ereignis verwenden:

    VB.NET-Quellcode

    1. Private Sub txtData_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles txtData.PreviewKeyDown
    2. If e.KeyCode = Keys.Tab Then TabIndent()
    3. End Sub


    Das funktioniert auch ... aber ...

    ... aber die Windows TAB Funktion wird NACH dem Preview Ereignis immer noch ausgelöst. Und damit springt der Focus auf die nächste Tab Position und txtData verliert den Focus. Meine Anweisung txtData.Focus wird "ausgehebelt".

    Das ist blöde. Denn so muss ich nach einem TAB immer erst noch einmal txtData anklicken, um den Focus zurück zu erhalten, bevor ich den nächsten TAB eingeben kann. So richtig gut kann man damit nicht arbeiten !

    Tja .... wie kann entweder verhindern, dass die Windows Funktion TAB ausgeführt wird (KeyPreview kennt weder e.handled noch e.cancel !)

    Oder wenn schon der Focus wechselt, wie kann ich den Focus auf txtData zurück erhalten ? Dazu müsste ich eine Funktion auslösen können, die erst NACH dem Windows TAB ausgeführt wird. Leider gibt es kein PreviewKeyUp Ereignis ...

    Die üblichen Techniken funktionieren hier also nicht. Any bright ideas ?

    LG
    Peter
    Es gibt doch auch bei TextBoxen ein KeyUp-Ereignis.



    Ansonsten würde ich es richtig dirty machen und nach dem feuern einen Timer aktivieren der abfragt, ob deine Textbox den Focus hat.
    Wenn nicht, DANN focus auf Textbox und timer deaktivieren. Ich weiß, viele finden die Lösung eklig, aber es würde klappen. Aber ich denke, du kannst das KeyUp-Ereignis nutzen.
    Die Multiline-eingestellte TextBox hat auch ne Property AcceptsTab, die dafür da ist, einen Tab in den Text einzutragen und so den Sprung zum nächsten CE zu verhindern.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Danke für eure Ratschläge !

    xored schrieb:

    Es gibt doch auch bei TextBoxen ein KeyUp-Ereignis.


    Jau, das ist schon richtig. Nur bei der TAB Taste feuert das Event nicht, weil das Dingens vom Windows abgefangen wird.

    VaporiZed schrieb:

    Die Multiline-eingestellte TextBox hat auch ne Property AcceptsTab, die dafür da ist, einen Tab in den Text einzutragen und so den Sprung zum nächsten CE zu verhindern.


    Damit wird dann TAB zu einer "normalen" Taste und kann mit KeyUp abgefangen werden. Aber ... damit wird dann ein TAB-Sprung (Chr(9)) eingefügt, den ich wieder entfernen müsste.

    Ich hab jetzt doch eine Lösung gefunden. Ich verwende einfach das LostFocus Event von txtData !

    VB.NET-Quellcode

    1. Dim KeepFocus As Boolean = False 'Keep focus on txtData flag
    2. Private Sub txtData_PreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs) Handles txtData.PreviewKeyDown
    3. If e.KeyCode = Keys.Tab Then 'TAB key depressed on txtData
    4. KeepFocus = True 'Indicate focus must be restored on txtData
    5. TabIndent() 'Insert indentation at caret position
    6. End If
    7. 'Focus will now be moved by windows ...
    8. End Sub
    9. Private Sub txtData_LostFocus(sender As Object, e As EventArgs) Handles txtData.LostFocus
    10. If Not KeepFocus Then Exit Sub 'TAB outside txtData - accept new focus
    11. KeepFocus = False 'Reset "Keep focus flag"
    12. txtData.Focus() 'Restore Focus
    13. End Sub


    Das klappt hervorragend !

    Also die Diskussion mit euch hat mich dann doch auf die richtige Schiene gebracht ... :)

    Danke an alle Ratgeber ... und Daumen hoch !

    LG
    Peter
    Ist mir irgendwie zu Pfeil-Rücken-Brust-Auge. Wenn Du statt nem Tab/Chr(9) lieber 4mal das x haben willst, dann geht das doch ganz einfach:

    VB.NET-Quellcode

    1. Private ReadOnly Indent As String = "xxxx"
    2. Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
    3. Dim Position = TextBox1.SelectionStart
    4. TextBox1.Text = TextBox1.Text.Replace(Microsoft.VisualBasic.Chr(9), Indent)
    5. TextBox1.SelectionStart = Position + Indent.Length - 1
    6. End Sub
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    VaporiZed schrieb:

    Ist mir irgendwie zu Pfeil-Rücken-Brust-Auge.


    Na ja, entweder lässt man die TAB Taste den Focus wechseln. Oder man lässt sie ein Chr(9) einfügen. Beides muss man rückgängig machen.

    Bei deiner Lösung mit dem Chr(9) stört mich aber der Replace:

    VB.NET-Quellcode

    1. TextBox1.Text = TextBox1.Text.Replace(Microsoft.VisualBasic.Chr(9), Indent)


    Dieser Befehl wirkt nämlich auf das GANZE Dokument. Und wenn dieses Dokument noch irgendwo anders Chr(9) Tabstops enthalten sollte, dann würden die auch entfernt werden, obwohl sie mit der Aktion gar nichts zu tun haben.

    Man müsste also besser mit Text.Remove und Text.Insert arbeiten. So sollte das dann funktionieren.

    Ich denke, es gibt zwei Techniken, um die vermaledeite TAB Taste in den Griff zu bekommen. Und was man macht, ist dann letztendlich Geschmackssache.

    LG
    Peter

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

    Aha, das spezifiziert etwas die Anforderungen, aber auch noch machbar:

    VB.NET-Quellcode

    1. Private ReadOnly Indent As String = "xxxx"
    2. Private HandleTab As Boolean = True
    3. Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
    4. If Not HandleTab OrElse TextBox1.SelectionStart = 0 OrElse TextBox1.Text.Substring(TextBox1.SelectionStart - 1, 1) <> Microsoft.VisualBasic.Chr(9) Then Return
    5. HandleTab = False
    6. Dim Position = TextBox1.SelectionStart
    7. TextBox1.Text = TextBox1.Text.Substring(0, Position - 1) & Indent & TextBox1.Text.Substring(Position)
    8. TextBox1.SelectionStart = Position + Indent.Length - 1
    9. HandleTab = True
    10. End Sub

    Ist ggf. kürzer mit Regex, aber da bin ich raus.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    @Peter329 Was funktioniert denn nicht mit den Tabulatoren?
    Was ist an Deinen Vorstellungen anders, als es die TextBox ermöglicht?
    Nimm ggf. eine RichTextBox, da kannst Du die Positionen der Tabulatoren vorgeben, die gelten bis zum Überschreiben der Positionen:

    VB.NET-Quellcode

    1. RichTextBox1.AcceptsTab = True
    2. RichTextBox1.SelectionTabs = New Integer() {80, 160, 240}
    3. RichTextBox1.AppendText(vbTab & "hhh" & vbTab & "uuu" & vbTab & "iii")
    4. RichTextBox1.AppendText(vbLf)
    5. RichTextBox1.SelectionTabs = New Integer() {40, 80, 120}
    6. RichTextBox1.AppendText(vbTab & "hhh" & vbTab & "uuu" & vbTab & "iii")
    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!

    VaporiZed schrieb:

    Ist ggf. kürzer mit Regex, aber da bin ich raus.


    Jau, den Text.Remove + Text.Insert kann man mit den beiden Text.Substring Anweisungen konsolidieren.Mit RegEx.Replace ist das ähnlich wie mit Text.Replace ... man kann es m.W. nicht auf eine Occurence einschränken ... Aber das ist ja wirklich nicht das Problem.

    RodFromGermany schrieb:

    Was funktioniert denn nicht mit den Tabulatoren?


    Mein Problem ist doch längst gelöst. Die TAB-Taste verhält sich eben anders als ein "normaler" Key ... und deswegen muss man das halt speziell behandeln.

    Und natürlich werde ich mich auch noch bei Gelegenheit mit der "RichTextBox" befassen.

    Also .... ich bin bereits restlos zufrieden. Mein Problem ist vernünftig gelöst. :)

    Danke noch einmal an alle Ratgeber und einen schönen coronafreien Sonntag Nachmittag !

    LG
    Peter

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

    Peter329 schrieb:

    ähnlich wie mit Text.Replace ... man kann es m.W. nicht auf eine Occurence einschränken
    Es sei denn man nimmt die gute alte Methode aus dem Microsoft.VisualBasic Namespace:
    docs.microsoft.com/de-de/dotne…sualbasic.strings.replace
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --