Link to home
Start Free TrialLog in
Avatar of jake072
jake072Flag for Canada

asked on

Custom Combobox Control (Read Only, with AutoComplete) Autocomplete problems.

I have a Custom Control that I've made for an Application I'm developing that has Autocomplete and Readonly properties added.  My problem is that the autocomplete function seems to "error" when a user is typing too fast; i.e., the control is trying to find a match, but another keystroke is entered that ends up overriding the current "match find".  The problem is that if I have an entry for "Custom 1", and I rapidly type in c, u, s, it will select "Custom 1", and append the 'u' and 's' to the end; it's not properly selecting the text.

Here is the relevant sub's for autocompletion:

Private Sub CompletionCombo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyUp
        Dim sTypedText As String
        Dim iFoundIndex As Integer
        Dim oFoundItem As Object
        Dim sFoundText As String
        Dim sAppendText As String

        If Me.Items.Count > 0 Then
            'Allow select keys without Autocompleting
            Select Case e.KeyCode
                Case Keys.Back, Keys.Left, Keys.Right, Keys.Up, Keys.Delete, Keys.Down, Keys.ControlKey, Keys.ShiftKey, Keys.Alt, Keys.ShiftKey, Keys.MButton, Keys.LButton, Keys.RButton, Keys.Home, Keys.End
                    Exit Sub
            End Select

            'Get the Typed Text and Find it in the list
            If Me.Text.Length > 0 And Me.Text.Length <> Me.SelectedText.Length Then
                sTypedText = Me.Text
                iFoundIndex = Me.FindString(sTypedText)

                'If we found the Typed Text in the list then Autocomplete
                If iFoundIndex >= 0 Then
                    'Get the Item from the list (Return Type depends if Datasource was bound
                    ' or List Created)
                    oFoundItem = Me.Items(iFoundIndex)

                    'Use the ListControl.GetItemText to resolve the Name in case the Combo
                    ' was Data bound
                    sFoundText = Me.GetItemText(oFoundItem)

                    'Append then found text to the typed text to preserve case
                    sAppendText = sFoundText.Substring(sTypedText.Length)
                    Me.Text = sTypedText & sAppendText

                    'Select the Appended Text
                    Me.SelectionStart = sTypedText.Length
                    Me.SelectionLength = sAppendText.Length
                    'Me.SelectedIndex = iFoundIndex
                End If
            End If

        End If
    End Sub

If someone knows of a way to trap these events somehow, to prevent the selection errors, I would appreciate it.  I have been struggling to correct this, and I need to do so as I am almost ready to implement my application.  Please do not refer me to existing Autocomplete Combobox pages; I'm only interested in fixing the problem in my own control, mainly to learn how to do it, and because I have got everything else working properly for my control, and don't want to have to redo all of the programming already done for my comboboxes in my forms.

The problem (as I figure it) is that while the control is attempting to do an autocomplete, the user has already typed in another key, so that when the match is found, there is extra text, and the autocompletion (keyup) routine does not properly select the text.  I have tried many different ways to trap this, but I have been wholly unsuccessful.  I *THINK* that what needs to be done is that some kind of buffer needs to save keystrokes entered, and send them to the keyup event only AFTER the routine to match has successfully finished.  I have no idea if this is what needs to be done, but in any case, I'm quite desperate, and clueless on how to achieve this!

Thanks,

Jake
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

I have an AutoComplete function for a ComboBox, which duplicates the way that Internet Explorer does AutoComplete.  It is a very nice class, but it is very advanced code.  Would you still be interested?

Bob
Avatar of jake072

ASKER

Well, I guess it'd be worth a look.  I'd prefer to use my own combo, as my users prefer the text selection method that I am currently using, but perhaps I could pick up some pointers.

Thanks,

Jake
Avatar of amyhxu
amyhxu

Take a look at this auto complete combo, it should work for you:
http://www.freevbcode.com/ShowCode.asp?ID=7007
Avatar of jake072

ASKER

TheLearnedOne,

Thanks for the code...  However, I need a class that inherits from ComboBox though, or it will break A LOT of coding that I've already done!

amyhxu,

That code solved the problem of the keystrokes, however it added behaviour that was very undesired: for instance, it doesn't work right with control keys, it won't let you type something in, unless it's in the list (I need to be able to do this, as in my program if they type an entry in that doesn't exist, the system will ask them if they want to create a new entry)...

I tried my best to try and port the code from freevbcode to work as I need it, however, I can't get it to work at all; it won't append text or select text for some reason (at least my conversion won't)!

What I did was simply make my procedure use the KeyPress event...  This solved the keystroke problem, but now the combo won't append text to what I typed, nor will it do a selection for me.  Does anyone know why??
(I.e., I took my code above, replaced the KeyEventArgs parameter with KeyPressEventArgs, and changed the handler to handle KeyPress;  Additionally, I had to cast the KeyChar from the KeyPressEvent to an int using Val(e.KeyChar))

Here's to hoping that someone can still help!

Thanks for the time though,

Jake
You could put that code into a UserControl that inherits from ComboBox, and easily enable AutoComplete, but you have to like the effect first, before going down that road.

Bob
Avatar of jake072

ASKER

TheLearnedOne,

I've tried setting up a demo form, to test your code, and I must say that I quite like it!  However, I have NO CLUE about how to go about placing the code into a UserControl...  This seems like a very good solution, and I've cleared it with my boss.

There are a couple of problems though:

1) As mentioned, I'm a little lost on how to use this code in a user control (help?!?)
2) Could I pass a dataset with a column to use, in place of an arraylist?  (I'm thinking that it would be better to pass a dataset, rather than build an arraylist from data [baring in mind that I'm not nearly as experienced as you]; so if it's faster to simply create an arraylist from a dataset, then I'm sure that I can figure out how to do that...)

Thanks in advance,

Jake
Hi Jake,

The combobox from freevbcode doesn't allow adding items to the list (because you mentioned "readonly"?). But you can modify it to make it work as you expected. This may take some effort though. If you can get Bob's method to work, then definitely that's the way to go.
Avatar of jake072

ASKER

amyhxu,

Thanks very much for the responses, and time.

I'm hoping that Bob will be able to walk me through creating a control with his code (if he's not cursing my inabilities as a programmer :) )

Thanks again,

Jake
ASKER CERTIFIED SOLUTION
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of jake072

ASKER

Wow!

I had just finished programming my own control, and now you've done it for me (go figure!)

Anyways, I just thought I'd post what I'd come up with, but I think as you're the Guru, I shall use yours :)

My Code (Using the code you had given me, in a Class called AutoComplete):

Public Class ComboBoxFix
    Inherits Windows.Forms.ComboBox

    Sub New()
    End Sub

    Private Declare Function FindWindowEx _
        Lib "user32" Alias "FindWindowExA" _
        (ByVal hWnd1 As IntPtr, ByVal hWnd2 As IntPtr, _
        ByVal lpsz1 As String, ByVal lpsz2 As String) As IntPtr

    Public Function ComboBoxEditHandle(ByVal comboCurrent As Windows.Forms.ComboBox) As IntPtr

        ' Get the handle of the inner Edit control.
        Dim editHandle As IntPtr = FindWindowEx(comboCurrent.Handle, IntPtr.Zero, vbNullString, vbNullString)

        Return editHandle

    End Function 'ComboBoxEditHandle'

    Private Sub ComboBoxFix_DisplayMemberChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.DisplayMemberChanged
        If Not (MyBase.DataSource Is Nothing) Then
            Dim _arrayList As New ArrayList
            If TypeOf (MyBase.DataSource) Is DataTable Then
                Dim dt As DataTable = MyBase.DataSource
                Dim dr As DataRow
                For Each dr In dt.Rows
                    _arrayList.Add(dr(MyBase.DisplayMember.ToString()))
                Next
            End If
            Dim completer As New AutoComplete(ComboBoxEditHandle(Me), _arrayList)
        End If
    End Sub

    Private Sub ComboBoxFix_DataSourceChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.DataSourceChanged
        If Not (MyBase.DisplayMember = "") Then
            ComboBoxFix_DisplayMemberChanged(sender, e)
        End If
    End Sub
End Class

Thanks very much Bob, I really appreciate this!

Just out of curiosity, is my solution viable (I know that it works for me, but perhaps I'm doing something terribly wrong???)  Btw, I have the last sub (DataSourceChanged) just to account for the fact that if a user enters a new value from the combobox, the arraylist will be redone...  I programmed it this way so I wouldn't have to change any of my existing code (I'm lazy).

One last question (sorry) Is this component going to be adding things to the registry?

Thanks once again Bob, I really can't tell you how happy I am!

Jake
There a lot of ways to skin a cat :)

After looking at your code, it may have worked, I am just not 100% sure.

Bob