none
AutoComplete - Nicht wirklich zufrieden... RRS feed

  • Frage

  • Guten Abend,

    ich bin heute auf die Idee gekommen, dass man nach einem Musiktitel suchen kann. Beispiel: Jemand kennt zwar den Titel, aber nicht den Interpreten. Jetzt möchte er zum Beispiel nach "Bilder im Kopf", ist von Sido.

    Jetzt habe ich die AutoCompleteSource auf Custom gestellt, die Art auf SuggestAppend und die Autocomplete-Liste mit Interpreten und Titel gefüllt.

    Fazit: Keine Treffer, weil, Sido steht vorne. Tippt man jetzt "si" ein, kommt zwar der Treffer, aber wenn man nicht weiß, von wem der Titel ist, ist das blöd.

    Umgekehrt genauso doof. Ich könnte die Liste jetzt füllen mit zuerst dem Titel und dann dem Interpreten, aber was ist wenn jemand zwar den Namen eines Interpreten im Kopf hat, aber nicht den Titel?

    So wirklich bin ich mit dem AutoComplete nicht zufrieden. Oder mache ich was falsch?

    Frage: Ist es möglich das er auch Vorschläge macht wenn ich NUR "kopf" eingebe?

    Und jetzt wo ich das so schreibe, fällt mir auf, dass ich AutoComplete ja irgendwie total falsch verstanden habe :-D Hach herrlich, bin ich doof :-D

    Ich brauche ja was ganz anderes! Eine Suchfunktion die jedes mal sucht wenn sich am Text was ändert und Vorschläge macht. Okay, dass ist simpel. Aber wie mache ich das, dass unter der TextBox die Liste mit den Vorschlägen kommt?

    Gruß
    Andy

    Samstag, 30. November 2013 17:47

Antworten

  • Hallo, wenn alles in einem String "gespeichert" wird, dann brauchst du eine Art Volltextsuche.

    Die normale TextBox unterstützt das so aber nicht. Nun weiß ich nicht ob du auch so ein Popup haben willst, wie es die TextBox macht oder ob sich vielleicht eine Standardmäßige Liste unter der TextBox besser für dich eignet.

    Eine Liste fest unter der TextBox...
    Füge dazu einfach eine ListBox bzw. eine ListView in deine Form ein. Im TextChanged-Event aktualisierst du dann deine Liste. Nachfolgend mal ein Beispiel über LINQ:

    Public Class Form1
        Dim songs As New List(Of Song)
    
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            ListBox1.Items.Clear()
            ListBox1.Items.AddRange( _
                songs.Where(Function(x) x.Artist.Contains(TextBox1.Text) Or x.Title.Contains(TextBox1.Text)) _
                     .Select(Function(x) x.Artist & " - " & x.Title) _
                     .ToArray())
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 2", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 1"})
        End Sub
    End Class
    
    Class Song
        Public Property Title As String
        Public Property Artist As String
    End Class

    Mit der Where-Methode überprüfe ich ob ein Song meinen Such-Kriterien entspricht. Dabei muss das Suchwort im Titel oder im Interpreten vorkommen. Es gibt aber noch bessere Möglichkeiten [1].
    Mit Hilfe der Select-Methode erzeuge ich einen String, der dann ausgegeben wird.
    Die ToArray-Methode formt dann ein Array, welches von AddRange aufgenommen werden kann.

    Solltest du die ListBox nun doch nicht permanent anzeigen wollen, dann kannst du dir eine neue Form erstellen und die ListBox dort hinein packen. Beim Fokuswechsel auf die TextBox öffnest du die Form dann nicht Modal (Show(Me)). Die Position musst du dann natürlich noch manuell setzen und beim bewegen der Hauptform die Unterform mit verschieben. Beim wechsel des markierten Elements kannst du dann über ein Event der Hauptform sagen was in der TextBox erscheinen soll. Diese Lösung ist sicherlich nicht die einfachste. Für VB.NET konnte ich leider keine wirklich empfehlkenswerte Lösung finden. Für C# gibts einiges, beispielsweise das hier.

    [1] Besser wäre es wenn auch "Sido Bilder" einen Treffer landen würde. Dazu müsstest du das das Suchwort in die einzelnen Wörter aufspalten (.Split(" "c)) und dann jedes einzeln prüfen. Die Ergebnisse solltest du ab einer gewissen anzahl von getroffenen Wörtern auswählen.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert AndreasMahub Samstag, 30. November 2013 22:22
    Samstag, 30. November 2013 19:29
    Moderator
  • Verständlich ausgedrückt hast du es. Das Problem liegt nur, wie so oft, bei Windows. Die meisten .NET Windows Forms-Controls sind bloß so genannte Wrapper zu den nativen Windows-Controls. Diese kann man leider so gut wie nicht anpassen.

    Ich habe trotzdem mal etwas herum probiert und dabei bin ich noch auf eine andere Idee gekommen. Um die ListBox kommst du zwar auch hier nicht herum, aber du sparst dir schonmal das Fenster, was vieles vereinfacht.

    Als erstes habe ich die ListBox ausgeblendet (Visible = False). Wichtig bei dieser ist, das sie das vorderste Element ist. Außerdem muss die ListBox ein Teil der Form sein. Hier ist nun also mein Code:

    Public Class Form1
        Dim songs As New List(Of Song)
    
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            UpdateListBox()
        End Sub
    
        Private Sub UpdateListBox()
            If discardTextChanges Then Return 'Liste soll nicht aktualisiert werden'
    
            ListBox1.Items.Clear()
            If String.IsNullOrEmpty(TextBox1.Text) Then
                ListBox1.Items.AddRange(songs.Select(Function(x) x.Artist & " - " & x.Title).ToArray()) 'Alle Songs anzeigen'
            Else
                'ListBox1.Items.AddRange( _
                ''    songs.Where(Function(x) x.Artist.Contains(TextBox1.Text) Or x.Title.Contains(TextBox1.Text)) _
                ''         .Select(Function(x) x.Artist & " - " & x.Title) _
                ''         .ToArray())'
                ListBox1.Items.AddRange( _
                    songs.Where(Function(x) (x.Artist & " - " & x.Title).ToLower().Contains(TextBox1.Text.ToLower())) _
                         .Select(Function(x) x.Artist & " - " & x.Title) _
                         .ToArray())
                'Hier kannst du dein eigenes Suchmuster anhand der Liste implementieren
                ''Oben meine Lösung aus meiner ersten Antwort, die darunter dient nur zum testen
                ''Durch ToLower wird die Groß/Kleinschreibung ignoriert'
            End If
            ListBox1.Visible = True
            UpdateListBoxPositionAndSize() 'Größe und Position der ListBox aktualisieren'
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            'Testdaten hinzufügen'
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 2", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 3"})
            songs.Add(New Song() With {.Artist = "Interpret 4", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 4", .Title = "Titel 2"})
        End Sub
    
    #Region "ListBox ein-/ausblenden"
        Private Sub TextBox1_Enter(sender As Object, e As EventArgs) Handles TextBox1.Enter
            ' ListBox1.Visible = True''Übernimmt UpdateListBox'
            UpdateListBox() 'Liste entsprechend aktualisieren'
        End Sub
    
        Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave
            If Not ListBox1.Focused Then
                ListBox1.Visible = False 'ListBox wieder ausblenden'
            End If
        End Sub
    #End Region
    
        ''' <summary>
        ''' Aktuelisiert die Position der ListBox
        ''' </summary>
    '
        Sub UpdateListBoxPositionAndSize()
            Dim p = GetPosition(TextBox1) 'Position in relation zur Form ermitteln'
    
            ListBox1.Left = p.X
            ListBox1.Top = p.Y + TextBox1.Height
            ListBox1.Width = TextBox1.Width
        End Sub
    
        ''' <summary>
        ''' Ermittelt die Position eines Controls in Bezug zur Form
        ''' </summary>
    '
        Function GetPosition(c As Control) As Point
            If c.Parent Is Nothing Then
                Return New Point(0, 0) 'Koordinaten der Form weg lassen'
            Else
                Dim p = GetPosition(c.Parent)
                Return New Point(c.Left + p.X, c.Top + p.Y)
            End If
        End Function
    
        Private Sub TextBox1_SizeChanged(sender As Object, e As EventArgs) Handles TextBox1.SizeChanged, TextBox1.Move
            UpdateListBoxPositionAndSize()
        End Sub
    
        Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
            If e.KeyCode = Keys.Down Then
                'In die ListBox springen um eine Steuerung mit der Tastatur zu zulassen'
                ListBox1.Focus()
                If ListBox1.Items.Count > 0 Then ListBox1.SelectedIndex = 0 'Mehr als 0 Elemente in der ListBox'
            End If
        End Sub
    
        Dim discardTextChanges As Boolean = False 'Aktualisieren der ListBox durch TextChange verhindern'
    
        Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
            discardTextChanges = True
            If Not ListBox1.SelectedItem Is Nothing Then 'Es wurde ein Element ausgewählt'
                TextBox1.Text = ListBox1.SelectedItem.ToString()
            End If
            discardTextChanges = False
        End Sub
    
        Private Sub ListBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles ListBox1.KeyDown
            If e.KeyCode <> Keys.Down And e.KeyCode <> Keys.Up Then
                'Hoch und Runter wird zum navigieren in der ListBox benötigt.
                ''Beim normalen Input allerdings springen wir wieder in die TextBox'
    
                UpdateListBox() 'Liste aktualisieren'
                TextBox1.Focus() 'TextBox fokusieren'
                TextBox1.SelectionLength = 0 'Cursor setzen'
                TextBox1.SelectionStart = TextBox1.Text.Length
                'Die richtige Autovervollständigung würde hier alle tasten weiter leiten, die nicht Enter sind.
                ''Das brauchen wir hier aber nicht, da die ListBox alle möglichen Songs enthält.'
                If ListBox1.Visible = True And e.KeyCode = Keys.Enter Then
                    ListBox1.Visible = False 'ListBox wird ausgeblendet, wenn die Entertaste gedrückt wurde'
                End If
                e.Handled = True
            End If
        End Sub
    
        Private Sub ListBox1_Leave(sender As Object, e As EventArgs) Handles ListBox1.Leave
            If Not TextBox1.Focused Then ListBox1.Visible = False
        End Sub
    
        Private Sub ListBox1_Click(sender As Object, e As EventArgs) Handles ListBox1.Click
            If Not ListBox1.SelectedItem Is Nothing Then 'Wurde ein Element ausgewählt?'
                TextBox1.Focus() 'TextBox fokusieren'
                TextBox1.SelectionLength = 0 'Cursor setzen'
                TextBox1.SelectionStart = TextBox1.Text.Length
    
                ListBox1.Visible = False 'Ausblenden, weil Auswahl mit Maus erfolgte'
            End If
        End Sub
    
        Private Sub TextBox1_Click(sender As Object, e As EventArgs) Handles TextBox1.Click
            'Benutzer klickte erneut in die TextBox, also die Liste wieder anzeigen'
            ' ListBox1.Visible = True''Übernimmt UpdateListBox'
            UpdateListBox()
        End Sub
    End Class
    
    Class Song
        Public Property Title As String
        Public Property Artist As String
    End Class

    Das Projekt samt Quellcode findest du auch noch einmal unter: http://sdrv.ms/1jVV06
    (Rechtsklick > Herunter laden)

    In der Anwendung siehst ist wenige Pixel links der TextBox die Kante eines SplitContainers. Dadurch kannst du die Größe verändern. Die CheckBox und der Button haben keine Funktion. Durch einen klick auf diese wird aber der Fokus verschoben, wodurch die ListBox ausgeblendet wird.

    PS: Wenn du auf ein Grafisch hochwertiges Programm, Freie anpassbarkeit und eventuell sogar Animationen abziehlst, dann ist WPF besser geeignet als Windows Forms. Ein Umstieg wird aber eventuell aufwendig und die Einarbeitungszeit in WPF ist auch nicht die Geringste. (Wenn man die neuen Features auch wirklich nutzen möchte.)
    In der MSDN gibt es mehr dazu:
    http://msdn.microsoft.com/de-de/library/ms754130.aspx


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.



    Sonntag, 1. Dezember 2013 00:36
    Moderator

Alle Antworten

  • Hallo, wenn alles in einem String "gespeichert" wird, dann brauchst du eine Art Volltextsuche.

    Die normale TextBox unterstützt das so aber nicht. Nun weiß ich nicht ob du auch so ein Popup haben willst, wie es die TextBox macht oder ob sich vielleicht eine Standardmäßige Liste unter der TextBox besser für dich eignet.

    Eine Liste fest unter der TextBox...
    Füge dazu einfach eine ListBox bzw. eine ListView in deine Form ein. Im TextChanged-Event aktualisierst du dann deine Liste. Nachfolgend mal ein Beispiel über LINQ:

    Public Class Form1
        Dim songs As New List(Of Song)
    
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            ListBox1.Items.Clear()
            ListBox1.Items.AddRange( _
                songs.Where(Function(x) x.Artist.Contains(TextBox1.Text) Or x.Title.Contains(TextBox1.Text)) _
                     .Select(Function(x) x.Artist & " - " & x.Title) _
                     .ToArray())
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 2", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 1"})
        End Sub
    End Class
    
    Class Song
        Public Property Title As String
        Public Property Artist As String
    End Class

    Mit der Where-Methode überprüfe ich ob ein Song meinen Such-Kriterien entspricht. Dabei muss das Suchwort im Titel oder im Interpreten vorkommen. Es gibt aber noch bessere Möglichkeiten [1].
    Mit Hilfe der Select-Methode erzeuge ich einen String, der dann ausgegeben wird.
    Die ToArray-Methode formt dann ein Array, welches von AddRange aufgenommen werden kann.

    Solltest du die ListBox nun doch nicht permanent anzeigen wollen, dann kannst du dir eine neue Form erstellen und die ListBox dort hinein packen. Beim Fokuswechsel auf die TextBox öffnest du die Form dann nicht Modal (Show(Me)). Die Position musst du dann natürlich noch manuell setzen und beim bewegen der Hauptform die Unterform mit verschieben. Beim wechsel des markierten Elements kannst du dann über ein Event der Hauptform sagen was in der TextBox erscheinen soll. Diese Lösung ist sicherlich nicht die einfachste. Für VB.NET konnte ich leider keine wirklich empfehlkenswerte Lösung finden. Für C# gibts einiges, beispielsweise das hier.

    [1] Besser wäre es wenn auch "Sido Bilder" einen Treffer landen würde. Dazu müsstest du das das Suchwort in die einzelnen Wörter aufspalten (.Split(" "c)) und dann jedes einzeln prüfen. Die Ergebnisse solltest du ab einer gewissen anzahl von getroffenen Wörtern auswählen.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    • Als Antwort markiert AndreasMahub Samstag, 30. November 2013 22:22
    Samstag, 30. November 2013 19:29
    Moderator
  • Hi Tom,

    IM GRUNDE(!!!) schon mal geil was du dir da für mich ausgearbeitet hast.

    Es gibt nur ein Problem: Die TextBox ist in einem Splitpanel. Und dieses Panel ist gerade mal so hoch, wie die TextBox selbst. Das ist kein Platz für eine Listbox. Ich möchte auch keine separate Listbox (höherer Programmieraufwand, siehe unten). Also Listbox weg!

    Ich muss zum Zeitpunkt, wo dein Suchergebnis aufgelistet wird, direkt unter der TextBox die Listbox anzeigen lassen. Aber das ginge mit dem Panel nicht. Also fällt das weg.

    Die EIGENTLICHE (von .NET mitgelieferte) AutoComplete-Funktion von der TextBox kann direkt unter der TextBox (auch mit dem Panel) eine Liste anzeigen (bzw. zeichnen).

    Und genau so etwas möchte ich gerne, nur mit einem Unterschied: Ich möchte "die Macht haben" über die "Suchfunktion" haben.

    Kann man vielleicht (ist nur so eine Idee!!!) mit einer eigener Klasse, welche über Inherits, die AutoComplete überladen das "verbessern"???

    Man, wie soll ich das bloss erklären?! :-(

    Also, die TextBox bietet ja AutoComplete an. Sie macht selbst schon tolle Sachen. Die TextBox selbst macht ja schon tolle AutoComplete (inkl. Zeichnen) Funktionen, die möchte ich auch nicht wegreden. ICH bin aber mit den "Suchergebnissen" nicht zufrieden, und würde gerne eine eigene (nennen wir sie mal) "SuchEngine" anbieten und das Ergebnis wiederum an die AutoComplete-Funktion übergeben.

    Also ICH möchte mit MEINER Art suchen lassen. Nicht die AutoComplete-Funktion. Aber die AutoComplete darf gerne MEINE Ergebnisse anzeigen lassen.

    Hab ich das so halbwegs verständlich ausgedrückt? :-)

    Also ich finde das Thema spannend mit dem Überladen :-)

    Liebe Grüße
    Andy

    Samstag, 30. November 2013 22:10
  • Ich markiere mal trotzdem deine Antwort als "Beantwortet", weil du dir wirklich Mühe gegeben hast.

    Das es nicht "MEINE" Antwort ist, ähm... ja, liegt einfach daran, dass meine Fragestellung nicht so ganz genau war ;-)

    Gruß
    Andy

    Samstag, 30. November 2013 22:22
  • Verständlich ausgedrückt hast du es. Das Problem liegt nur, wie so oft, bei Windows. Die meisten .NET Windows Forms-Controls sind bloß so genannte Wrapper zu den nativen Windows-Controls. Diese kann man leider so gut wie nicht anpassen.

    Ich habe trotzdem mal etwas herum probiert und dabei bin ich noch auf eine andere Idee gekommen. Um die ListBox kommst du zwar auch hier nicht herum, aber du sparst dir schonmal das Fenster, was vieles vereinfacht.

    Als erstes habe ich die ListBox ausgeblendet (Visible = False). Wichtig bei dieser ist, das sie das vorderste Element ist. Außerdem muss die ListBox ein Teil der Form sein. Hier ist nun also mein Code:

    Public Class Form1
        Dim songs As New List(Of Song)
    
        Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
            UpdateListBox()
        End Sub
    
        Private Sub UpdateListBox()
            If discardTextChanges Then Return 'Liste soll nicht aktualisiert werden'
    
            ListBox1.Items.Clear()
            If String.IsNullOrEmpty(TextBox1.Text) Then
                ListBox1.Items.AddRange(songs.Select(Function(x) x.Artist & " - " & x.Title).ToArray()) 'Alle Songs anzeigen'
            Else
                'ListBox1.Items.AddRange( _
                ''    songs.Where(Function(x) x.Artist.Contains(TextBox1.Text) Or x.Title.Contains(TextBox1.Text)) _
                ''         .Select(Function(x) x.Artist & " - " & x.Title) _
                ''         .ToArray())'
                ListBox1.Items.AddRange( _
                    songs.Where(Function(x) (x.Artist & " - " & x.Title).ToLower().Contains(TextBox1.Text.ToLower())) _
                         .Select(Function(x) x.Artist & " - " & x.Title) _
                         .ToArray())
                'Hier kannst du dein eigenes Suchmuster anhand der Liste implementieren
                ''Oben meine Lösung aus meiner ersten Antwort, die darunter dient nur zum testen
                ''Durch ToLower wird die Groß/Kleinschreibung ignoriert'
            End If
            ListBox1.Visible = True
            UpdateListBoxPositionAndSize() 'Größe und Position der ListBox aktualisieren'
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            'Testdaten hinzufügen'
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 1", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 2", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 2"})
            songs.Add(New Song() With {.Artist = "Interpret 3", .Title = "Titel 3"})
            songs.Add(New Song() With {.Artist = "Interpret 4", .Title = "Titel 1"})
            songs.Add(New Song() With {.Artist = "Interpret 4", .Title = "Titel 2"})
        End Sub
    
    #Region "ListBox ein-/ausblenden"
        Private Sub TextBox1_Enter(sender As Object, e As EventArgs) Handles TextBox1.Enter
            ' ListBox1.Visible = True''Übernimmt UpdateListBox'
            UpdateListBox() 'Liste entsprechend aktualisieren'
        End Sub
    
        Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave
            If Not ListBox1.Focused Then
                ListBox1.Visible = False 'ListBox wieder ausblenden'
            End If
        End Sub
    #End Region
    
        ''' <summary>
        ''' Aktuelisiert die Position der ListBox
        ''' </summary>
    '
        Sub UpdateListBoxPositionAndSize()
            Dim p = GetPosition(TextBox1) 'Position in relation zur Form ermitteln'
    
            ListBox1.Left = p.X
            ListBox1.Top = p.Y + TextBox1.Height
            ListBox1.Width = TextBox1.Width
        End Sub
    
        ''' <summary>
        ''' Ermittelt die Position eines Controls in Bezug zur Form
        ''' </summary>
    '
        Function GetPosition(c As Control) As Point
            If c.Parent Is Nothing Then
                Return New Point(0, 0) 'Koordinaten der Form weg lassen'
            Else
                Dim p = GetPosition(c.Parent)
                Return New Point(c.Left + p.X, c.Top + p.Y)
            End If
        End Function
    
        Private Sub TextBox1_SizeChanged(sender As Object, e As EventArgs) Handles TextBox1.SizeChanged, TextBox1.Move
            UpdateListBoxPositionAndSize()
        End Sub
    
        Private Sub TextBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyDown
            If e.KeyCode = Keys.Down Then
                'In die ListBox springen um eine Steuerung mit der Tastatur zu zulassen'
                ListBox1.Focus()
                If ListBox1.Items.Count > 0 Then ListBox1.SelectedIndex = 0 'Mehr als 0 Elemente in der ListBox'
            End If
        End Sub
    
        Dim discardTextChanges As Boolean = False 'Aktualisieren der ListBox durch TextChange verhindern'
    
        Private Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox1.SelectedIndexChanged
            discardTextChanges = True
            If Not ListBox1.SelectedItem Is Nothing Then 'Es wurde ein Element ausgewählt'
                TextBox1.Text = ListBox1.SelectedItem.ToString()
            End If
            discardTextChanges = False
        End Sub
    
        Private Sub ListBox1_KeyDown(sender As Object, e As KeyEventArgs) Handles ListBox1.KeyDown
            If e.KeyCode <> Keys.Down And e.KeyCode <> Keys.Up Then
                'Hoch und Runter wird zum navigieren in der ListBox benötigt.
                ''Beim normalen Input allerdings springen wir wieder in die TextBox'
    
                UpdateListBox() 'Liste aktualisieren'
                TextBox1.Focus() 'TextBox fokusieren'
                TextBox1.SelectionLength = 0 'Cursor setzen'
                TextBox1.SelectionStart = TextBox1.Text.Length
                'Die richtige Autovervollständigung würde hier alle tasten weiter leiten, die nicht Enter sind.
                ''Das brauchen wir hier aber nicht, da die ListBox alle möglichen Songs enthält.'
                If ListBox1.Visible = True And e.KeyCode = Keys.Enter Then
                    ListBox1.Visible = False 'ListBox wird ausgeblendet, wenn die Entertaste gedrückt wurde'
                End If
                e.Handled = True
            End If
        End Sub
    
        Private Sub ListBox1_Leave(sender As Object, e As EventArgs) Handles ListBox1.Leave
            If Not TextBox1.Focused Then ListBox1.Visible = False
        End Sub
    
        Private Sub ListBox1_Click(sender As Object, e As EventArgs) Handles ListBox1.Click
            If Not ListBox1.SelectedItem Is Nothing Then 'Wurde ein Element ausgewählt?'
                TextBox1.Focus() 'TextBox fokusieren'
                TextBox1.SelectionLength = 0 'Cursor setzen'
                TextBox1.SelectionStart = TextBox1.Text.Length
    
                ListBox1.Visible = False 'Ausblenden, weil Auswahl mit Maus erfolgte'
            End If
        End Sub
    
        Private Sub TextBox1_Click(sender As Object, e As EventArgs) Handles TextBox1.Click
            'Benutzer klickte erneut in die TextBox, also die Liste wieder anzeigen'
            ' ListBox1.Visible = True''Übernimmt UpdateListBox'
            UpdateListBox()
        End Sub
    End Class
    
    Class Song
        Public Property Title As String
        Public Property Artist As String
    End Class

    Das Projekt samt Quellcode findest du auch noch einmal unter: http://sdrv.ms/1jVV06
    (Rechtsklick > Herunter laden)

    In der Anwendung siehst ist wenige Pixel links der TextBox die Kante eines SplitContainers. Dadurch kannst du die Größe verändern. Die CheckBox und der Button haben keine Funktion. Durch einen klick auf diese wird aber der Fokus verschoben, wodurch die ListBox ausgeblendet wird.

    PS: Wenn du auf ein Grafisch hochwertiges Programm, Freie anpassbarkeit und eventuell sogar Animationen abziehlst, dann ist WPF besser geeignet als Windows Forms. Ein Umstieg wird aber eventuell aufwendig und die Einarbeitungszeit in WPF ist auch nicht die Geringste. (Wenn man die neuen Features auch wirklich nutzen möchte.)
    In der MSDN gibt es mehr dazu:
    http://msdn.microsoft.com/de-de/library/ms754130.aspx


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.



    Sonntag, 1. Dezember 2013 00:36
    Moderator
  • Hi Tom,

    Sorry, bin leider etwas eingepennt :-D

    PERFEKT!!!!!!!!!!!!!!!!!!! DANKE!!!!!!!!!!!!!!!

    Allerdings legt sich die Listbox nicht "über" das Panel, sondern ist abgeschnitten. Ich habe einen Splicontainer, Ausrichtung ist Horizontal. Panel1 (oben) ist so hoch wie die TextBox. Die Listbox ist so gut wie nicht zu sehen, weil, das soll ja auch so sein. Aber man kann doch bestimmt die Listbox "zwingen" trotzdem als oberstes Control dargestellt zu werden.

    Ansonsten: Danke, genau so wollte ich es! :-)

    Liebe Grüße
    Andy

    Sonntag, 1. Dezember 2013 10:01
  • Ach ja, Nachtrag bzgl. WPF:

    WPF ist wirklich eine ganz tolle Sache, ich habe mir auch dafür bereits 2 Bücher gekauft. Aber wenn ich ehrlich sein soll, ich glaube mit 40 Jahren jetzt noch so krass "umzudenken" fällt mir ehrlich gesagt echt schwer :-(

    Würde ich sehr gerne können, aber das ist schon ein extremer Umstieg von Windows Forms auf WPF.

    Gruß
    Andy

    Sonntag, 1. Dezember 2013 10:03
  • Also ich habe die ListBox nach ganz oben bekommen indem ich sie als letztes in die Form eingefügt habe.

    Gucke mal in die Form.Designer.vb. Dort fiondest du eine Methode InitialisizeComponent, diese ist für das Aufbauen der GUI verantwortlich. Kurz vor dem Ede werden die Controls hinzugefügt. In meinem Beispiel sieht das so aus:

            Me.Controls.Add(Me.ListBox1)
            Me.Controls.Add(Me.CheckBox1)
            Me.Controls.Add(Me.Button1)
            Me.Controls.Add(Me.SplitContainer1)

    Die erste Zeile dieser Add-Befehle muss die ListBox hinzufügen. Dadurch legt diese sich über alle anderen Controls.

    PS: Eventuell lohnt es sich, die ListBox auch breiter zu gestalten als die TextBox, das musst du aber anhand des Designs entscheiden.


    Zu WPF, wenn du mal etwas Zeit übrig hast, solltest du es probieren. Vielleicht fällt es dir auch leichter, als du denkst ;)


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 1. Dezember 2013 11:18
    Moderator
  • Habe (glaube ich) eine Lösung gefunden ;-)

    ListBox1.Parent = SplitContainer1.Panel2

    So wird dann die Listbox im unteren Panel dargestellt ;-)

    Gruß
    Andy

    Sonntag, 1. Dezember 2013 11:18
  • Solange die ListBox nicht größer ist als das Panel funktioniert das natürlich auch.

    Wenn meine Lösung nicht richtig funktioniert, dann kann ich leider aus der Ferne auch nichts weiter dazu sagen. Entweder benutzt du ein Control das sich anders als erwartet verhält oder irgendwo in der Reihenfolge ist doch noch ein Fehler.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Sonntag, 1. Dezember 2013 14:22
    Moderator
  • Solange die ListBox nicht größer ist als das Panel funktioniert das natürlich auch.

    Wenn meine Lösung nicht richtig funktioniert, dann kann ich leider aus der Ferne auch nichts weiter dazu sagen. Entweder benutzt du ein Control das sich anders als erwartet verhält oder irgendwo in der Reihenfolge ist doch noch ein Fehler.


    Koopakiller [kuːpakɪllɐ] (Tom Lambert)
    Webseite | Code Beispiele | Facebook | Twitter | Snippets   C# ↔ VB.NET Konverter
    Markiert bitte beantwortende Posts als Antwort und bewertet Beiträge. Danke.

    Nein nein nein, dein Code funktioniert einwandfrei ;-)

    Aber das Panel ist nur so hoch, wie die TextBox selbst und dadurch wird die Listbox drunter dann nicht mehr richtig dargestellt ;-) Daher habe ich es lediglich nur so abändern müssen, dass die Listbox im 2. (unteren) Panel ganz oben angezeigt wird ;-)

    Danke nochmal ;-)

    Gruß
    Andy

    Sonntag, 1. Dezember 2013 14:32