Suchen in Textbox

  • VB.NET
  • .NET 4.5

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    Suchen in Textbox

    Hi,

    Ich versuche einen eigenen "Notepad" zu kodieren, weil mich einige Eigenheiten der originalen Notepad.exe stören.

    Das sollte ja eigentlich nicht gar so schwer sein. Ich habe eine Textbox txtData genommen, in die ich meinen File einlese ...

    In einem SearchFeld gebe ich meinen Suchbegriff ein. Sobald sich das Search Feld ändert führe ich meine Suchfunktion aus und markiere dann mit txtData.Select(start, length) den ersten gefundenen String. (s. Anhang)

    Allerding muss die txtData TextBox FOKUSSIERT bleiben. Sobald ich den Focus auf das Searchfeld richte VERSCHWINDET die Markierung.

    Das ist für meine Zwecke natürlich katastrophal, weil ich dadurch den zu suchenden String nicht flüssig in das SearchFeld eingeben kann ... sondern ständig das Search Feld neu fokussieren muss.

    Ich hoffe, ich habe mein Problem verständlich machen können !

    Hat jemand eine Idee, wie ich Daten in txtData so markieren kann, dass die Markierung auch erhalten bleibt, wenn txtData nicht den Focus hat ?

    Möglicherweise geht das mit einer TextBox nicht ... ich bin für alle alternativen Lösungswege offen !

    LG
    Peter
    Bilder
    • s 2019-11-07 11-15-395.jpg

      62,5 kB, 942×992, 26 mal angesehen

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

    Stelle die Property HideSelection der TextBox auf False.
    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 Hast Du Deine Probleme auch mit dem Notepad++?
    notepad-plus-plus.org/
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    ouch ... die Option hatte ich übersehen. Danke !

    Die Selection bleibt damit erhalten ... allerdings sind damit meine Probleme noch nicht vollständig gelöst.

    1. Wie man am nächsten Screenshot erkennt habe ich ich jetzt einen File am Wickel, der sehr lange Zeilen hat. Die Zeilen werden umgebrochen. Das ist manchmal aber nicht ganz so praktisch. Besser wäre es den Rest der Zeile nicht anzuzeigen ... das entspricht der Option "WordWrap=False" im originalen Notepad.

    Kann man das realisieren ? Natürlich darf ich die Zeilen nicht einfach abschneiden ... denn ich will den File ja auch wieder speichern !

    2. Ich habe den String "sender" gesucht ... wie man am rechten Schieberegler erkennt, wird der String irgendwo in der Mitte des Files gefunden. Wenn das Display aber am ANFANG des Files steht, sieht man den gefundenen String nicht.

    Ich müsste also wissen, wo der selektierte String steht und den Schieberregler entsprechend anpassen.

    Allerdings kann man nicht einfach die Zeilenhöhe mit der gefundenen Zeilennummer mal nehmen ... wegen der umgebrochenen Zeilen, die ja mehrfach zählen. Natürlich könnte ich ausrechnen, wie viele Zeilen wie oft umgebrochen werden ... aber das scheint mir reichlich unelegant.

    Gibt es eine einfache Möglichkeit, sicher zu stellen, dass der selektierte String auch in der aktuellen Anzeige erscheint ?

    LG
    Peter

    @RGF: habe gerade deinen Post gelesen ... Notepad++ kenne ich noch nicht ... den schaue ich mir mal an ... :)
    Bilder
    • s 2019-11-07 12-33-304.jpg

      121,5 kB, 881×1.000, 27 mal angesehen

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

    Die TextBox hat doch auch die Eigenschaft .WordWrap,
    die wirkt aber nur Visuell, wenn du Umbrüche mit in einer Datei speichern willst, mußt du selber was basteln.
    edit: Hier sind schonmal ein paar Ansätze zum manuellen Umbrechen.

    Als alternativen Texteditor, werfe ich mal Visual Studio Code in den Raum.

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

    Jetzt habe ich doch schon wieder eine Option übersehen .... :)

    Ok ... damit habe ich jetzt weitgehend die Funktionalität der Notepad.exe abgebildet ...

    Was halt noch fehlt ist das sichtbar machen des selektierten Strings. Denn die "Search" Funktion ist ziemlich "witzlos", wenn der String außerhalb des Display Windows liegt l

    Also noch einmal die Frage: ich habe den Start Index "ix" des selektierten String. Wie kann ich damit die Position des Schiebereglers ermitteln, damit der String im sichtbaren Teil der Textbox enthalten ist ?

    Ich hoffe, ich hab nicht schon wieder etwas elementares übersehen ....

    LG
    Peter

    Peter329 schrieb:

    txtData.Select(start, length)


    start und length einfach auf die Position der Treffer-Ergebnisse setzen.

    Muss natürlich berechnet werden.

    FindInString aber auf keinen Fall mehr nutzen, am besten RegEx.Matches als Array-Werte Pro Treffer.

    Einfach mal probieren mit der Auswahl des Textes zu arbeiten, ganz einfach.

    c.u. Joshi

    Peter329 schrieb:

    wenn der String außerhalb des Display Windows liegt l
    Da gibt es die Funktion .ScrollToCaret(), die sorgt dafür, dass die Cursorposition im Bildfeld erscheint.
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    @RFG: Die Methode .ScrollToCaret() tut genau das, wonach ich gesucht hatte ! Die ist auch super schnell ! Danke ! :D

    @Joshi: Jau, Performance ist ein Problem ... meine hausbackene Search Routine ist langsam wie eine Schnecke, sobald die Daten auch nur 50 KB groß sind ... Deshalb suche ich schon ein performanteres Coding ... möglicherweise ist RegEx die Lösung ....

    Ich frage mich nur, wie das mit RegEx.Matches funktionieren soll ? Denn mit Regex.Match bzw. Regex.NextMatch erhalte ich ja nur das gefundene Object in match.Value zurück. Ich brauche aber die Position des gefundenen Strings, also den Start Index des gefundenen Strings innerhalb der Daten. Wo bekomme ich den denn her ?

    Kann man mit RegEx dann auch rückwärts suchen, also vom Ende der Daten zum Anfang hin ?

    LG
    Peter
    Hi.

    Das geht ganz gut mit MatchCollection.

    Das RegEx.Matches kann in einer MatchCollection abgelegt werden.
    Die dann mittels einer "For Each"-schleife bequem durchlaufen kann.

    Der folgende Link führt zu einem übersetzten Artikel mit VB Beispiel. :)

    docs.microsoft.com/de-de/dotne…ion?view=netframework-4.8

    Anmerkung: Bei sehr langen Texten, ist das Arbeiten mit RegEx nicht zu empfehlen, weil sehr langsam.
    Aber das 50kB große Dateien schon sehr langsam sind, ist mir ein Rätsel.
    Kann es sein das noch einen Amiga benutzt? Musst dich nicht verstecken, habe immernoch meinen Amiga 2000. Amiga is not dead!!! schniff...

    Nun denn...

    Eine Strategie ist, nur den Inhalt des "sichtbaren" Textes zu bemühen.
    Sobald der sichtbare Text sich durch Scrollen ändert, erneut die suche anstoßen.

    Wie auch immer die Suchfunktion gestaltet wird, das ist eine komplexe Aufgabe, daher empfehle ich nicht jedes Feature eines "MSWord" selbst umzusetzen.

    Ich habe schon einige "Editoren" versucht umzusetzen, aber immer ist das SyntaxHighlighting" eine große Hürde, was genau die "Suche" von Wörtern beinhaltet.
    Dafür habe ich RegEx mittels MatchCollection genutzt, aber dauerte dementsprechend sehr lange.

    War für meinen Zweck genügend, aber dann dachte ich an andere Benutzer und merkte das das eher ein "optimierungs Problem" ist.

    RegEx wurde genau für diesen Zweck erfunden, aber hat halt den Nachteil der "Verlangsamung".

    ______________________________________________________

    Das "Regex.NextMatch" geht ja in einer MatchCollection, die intern schon existiert, zum nächsten "Match".

    Da eine "Suche" nach "Matches" immer eine "MatchCollection" erzeugt, ist es besser sich eine Variable "explizit" zu erstellen.

    Klingt kompliziert, ist aber sehr einfach.

    Ablauf:
    RegexMuster in einer Variable mitsamt des "Suchtext" anlegen/aktualisieren.
    Inhalts-Text einlesen und in einer Variable ablegen.
    MatchCollection Variable anlegen.
    Mustererkennung Anstoßen und Resultate in die MatchCollection-Variable ablegen.
    Dann eine Schleife über die MatchCollection-Variable ausführen.
    Innerhalb der Schleife die Treffer-Indexe berechnen und evtl. weiterverarbeiten.
    Treffer-Indexe am besten in eine separate Variable ablegen (Array oder Dictionary).

    Sobald, wie in Notepad, die "F3"-Taste gedrückt wird, zum nächsten Indexwert in der "Kette der Treffer-Indexe" springen.
    Nur mal ein Beispiel für ein erweitertes Suchfeature... nicht umbedingt umsetzen. :)

    Ich kann hier nur Empfehlen sich mit RegEx einmal als "Fingerübung" zu beschäftigen, aber es auch nicht zum Lebensinhalt zu machen...

    c.u. Joshi

    Peter329 schrieb:

    also vom Ende der Daten zum Anfang hin ?
    indem Du die MatchCollection in einer For-Schleife rückwärts durchläufst.
    Wenn Du mit String arbeitest, sieh Dir die String.LastIndexOf-Methode an.
    docs.microsoft.com/de-de/dotne…xof?view=netframework-4.8
    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).
    VB-Fragen über PN / Konversation werden ignoriert!
    Jau, die Search Routine ist tatsächlich hochkomplex. Ich hab mal versucht deine Ratschläge umzusetzen:

    Ich arbeite nicht mit RegEx. Den Funktionsumfang nach Schablonen zu suchen benötige ich in meiner Anwendung nicht - und da würde ich gern den Overhead vermeiden.

    Ich verwende stattdessen .IndexOf und .LastIndexOf

    Natürlich muss man da bei großen Files auf die Performance achten.

    Zunächst einmal bestimme ich den ersten Match mit .IndexOf - wenn das nicht erfolgreich ist, dann ist die Reise zu Ende (Kein Match vorhanden).

    Danach bestimme ich den letzen Match mit .LastIndexof - wenn ich da den gleichen Match erhalte, dann gibt es genau einen Match. Auch hier ist die Reise zu Ende (Blättern nicht verfügbar).

    Andernfalls habe ich genau ZWEI verschiedene Matches. Die zugehörigen Indices schreibe ich in ixList1 und ixLlist2.

    Wenn ich nun blättere schaue ich nach, ob ich über die ixList1 und ixLiist2 "hinauslaufe". Solange ich mich beim Blättern in der Liste bewege werden keine Indices nachgeladen. Andernfalls besorge ich mir für einen Block von 1000 Bytes die jeweils nächsten Indices vom Beginn bzw. vom Ende des Files. Das dauert nur Bruchteile von Sekunden.

    Damit "rennt" meine Routine auch für sehr große Files sauschnell ! Ich bin begeistert.

    Obwohl das so einfach klingt, ist die Logik ganz schön kniffelig. Ein richtiger Leckerbissen ... hehehe ... Ich hab mal die Routine zum Nachladen der Blöcke spaßeshalber eingestellt .. damit man eine Vorstellung davon hat ... :)

    VB.NET-Quellcode

    1. Private Sub GetNextDataBlock(intDirection As Integer)
    2. Dim ix0 As Integer = txtData.Text.IndexOf(txtSearch.Text, ixLow + 1) 'Check more matches available
    3. If ix0 >= ixHigh Then 'No more matches found
    4. ixLow = ixHigh 'Indicate search completed
    5. Exit Sub 'All done
    6. End If
    7. If intDirection = +1 Then 'Search forward
    8. Dim ixFrom As Integer = ixLow + 1 'Point to next data byte
    9. Dim ixTo As Integer = ixLow + blkSize 'Search next block
    10. If ixTo < ix0 Then ixTo = ix0 'Enforce inclusion of first match
    11. If ixTo > ixHigh Then ixTo = ixHigh 'Limit to lower search boundary of ixList2
    12. Dim ix1 As Integer = ixList1(ixList1.Count - 1) 'Last entry in ixlist1
    13. While ix1 < ixTo
    14. ix1 = txtData.Text.IndexOf(txtSearch.Text, ix1 + 1) 'Search bottom to top (next match)
    15. If ix1 = -1 Then Exit While 'No more matches found
    16. ixList1.Add(ix1) 'Add to the end of the list
    17. End While
    18. ixLow = ixTo 'Update upper boundary of ixList1
    19. End If
    20. If intDirection = -1 Then 'Search forward
    21. Dim ixFrom As Integer = ixHigh - blkSize 'Point to start data byte
    22. If ixFrom <= ixLow Then ixFrom = ixLow + 1 'Limit to upper search boundary of ixList1
    23. Dim k As Integer = 0 'Count found matches
    24. Dim ix1 As Integer = ixFrom
    25. While ix1 > -1
    26. ix1 = txtData.Text.IndexOf(txtSearch.Text, ix1 + 1) 'Search bottom to top (next match)
    27. If ix1 = -1 Then 'No match found
    28. If k > 0 Then Exit While 'New match found
    29. ixFrom -= blkSize 'No match found - increase interval
    30. ix1 = ixFrom 'Set ix1 to start of interval
    31. If ix1 <= ixLow Then ix1 = ixLow + 1 'Limit to upper search boundary of ixList1
    32. Continue While 'Search match
    33. End If
    34. k += 1 'Count match
    35. ptrList += 1 'Advance current index pointer
    36. ixList2.Insert(k - 1, ix1) 'Add to the beginning of the list
    37. End While
    38. ixHigh = ixFrom 'Update lower boundary of ixList2
    39. End If
    40. End Sub


    Damit funktioniert mein handgestrickter Notepad! Herzlichen Dank an alle Ratgeber ... ihr habt mir wieder einmal sehr geholfen ...

    LG
    Peter

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