This is based on a version by Paul Noble.
The program associates a ComboBox with an instance of the ComboExpander class. This object's Initialize method saves a WithEvents reference to the ComboBox it is watching.
The ProcessKey method takes a key that the user pressed and uses subroutine NewComboText to figure out what the ComboBox's new text will be. It then uses SendMessage to find a match in the list. If the user pressed backspace or delete, it sends the CB_FINDSTRINGEXACT message so the match must be exact. If the user pressed another key, it sends the CB_FINDSTRING message so it finds the first match in the ComboBox. The routine then selects the matched item if it found one.
|
' The ComboBox we are expanding.
Private WithEvents m_ComboBox As ComboBox
' Prepare to expand the ComboBox.
Public Sub Initialize(ByVal cbo As ComboBox)
Set m_ComboBox = cbo
End Sub
' The user typed this key.
Private Sub ProcessKey(ByRef key_code As Integer)
Dim new_text As String
Dim row_found As Long
' See what the ComboBox's new text will be.
new_text = NewComboText(key_code)
' Find the first row that matches this string.
' See if the character is Backspace or Delete.
If key_code = vbKeyBack Or key_code = vbKeyDelete Then
' This is Backspace or Delete.
' Require an exact match.
row_found = SendMessageString(m_ComboBox.hwnd, _
CB_FINDSTRINGEXACT, 0, new_text)
Else
' This is not Backspace or Delete.
' Require any match.
row_found = SendMessageString(m_ComboBox.hwnd, _
CB_FINDSTRING, 0, new_text)
End If
' See if we found a row.
If row_found <> -1 Then
' We found a row. Select the row.
SendMessage m_ComboBox.hwnd, _
CB_SETCURSEL, row_found, ByVal 0&
' Select the text.
m_ComboBox.SelStart = Len(new_text)
m_ComboBox.SelLength = Len(m_ComboBox.Text) - _
Len(new_text)
' Cancel the input character so the ComboBox
' doesn't try to use it.
key_code = 0
End If
End Sub
' Determine the text's new value based on the
' currently selected text and the character pressed.
Private Function NewComboText(ByVal key_asc As Integer) As _
String
Dim txt_left As String
Dim txt_selected As String
Dim txt_right As String
Dim result As String
' Get the pieces of the ComboBox's text.
txt_left = Left$(m_ComboBox.Text, m_ComboBox.SelStart)
txt_selected = Mid$(m_ComboBox.Text, _
m_ComboBox.SelStart + 1, m_ComboBox.SelLength)
txt_right = Mid$(m_ComboBox.Text, m_ComboBox.SelStart + _
m_ComboBox.SelLength + 1)
' Take action based on the character.
Select Case key_asc
Case vbKeyBack ' Backspace.
' See if any text is selected.
If Len(txt_selected) > 0 Then
' Text is selected. Delete it.
result = txt_left & txt_right
Else
' No text is selected.
' Delete one character to the left.
If Len(txt_left) > 0 Then
txt_left = Left$(txt_left, _
Len(txt_left) - 1)
End If
result = txt_left & txt_right
End If
Case vbKeyDelete ' Delete.
' See if any text is selected.
If Len(txt_selected) > 0 Then
' Text is selected. Delete it.
result = txt_left & txt_right
Else
' No text is selected.
' Delete one character to the right.
txt_right = Mid$(txt_right, 2)
result = txt_left & txt_right
End If
Case Else ' Some other character.
' Replace the selected text with the character.
result = txt_left & Chr$(key_asc) & txt_right
End Select
NewComboText = result
End Function
' Watch for the Delete key.
Private Sub m_ComboBox_KeyDown(KeyCode As Integer, Shift As _
Integer)
' If it's a Delete key, process it.
If KeyCode = vbKeyDelete Then ProcessKey KeyCode
End Sub
' Process printing keys and Backspace.
Private Sub m_ComboBox_KeyPress(KeyAscii As Integer)
Const ASC_SPACE As Integer = 32
Const ASC_TILDE As Integer = 126
' Ignore the key if it is non-printing
' and not Backspace.
If (KeyAscii < ASC_SPACE Or KeyAscii > ASC_TILDE) _
And KeyAscii <> vbKeyBack _
Then
' The key is non-printing and not Backspace.
Exit Sub
End If
' Process the key.
ProcessKey KeyAscii
End Sub
|