AutoWordComplete on a DropDown as you type in a wsheet cell

Jaafar Tribak

Well-known Member
Joined
Dec 5, 2002
Messages
9,621
Office Version
  1. 2016
Platform
  1. Windows
Hi all,

Here is an example: http://www.savefile.com/files/1228450

I have been working on a little project that requires making entries from an unsorted list into an input cell . The list extends over hundreds of rows and the syntax of the items on the list is irregular and difficult to remember.

Neither the XL native Data validation or the Pick From Drop-down List on the Cell menu are good for very long lists.

So i thought that if i got a drop down list to pop up with the corresponding matches dynamically as i type in the first letters (similar ot the AutoComplete feature in IE) , i would save plenty of time in the data entry process and avoid making syntax mistakes.

The AutoWordCompleter Class i have come up with works fairly well except for one major problem I have come up against and that is when typing too fast, the Class goes out of scope abruptly and the programm stops working without giving the Class a chance to cleanup :( . I couldn't get the Terminate event handler to do the cleanup either !

I am currently using a Loop to watch for key strokes so I have tried using a mouse hook instead and even a timer to see if they can avoid this problem but with no luck so far.

I am open to any suggestions in order to get around this - not being able to type too fast - problem .

Here is the Class code : (cAutoWordCompleter)

Code:
Option Explicit

Private WithEvents ListBoxEvents As MSForms.ListBox
Private WithEvents wsEvents As Worksheet

Private P_InputCell As Range
Private P_SearchList As Range

'_________________________________________________________
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long

Private Const WM_LBUTTONDOWN = &H201
'__________________________________________________________

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function SetFocus Lib "user32" (ByVal hWnd As Long) As Long

Private Declare Function GetFocus Lib "user32" () As Long

Private Declare Function WindowFromPoint Lib "user32" _
(ByVal xPoint As Long, ByVal yPoint As Long) As Long

Private lHwnd As Long
'________________________________________________________

Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" _
(ByVal lpDriverName As String, ByVal lpDeviceName As String, _
ByVal lpOutput As String, lpInitData As Long) As Long

Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" _
(ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long

Private Declare Function CreateFontIndirect Lib "gdi32.dll" Alias _
"CreateFontIndirectA" (lpLogFont As LOGFONT) As Long

Private Declare Function SelectObject Lib "gdi32.dll" _
(ByVal hDC As Long, ByVal hObject As Long) As Long

Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long

Private Declare Function GetTextExtentPoint32 Lib "gdi32.dll" Alias _
"GetTextExtentPoint32A" (ByVal hDC As Long, ByVal lpsz As String, _
ByVal cbString As Long, lpSize As POINTAPI) As Long

Private Declare Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long

Private Type LOGFONT
    lfHeight As Long
    lfWidth As Long
    lfEscapement As Long
    lfOrientation As Long
    lfWeight As Long
    lfItalic As Byte
    lfUnderline As Byte
    lfStrikeOut As Byte
    lfCharSet As Byte
    lfOutPrecision As Byte
    lfClipPrecision As Byte
    lfQuality As Byte
    lfPitchAndFamily As Byte
    lfFaceName As String * 32
End Type


Private Declare Function GetDC Lib "user32.dll" (ByVal hWnd As Long) As Long

Private Declare Function GetDeviceCaps Lib "gdi32.dll" _
(ByVal hDC As Long, ByVal nIndex As Long) As Long

Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hDC As Long) As Long

Private Declare Function ReleaseDC Lib "user32" _
(ByVal hWnd As Long, ByVal hDC As Long) As Long

Private Const LOGPIXELSX As Long = 88
Private Const LOGPIXELSY As Long = 90
Private Const PointsPerInch = 72
Private lnghDC As Long

'_________________________________________________________

Private Declare Function CreateCaret Lib "user32" _
(ByVal hWnd As Long, ByVal hBitmap As Long, _
ByVal nWidth As Long, ByVal nHeight As Long) As Long

Private Declare Function ShowCaret Lib "user32" (ByVal hWnd As Long) As Long

Private Declare Function SetCaretPos Lib "user32" _
(ByVal x As Long, ByVal Y As Long) As Long

Private Declare Function HideCaret Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Function DestroyCaret Lib "user32.dll" () As Long

Private Declare Function ScreenToClient Lib "user32" ( _
ByVal hWnd As Long, _
lpPoint As POINTAPI) As Long

Private Declare Function SetCursorPos Lib "user32.dll" ( _
ByVal x As Long, ByVal Y As Long) As Long

'_______________________________________________________

Private Declare Function WaitMessage Lib "user32" () As Long

Private Declare Function TranslateMessage Lib "user32" _
(ByRef lpMsg As MSG) As Long

Private Declare Function PeekMessage Lib "user32" _
Alias "PeekMessageA" _
(ByRef lpMsg As MSG, ByVal hWnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long) As Long

Private Const WM_KEYDOWN = &H100
Private Const WM_CHAR As Long = &H102
Private Const PM_REMOVE As Long = &H1
Private Const PM_NOYIELD As Long = &H2
Private msgMessage As MSG

Private Type POINTAPI
    x As Long
    Y As Long
End Type

Private Type MSG
    hWnd As Long
    Message As Long
    wParam As Long
    lParam As Long
    time As Long
    pt As POINTAPI
End Type

'_______________________________________________________


Private Declare Function IsCharAlphaNumeric Lib "user32" _
Alias "IsCharAlphaNumericA" (ByVal cChar As Byte) As Long

Private Const VK_UP = &H26
Private Const VK_DOWN = &H28
Private Const VK_RIGHT = &H27
Private Const VK_LEFT = &H25
Private Const VK_SHIFT = &H10
Private Const VK_BACK = &H8
Private Const VK_ESCAPE = &H1B
Private Const VK_DELETE = &H2E
Private Const VK_SPACE = &H20
Private Const VK_ENTER = &HD

Private oHiddenWs As Worksheet
Private oSearchListCopyRange As Range
Private oCriteriaRange As Range
Private oCopyToRange As Range
Private oListBox


Private lPrevCellColor As Long


Private bCancelLoop As Boolean
Private bInputCellToBeRefreshed As Boolean
Private bInCellEdit As Boolean
Private bClassTerminatedProperly As Boolean
Private bEnterKeyPressed As Boolean
Private bAutoCompleteOn As Boolean
Private bListBoxDblClicked As Boolean



Private Sub ProcessKeyStrokes()
    
    'adjust the value of this const to suit the
    'machine screen resolution
    Const OffsetPixels = 4

    Dim Message As MSG
    Dim sTempText As String
    Dim sOddStrings As String
    Dim lResult As Long
    Dim bFoundMatch As Boolean
    Dim iMatchPos
    
    'prevent user from interrupting the loop
    Application.EnableCancelKey = xlDisabled
    
    'store the app hwnd in a module level var
    lHwnd = FindWindow("XLMAIN", Application.Caption)
    
    'hook our inputcell worksheet events
    Set wsEvents = P_InputCell.Parent
    
    'dont clear the inputcell if value is entered from the lbx
    If Not bListBoxDblClicked Then P_InputCell.ClearContents
    RefreshInputCell P_InputCell
    
    'color our inputcell so it easyly recognised
    If lPrevCellColor = 0 Then
        Call FormatInputCell
    End If
    
    'add a listbox to show the filtered list
    On Error Resume Next
    If oListBox Is Nothing Then
        Call CreateListBox
    End If
    On Error GoTo 0
    
    'setup advanced filter ranges in
    'a newly added hidden sheet
    If Not bAutoCompleteOn Then
        SetUpSearchAndFilteredLists
        bAutoCompleteOn = True
    End If
    
    'create a custom blinkikg cursor
    CreateCaret lHwnd, 0, 1, 15       '
    
    'store InCell edit property for later recovery
    bInCellEdit = Application.EditDirectlyInCell
    
    'in-cell editing can cause probs
    'so let's disable it temporarly
    Application.EditDirectlyInCell = False
    
    'start watching for user keystrokes
    Do While Not bCancelLoop
    
    'check for keystrokes only if we are typing on our inputcell
    If ActiveCell.Address = P_InputCell.Address Then
    
            'wait for a message
            WaitMessage
    
            'check if it's a WM_KEYDOWN message
            If PeekMessage(Message, lHwnd, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) Then

                'translate the virtual key code to a character
                lResult = TranslateMessage(Message)
                
                'Check which char is in the mssg queu
                lResult = PeekMessage(Message, lHwnd, WM_CHAR, _
                WM_CHAR, PM_REMOVE + PM_NOYIELD)
                
                'refresh the inputcell before processing the char messages
                If bInputCellToBeRefreshed Then bInputCellToBeRefreshed = False: _
                P_InputCell.ClearContents: HideListBox
                
                                
                ShowCaret lHwnd

            'let's process the char messages here
            Select Case Message.wParam
            
                Case Is = VK_BACK
                
                    'clear everything when inputcell is empty
                    If Len(P_InputCell) <= 1 Then
                        P_InputCell.ClearContents
                        oListBox.Visible = False
                        AdjustCaretPos P_InputCell, OffsetPixels
                        GoTo ResumeLoop
                    End If
                    
                    'otherwise, update the input cell value
                    P_InputCell = Mid(P_InputCell, 1, Len(P_InputCell) - 1)
            
                    'move the cursor to the new last char in the inputcell
                    AdjustCaretPos P_InputCell, OffsetPixels
                    
                    'refresh advanced filter ranges in the hidden ws
                    On Error Resume Next
                    oCriteriaRange.ClearContents
                    oCopyToRange.ClearContents
                    On Error GoTo 0
                    Set oCriteriaRange = oHiddenWs.Range("F1:F2")
                    oCriteriaRange(1) = oSearchListCopyRange(1).Value
                    oCriteriaRange(2) = P_InputCell & "*"
                    
                    'do the filtering now
                    Set oCopyToRange = _
                    oSearchListCopyRange(oSearchListCopyRange.Rows.Count).Offset(4)
                    oSearchListCopyRange.AdvancedFilter Action:=xlFilterCopy, _
                    CriteriaRange:=oCriteriaRange, CopyToRange:=oCopyToRange, Unique:=True
                    
                    'if no match found, clear everything and wait for the next keystroke
                    If Len(oCopyToRange(2)) = 0 Then
                        oListBox.Visible = False
                        AdjustCaretPos P_InputCell, OffsetPixels
                        GoTo ResumeLoop
                    Else
                        oListBox.Visible = True
                        AdjustCaretPos P_InputCell, OffsetPixels
                    End If
                    
                    'otherwise, populate the listbox with the filtered list
                    Set oCopyToRange = Range(oCopyToRange.CurrentRegion(2), oCopyToRange.CurrentRegion.End(xlDown))
                    oListBox.ListFillRange = oHiddenWs.Name & "!" & oCopyToRange.Address
                
                Case Is = VK_ESCAPE
                    'clear everything if Esc/Del are pressed
                    HideListBox
                    P_InputCell.ClearContents
                    
                Case Is = VK_DELETE
                    HideListBox
                    P_InputCell.ClearContents
            
                'deselect the inputcell and hide
                'the listbox if the keys Left/Right are hit
                Case Is = VK_RIGHT
                    P_InputCell.Offset(, 1).Select
                    ClearInvalidEntries
                    HideListBox
                
                Case Is = VK_LEFT
                    P_InputCell.Offset(, -1).Select
                    ClearInvalidEntries
                    HideListBox
            
                'if the listbox is displayed,activate it
                'and use the Up/Down keys to navigate it
                Case Is = VK_UP
                    On Error Resume Next
                    If oListBox.Visible And _
                        GetFocus = GetListBoxHwnd Then
                        Call KeyUpProc
                    Else
                        P_InputCell.Offset(-1).Select
                        ClearInvalidEntries
                    End If
                    
                Case Is = VK_DOWN
                    On Error Resume Next
                    If oListBox.Visible Then
                        Call ActivateListBox
                        Call KeyDownProc
                    Else
                    P_InputCell.Offset(1).Select
                        ClearInvalidEntries
                    End If
            
                'if the listbox is activated update
                'the inputcell with the selected lisbox item
                Case Is = VK_ENTER
                    If Not oListBox.Visible Then ClearInvalidEntries
                    If GetFocus = GetListBoxHwnd Then
                        P_InputCell = oListBox.Object.Value
                        HideListBox
                        HideCaret lHwnd
                        RefreshInputCell P_InputCell
                    Else
                        SetFocus lHwnd
                        HideListBox
                    End If
                    P_InputCell.Offset(1).Select
           
            End Select
        
            'store a bunch of some usual non alphanumeric chars
            sOddStrings = ",;:!<>-_@=+*-/?}{\^][#~`"
            
            'if the key hit is alphanumeric
            If IsCharAlphaNumeric(Message.wParam) Or Message.wParam = VK_SPACE _
            Or InStr(1, sOddStrings, Chr(Message.wParam)) > 0 Then
            
                'update inputcell value
                P_InputCell = P_InputCell & Chr(Message.wParam)
            
                'move cursor to the last char
                AdjustCaretPos P_InputCell, OffsetPixels
                
                'refresh our advanced filter ranges
                On Error Resume Next
                oCriteriaRange.ClearContents
                oCopyToRange.ClearContents
                On Error GoTo 0
            
                'update the advanced filter ranges
                Set oCriteriaRange = oHiddenWs.Range("F1:F2")
                oCriteriaRange(1) = oSearchListCopyRange(1).Value
                oCriteriaRange(2) = P_InputCell & "*"
                Set oCopyToRange = oSearchListCopyRange _
                (oSearchListCopyRange.Rows.Count).Offset(4)
                
                'do the filtering now
                oSearchListCopyRange.AdvancedFilter Action:=xlFilterCopy, _
                CriteriaRange:=oCriteriaRange, CopyToRange:=oCopyToRange, Unique:=True
            
                'if no matches found hide lisbox and wait for next char
                If Len(oCopyToRange(2)) = 0 Then Set oCopyToRange = Nothing: _
                oListBox.Visible = False: GoTo ResumeLoop
                Set oCopyToRange = Range(oCopyToRange.CurrentRegion(2), _
                oCopyToRange.CurrentRegion.End(xlDown))
                
                'otherwise, populate the listbox with the filered list
                oListBox.ListFillRange = oHiddenWs.Name & "!" & oCopyToRange.Address
                'show the updated listbox
                oListBox.Visible = True
                
            End If
    
        End If

    End If
    
ResumeLoop:

    'let the os process other mssgs
    DoEvents
    
    Loop

End Sub


Private Sub StopKeyProcessing()

    On Error Resume Next
    
    'reset flags
    bCancelLoop = True
    bAutoCompleteOn = False
    bClassTerminatedProperly = True
    
    'restore initial incell editing value
    Application.EditDirectlyInCell = bInCellEdit
    
    'delete the hidden sheet
    Application.DisplayAlerts = False
    oHiddenWs.Delete
    Application.DisplayAlerts = True
    Set oHiddenWs = Nothing
    
    'delete the listbox
    oListBox.ShapeRange.Delete
    Set oListBox = Nothing
    
    'clear inputcell
    RefreshCaret P_InputCell
    DestroyCaret
    P_InputCell.Interior.ColorIndex = lPrevCellColor
    P_InputCell.ClearContents
    
    On Error GoTo 0



End Sub



'************************Supporting Routines *****************

Private Function GetTextSize(text As String, font As font) As POINTAPI

    Dim tempDC As Long
    Dim tempBMP As Long
    Dim f As Long
    Dim lf As LOGFONT
    Dim textSize As POINTAPI

    ' Create a device context and a bitmap that can be used to store a
    ' temporary font object
    tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)
    tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)

    ' Assign the bitmap to the device context
    DeleteObject SelectObject(tempDC, tempBMP)

    ' Set up the LOGFONT structure and create the font
    lf.lfFaceName = font.Name & Chr$(0)
    lf.lfHeight = -MulDiv(font.SIZE, GetDeviceCaps(GetDC(0), LOGPIXELSY), 72)
    lf.lfItalic = font.Italic
    lf.lfStrikeOut = font.Strikethrough
    'lf.lfUnderline = font.Underline
    If font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400
    f = CreateFontIndirect(lf)

    ' Assign the font to the device context
    DeleteObject SelectObject(tempDC, f)

    ' Measure the text, and return it into the textSize SIZE structure
    GetTextExtentPoint32 tempDC, text, Len(text), textSize

    ' Clean up
    DeleteObject f
    DeleteObject tempBMP
    DeleteDC tempDC

    ' Return the measurements
    GetTextSize = textSize

End Function


'function needed to set the horz caret position
Private Function PointsToClientPixelsX(x As Long) As Long

    Dim tScreen As POINTAPI
    Dim PointsToScrPixelsX As Long
    
    lnghDC = GetDC(0)
    PointsToScrPixelsX = ActiveWindow.PointsToScreenPixelsX(x * _
    (GetDeviceCaps(lnghDC, LOGPIXELSX) / PointsPerInch * ActiveWindow.Zoom / 100))
    tScreen.x = PointsToScrPixelsX
    ScreenToClient lHwnd, tScreen
    ReleaseDC 0, lnghDC
    PointsToClientPixelsX = tScreen.x

End Function

'function needed to set the ver caret position
Private Function PointsToClientPixelsY(Y As Long) As Long

    Dim tScreen As POINTAPI
    Dim PointsToScrPixelsY As Long
    
    lnghDC = GetDC(0)
    PointsToScrPixelsY = ActiveWindow.PointsToScreenPixelsY(Y * _
    (GetDeviceCaps(lnghDC, LOGPIXELSY) / PointsPerInch * ActiveWindow.Zoom / 100))
    tScreen.Y = PointsToScrPixelsY
    ScreenToClient lHwnd, tScreen
    ReleaseDC 0, lnghDC
    PointsToClientPixelsY = tScreen.Y

End Function



Private Sub AdjustCaretPos(r As Range, OffsetPixels As Long)

    Dim tSize As POINTAPI
    Dim tScreen As POINTAPI
    Dim sTempText As String
    
    tSize = GetTextSize(r.text, r.font)
    ScreenToClient lHwnd, tSize
    tScreen.x = PointsToClientPixelsX(r.Left)
    tScreen.Y = PointsToClientPixelsY(r.Offset(1).Top)
    SetCaretPos tScreen.x + tSize.x + OffsetPixels, tScreen.Y - 15 - 3
    ShowCaret (lHwnd)
    Call RefreshCaret(r)

End Sub

'needed to refresh the caret
Private Sub RefreshCaret(r As Range)

    Dim sTempText
    sTempText = (r)
    r.ClearContents
    r = sTempText

End Sub


Private Sub RefreshInputCell(r As Range)

    RefreshCaret r
    HideCaret lHwnd

End Sub

Private Sub HideListBox()

    oListBox.Visible = False
    HideCaret lHwnd

End Sub

Private Sub ClearInvalidEntries()

    Dim iMatchPos As Integer
    
    On Error Resume Next
    iMatchPos = WorksheetFunction.Match(P_InputCell, P_SearchList, 0)
    If Err <> 0 And Len(P_InputCell) <> 0 Then
        MsgBox "Invalid Entry !       ", vbCritical
        P_InputCell.ClearContents
    End If

End Sub



Private Sub SetUpSearchAndFilteredLists()

    'arbitrary row # where the sorted list
    'in the hidden worksheet begins
    Const lTargetRow As Long = 5
    
    On Error Resume Next
    Application.ScreenUpdating = False
    
    'add new hidden sheet
    Set oHiddenWs = Sheets.Add
    
    With oHiddenWs
        .Visible = xlSheetHidden
        .Name = "AutoCompleterWS"
        Set oSearchListCopyRange = .Cells(lTargetRow, 1)
    End With
    
    'copy the search list onto the hidden sheet
    P_SearchList.Offset(-1).Resize(P_SearchList.Rows.Count + 1).Copy _
    oSearchListCopyRange
    
    'sort the copied list
    oSearchListCopyRange.Sort oSearchListCopyRange(1) _
    , Order1:=xlAscending, Header:=xlGuess
    
    'adjust the copied list size to include all the sorted cells
    Set oSearchListCopyRange = _
    oSearchListCopyRange.Resize(P_SearchList.Rows.Count, 1)
    Application.ScreenUpdating = True

End Sub

Private Function GetListBoxHwnd() As Long

    With GetRangeCenterPointInPixels(P_InputCell.Offset(1))
        GetListBoxHwnd = WindowFromPoint(.x, .Y)
    End With

End Function

'function needed to get the lisbox hwnd
Private Function GetRangeCenterPointInPixels(rng As Range) As POINTAPI

    Dim CenterX, CenterY As Double
    
    lnghDC = GetDC(0)
    CenterX = rng.Left + (rng.Width / 2)
    CenterY = rng.Top + (rng.Height / 2)
    
    With GetRangeCenterPointInPixels
        .x = ActiveWindow.PointsToScreenPixelsX((CenterX) * _
        (GetDeviceCaps _
        (lnghDC, LOGPIXELSX) / PointsPerInch * ActiveWindow.Zoom / 100))
        .Y = ActiveWindow.PointsToScreenPixelsY((CenterY) * _
        (GetDeviceCaps _
        (lnghDC, LOGPIXELSY) / PointsPerInch * ActiveWindow.Zoom / 100))
    End With
    
    'cleanup
    ReleaseDC 0, lnghDC

End Function


'could find no other way to activate the listbox programatically
Private Sub ActivateListBox()

    SendMessage GetListBoxHwnd, WM_LBUTTONDOWN, 0, 0

End Sub


Private Sub CreateListBox()

    Application.ScreenUpdating = False
    Set oListBox = ActiveSheet.OLEObjects.Add(ClassType:="Forms.ListBox.1")
    
    'set listbox properties
    With oListBox
        .Name = "AutoCompleterLB"
        .Visible = False
        .ListFillRange = P_SearchList.Address
        .Top = P_InputCell.Offset(1).Top
        .Left = P_InputCell.Left
        .Width = P_InputCell.Width
        .Height = P_InputCell.Height * 4
        .Object.SpecialEffect = fmSpecialEffectEtched
        .Object.BackColor = P_InputCell.Interior.Color
    End With
    
    Application.ScreenUpdating = True
    
    'hook the listbox events here
    Set ListBoxEvents = oListBox.Object

End Sub




Private Sub ListBoxEvents_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
    
    'place the selected lisbox item
    'into the inputcell upon dblclicking
    P_InputCell = oListBox.Object.Value
    oListBox.Visible = False
    P_InputCell.Select
    RefreshInputCell P_InputCell
    'watch for the next keystroke
    bListBoxDblClicked = True
    Call ProcessKeyStrokes


End Sub

'function needed
Private Sub KeyDownProc()

    Select Case oListBox.Object.ListIndex
    
    Case Is = -1
        oListBox.Object.Selected(0) = True
    
    Case Is = oListBox.Object.ListCount - 1
        oListBox.Object.Selected(0) = True
    
    Case Else
        oListBox.Object.Selected(oListBox.Object.ListIndex + 1) = True
    
    End Select
    
End Sub


Private Sub KeyUpProc()

    Select Case oListBox.Object.ListIndex
    
    Case Is = 0
        oListBox.Object.Selected(oListBox.Object.ListCount - 1) = True
    
    Case Else
        oListBox.Object.Selected(oListBox.Object.ListIndex - 1) = True
    
    End Select

End Sub

Private Sub wsEvents_SelectionChange(ByVal Target As Range)

    Dim iMatchPos As Integer
    
    If Target.Address = P_InputCell.Address Then
    
        Application.EditDirectlyInCell = False
        
        'the caret is lost if editing the worksheet
        'so let's recreate one here
        DestroyCaret
        CreateCaret lHwnd, 0, 1, 15
    Else
    
        bInputCellToBeRefreshed = True
        HideListBox
        
        'restore initial incell editing value
        Application.EditDirectlyInCell = bInCellEdit
        RefreshInputCell P_InputCell
        
    End If

End Sub

Private Sub FormatInputCell()
    
    'store initial inputcell color for later recovery
    lPrevCellColor = P_InputCell.Interior.ColorIndex
    P_InputCell.Interior.ColorIndex = 35 ' pale green

End Sub


'******************Class Properties/Methods ********************

Public Property Set InputCell(ByVal vNewValue As Range)

    Set P_InputCell = vNewValue

End Property

Public Property Set SearchList(ByVal vNewValue As Range)

    Set P_SearchList = vNewValue

End Property


Public Sub Execute()

    Call ProcessKeyStrokes

End Sub

Public Sub DestroyMe()

    Call StopKeyProcessing


End Sub


'very important !!
'-----------------
'if the keyboard typing is too fast, the
'all the variables are reset and the Class
'goes out of scope before removing the
'temporary hidden worksheet and listbox !!
'The Terminate event of the class failes to do so
'so let's at least inform the user.
Private Sub Class_Terminate()

    Dim sMessage As String

    sMessage = "Attention !!!" & vbNewLine & vbNewLine
    sMessage = sMessage & "Maybe you have typed in too fast." _
    & vbNewLine & vbNewLine
    sMessage = sMessage & _
    "The Class has accidently gone out of scope and the program" _
    & vbNewLine
    sMessage = sMessage & _
    "has not removed the temp hidden worksheet and listbox !" _
    & vbNewLine & vbNewLine
    sMessage = sMessage & _
    "Proceed cleaning up the workbook manually now."

    If Not bClassTerminatedProperly Then
        Beep
        MsgBox sMessage, vbCritical
    End If

End Sub


and here is how to call the Class in a Standard Module:

Code:
Option Explicit

Private AutoWordCompleter As cAutoWordCompleter

Sub Test()

    'prevent creating more than one
    'AutoCompleter instance at a time !
    If AutoWordCompleter Is Nothing Then
    
        Set AutoWordCompleter = New cAutoWordCompleter
        
        'assign the input and list ranges to the class
        With AutoWordCompleter
            Set .InputCell = Range("d3")
            Set .SearchList = Range("a4:a2500")
            .Execute
        End With
    Else
    
    MsgBox "The AutoWordCompleter is already running.", vbInformation
    
    End If

End Sub

Sub StoptheClass()

    If Not AutoWordCompleter Is Nothing Then
    
        AutoWordCompleter.DestroyMe
        Set AutoWordCompleter = Nothing
    Else
    
    MsgBox "The AutoCompleter is already terminated.", vbInformation
    
    End If


End Sub


Should the Class go out of scope when typing too fast , here is a routine that does the Class clean up for your convinience: (where Range("D3") is the hard-coded input cell address. Change this as required) :

Code:
Option Explicit

Sub CleanUp()

    On Error GoTo errHandler
    
    Application.DisplayAlerts = False
    ThisWorkbook.Worksheets("AutoCompleterWS").Delete
    Application.DisplayAlerts = True
    ActiveSheet.Shapes("AutoCompleterLB").Delete
    Range("d3").Interior.ColorIndex = 0
    Range("d3").ClearContents
    Application.EditDirectlyInCell = True
    Exit Sub
errHandler:
    MsgBox "Temp hidden sheet and listbox already deleted.", vbInformation

End Sub

*Note : If the Input Cell blinking cursor is ill-placed over the text or too far this can be adjusted by changing the value of the Const :OffsetPixels declared in the ProcessKeyStrokes main Routine located in the Class module.


Regards.
 

Excel Facts

Bring active cell back into view
Start at A1 and select to A9999 while writing a formula, you can't see A1 anymore. Press Ctrl+Backspace to bring active cell into view.
I was wondering if it is somehow possible to control the speed of the keyboard key-strokes programatically. This would probably help solve the problem of the unwanted termination of the Class when fast typing.

Bump.

Regards.
 
Upvote 0
I have set the SPI_SETKEYBOARDDELAY and SPI_SETKEYBOARDSPEED Constantes via the SystemParametersInfo API function but made no real difference specially the SPI_SETKEYBOARDDELAY even when giving it the highest delay value :3

Still stuck with this .

Regards.
 
Upvote 0
Hello - I am trying to create the same concept in VBA (a drop down list to pop up with the corresponding matches dynamically as i type in the first letters), however, I would need the entire example in excel to make sense of how to adjust the aforementioned code to my variables....

I am not able to download http://www.savefile.com/files/1228450 either, as I am at work and there are security issues around the download. Could you please email me the excel example with the macro in it to middle.maria@gmail.com

I would really appreciate your help!

Best regards,
Rama1
 
Upvote 0

Forum statistics

Threads
1,214,986
Messages
6,122,611
Members
449,090
Latest member
vivek chauhan

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top