wtconway
asked on
ComboBox automatically select item on key press
Hello,
I have a combobox that once you enter the control, sets the .DroppedDown property to True so it will expand the box to show the contents. Then a user can start typing stuff in and it will scroll to any matches in the box. From there I want the user to be able to hit the ENTER key and the top item be "selected."
I've tried doing this and it appears to be working; however, I only sets the .Text property to whatever the top item is and doesn't actually affect the .SelectedText or .SelectedValue (which is ultimately what I care about).
So what this means is that in order for the top shown item to be "selected" I have to hit the DOWN ARROW and then ENTER for it to process the selection change and modify the appropriate properties.
Any ideas on how I can eliminate the DOWN ARROW step? I have not overridden the combobox keypress eventhandler in anyway. Here's the Handler I have for it:
Private Sub cbo_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv entArgs) Handles cboFrom.KeyDown, cboTo.KeyDown
Dim cbo As ComboBox = CType(sender, ComboBox)
If e.KeyCode = Keys.Enter Then
cbo.DroppedDown = False
If cbo.Name = "cboFrom" Then cboTo.Focus() 'Move the focus to the next object
If cbo.Name = "cboTo" Then txtAmount.Focus() 'Move the focus to the next object
Else
If cbo.DroppedDown = False Then
cbo.DroppedDown = True
End If
End If
End Sub
Thank you in advance.
I have a combobox that once you enter the control, sets the .DroppedDown property to True so it will expand the box to show the contents. Then a user can start typing stuff in and it will scroll to any matches in the box. From there I want the user to be able to hit the ENTER key and the top item be "selected."
I've tried doing this and it appears to be working; however, I only sets the .Text property to whatever the top item is and doesn't actually affect the .SelectedText or .SelectedValue (which is ultimately what I care about).
So what this means is that in order for the top shown item to be "selected" I have to hit the DOWN ARROW and then ENTER for it to process the selection change and modify the appropriate properties.
Any ideas on how I can eliminate the DOWN ARROW step? I have not overridden the combobox keypress eventhandler in anyway. Here's the Handler I have for it:
Private Sub cbo_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv
Dim cbo As ComboBox = CType(sender, ComboBox)
If e.KeyCode = Keys.Enter Then
cbo.DroppedDown = False
If cbo.Name = "cboFrom" Then cboTo.Focus() 'Move the focus to the next object
If cbo.Name = "cboTo" Then txtAmount.Focus() 'Move the focus to the next object
Else
If cbo.DroppedDown = False Then
cbo.DroppedDown = True
End If
End If
End Sub
Thank you in advance.
Include the following class in the solution you need auto-complete combos in:
--- 8< ---
Public Class CAutoCompleteCombo
'This variable will keep track of whether to skip the operation because
'of a special key having been pressed.
Private Shared mfSkip As Boolean = False
'Determine whether any special key was pressed.
Public Shared Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr essEventAr gs)
'Retrieve a reference to the ComboBox that sent this event.
Dim Combo As ComboBox = CType(sender, ComboBox)
If Asc(e.KeyChar) = Keys.Escape Then
'Clear the text.
Combo.SelectedIndex = -1
Combo.Text = ""
mfSkip = True
ElseIf Char.IsControl(e.KeyChar) Then
mfSkip = True
Else
mfSkip = False
End If
End Sub
'Perform the text substituion.
Shared Sub TextChanged(ByVal sender As Object, ByVal e As System.EventArgs)
'Retrieve a reference to the ComboBox that sent this event.
Dim Combo As ComboBox = CType(sender, ComboBox)
If Combo.Text <> "" And Not mfSkip Then
'Search for a matching entry.
Dim MatchText As String = Combo.Text
Dim Match As Integer = Combo.FindString(MatchText )
'If a matching entry is found, insert it now.
If Match <> -1 Then
Combo.SelectedIndex = Match
'Select the added text so it can be replaced
'if the user keeps typing.
Combo.SelectionStart = MatchText.Length
Combo.SelectionLength = Combo.Text.Length - Combo.SelectionStart
End If
End If
End Sub
End Class
--- 8< ---
Note that this code is not my own, but I - sadly - don't know who has written it.
Anyway, in order for the above to be working, you'll need to have your combos' appropriate events be handled by that class.
So, in your form's Load-event, add the handlers like this:
AddHandler cboYourCombo.KeyPress, AddressOf CAutoCompleteCombo.KeyPres s
AddHandler cboYourCombo.TextChanged, AddressOf CAutoCompleteCombo.TextCha nged
Et oilá, you're ready to go. That is, if I understood your question right anyway ... :-)
Cheers & HTH,
Olaf
--- 8< ---
Public Class CAutoCompleteCombo
'This variable will keep track of whether to skip the operation because
'of a special key having been pressed.
Private Shared mfSkip As Boolean = False
'Determine whether any special key was pressed.
Public Shared Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr
'Retrieve a reference to the ComboBox that sent this event.
Dim Combo As ComboBox = CType(sender, ComboBox)
If Asc(e.KeyChar) = Keys.Escape Then
'Clear the text.
Combo.SelectedIndex = -1
Combo.Text = ""
mfSkip = True
ElseIf Char.IsControl(e.KeyChar) Then
mfSkip = True
Else
mfSkip = False
End If
End Sub
'Perform the text substituion.
Shared Sub TextChanged(ByVal sender As Object, ByVal e As System.EventArgs)
'Retrieve a reference to the ComboBox that sent this event.
Dim Combo As ComboBox = CType(sender, ComboBox)
If Combo.Text <> "" And Not mfSkip Then
'Search for a matching entry.
Dim MatchText As String = Combo.Text
Dim Match As Integer = Combo.FindString(MatchText
'If a matching entry is found, insert it now.
If Match <> -1 Then
Combo.SelectedIndex = Match
'Select the added text so it can be replaced
'if the user keeps typing.
Combo.SelectionStart = MatchText.Length
Combo.SelectionLength = Combo.Text.Length - Combo.SelectionStart
End If
End If
End Sub
End Class
--- 8< ---
Note that this code is not my own, but I - sadly - don't know who has written it.
Anyway, in order for the above to be working, you'll need to have your combos' appropriate events be handled by that class.
So, in your form's Load-event, add the handlers like this:
AddHandler cboYourCombo.KeyPress, AddressOf CAutoCompleteCombo.KeyPres
AddHandler cboYourCombo.TextChanged, AddressOf CAutoCompleteCombo.TextCha
Et oilá, you're ready to go. That is, if I understood your question right anyway ... :-)
Cheers & HTH,
Olaf
Sample code for a combobox type ahead and a piece for colored combox lines....
'FORM 1
Public Class ctlComboBoxTypeAhead
Inherits System.Windows.Forms.Form
Public Declare Function SendMessageByString Lib "user32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As Int32
Public Const CB_FINDSTRING As Integer = &H14CS
Public Const CB_FINDSTRINGEXACT As Integer = &H158S
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
InitializeCombobox()
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.ICon tainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents ComboBox1 As System.Windows.Forms.Combo Box
Friend WithEvents cbo As System.Windows.Forms.Combo Box
<System.Diagnostics.Debugg erStepThro ugh()> Private Sub InitializeComponent()
Me.ComboBox1 = New System.Windows.Forms.Combo Box
Me.cbo = New System.Windows.Forms.Combo Box
Me.SuspendLayout()
'
'ComboBox1
'
Me.ComboBox1.DrawMode = System.Windows.Forms.DrawM ode.OwnerD rawFixed
Me.ComboBox1.DropDownStyle = System.Windows.Forms.Combo BoxStyle.D ropDownLis t
Me.ComboBox1.Location = New System.Drawing.Point(16, 16)
Me.ComboBox1.Name = "ComboBox1"
Me.ComboBox1.Size = New System.Drawing.Size(232, 21)
Me.ComboBox1.TabIndex = 0
'
'cbo
'
Me.cbo.Location = New System.Drawing.Point(16, 88)
Me.cbo.Name = "cbo"
Me.cbo.Size = New System.Drawing.Size(232, 21)
Me.cbo.TabIndex = 1
Me.cbo.Text = "ComboBox2"
'
'ctlComboBoxTypeAhead
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 135)
Me.Controls.Add(Me.cbo)
Me.Controls.Add(Me.ComboBo x1)
Me.Name = "ctlComboBoxTypeAhead"
Me.Text = "ctlComboBoxTypeAhead"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub ComboBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr essEventAr gs) Handles ComboBox1.KeyPress
Dim KeyAscii As Short = Asc(e.KeyChar)
Select Case KeyAscii
Case System.Windows.Forms.Keys. Back
Case Is >= 32
'perform auto search
If ComboBox1.SelectionLength > 0 Then
ComboBox1.SelectedText = Chr(KeyAscii)
Else
ComboBox1.Text += Chr(KeyAscii)
End If
KeyAscii = 0
Call TypeAheadCombo(ComboBox1)
End Select
If KeyAscii = 0 Then
e.Handled = True
End If
End Sub
Private Sub InitializeCombobox()
With ComboBox1
.Items.Clear()
.Items.Add("one")
.Items.Add("two")
.Items.Add("other")
.Items.Add("new")
.Items.Add("newer")
.Items.Add("newbee")
.Items.Add("safe")
.Items.Add("error")
.Items.Add("apple")
.Items.Add("alter")
.Items.Add("ables")
.Items.Add("absents")
End With
With cbo
.Items.Clear()
.Items.Add("one")
.Items.Add("two")
.Items.Add("other")
.Items.Add("new")
.Items.Add("newer")
.Items.Add("newbee")
.Items.Add("safe")
.Items.Add("error")
.Items.Add("apple")
.Items.Add("alter")
.Items.Add("ables")
.Items.Add("absents")
End With
End Sub
Public Sub TypeAheadCombo(ByVal ljCombo As Object)
Dim lviIndex As Short
Dim lvsKeyString As String
Dim lviKeyLen As Short
lvsKeyString = ljCombo.Text
lviKeyLen = lvsKeyString.Length
If lviKeyLen > 0 Then
With ljCombo
lviIndex = SrchComboList(.Handle, lvsKeyString, False)
If lviIndex <> -1 Then
.SelectedIndex = -1
.SelectedIndex = lviIndex
.SelectionStart = lviKeyLen
.SelectionLength = Len(.Text) - lviKeyLen
End If
End With
End If
End Sub
Public Function SrchComboList(ByVal hWnd As IntPtr, ByVal lvsFind As String, Optional ByVal fExact As Boolean = False) As Short
'Finds an entry in a combo box that matches the specified prefix.
'This search is not case-sensitive.
'Returns:
'if found - a valid listindex
'Else -1
Dim lvlSearchType As Short
If Len(lvsFind) = 0 Then
SrchComboList = -1
Exit Function
End If
Select Case fExact
Case True
lvlSearchType = CB_FINDSTRINGEXACT
Case False
lvlSearchType = CB_FINDSTRING
End Select
SrchComboList = SendMessageByString(hWnd, lvlSearchType, -1, lvsFind)
End Function
'cbo is the comboBox control
Private Sub cbo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv entArgs) Handles cbo.KeyUp
AutoCompleteCombo_KeyUp(se nder, e)
End Sub
'********************* Better way of doing Type Head *************************
Public Shared Sub AutoCompleteCombo_KeyUp(By Val cbo As ComboBox, ByVal e As KeyEventArgs)
'**************
' you can use it 2 ways.
'either add the handles keyword after the sub name to handle your KeyUp event
'eg. Public Shared Sub AutoCompleteCombo_KeyUp(By Val cbo As ComboBox, ByVal e As KeyEventArgs) Handles cboSuburb.KeyUp
'or
'i use it as a generic sub so call it from the KeyUp event as follows:
' Private Sub cboSuburb_KeyUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEv entArgs) Handles cboSuburb.KeyUp
' AutoCompleteCombo_KeyUp(se nder, e)
' End Sub
'**************
Dim sTypedText As String
Dim iFoundIndex As Integer
Dim oFoundItem As Object
Dim sFoundText As String
Dim sAppendText As String
'Allow select keys without Autocompleting
Select Case e.KeyCode
Case Keys.Back, Keys.Left, Keys.Right, Keys.Up, Keys.Delete, Keys.Down
Return
End Select
'Get the Typed Text and Find it in the list
sTypedText = cbo.Text
iFoundIndex = cbo.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 = cbo.Items(iFoundIndex)
'Use the ListControl.GetItemText to resolve the Name in case the Combo
' was Data bound
sFoundText = cbo.GetItemText(oFoundItem )
'Append then found text to the typed text to preserve case
sAppendText = sFoundText.Substring(sType dText.Leng th)
cbo.Text = sTypedText & sAppendText
'Select the Appended Text
cbo.SelectionStart = sTypedText.Length
cbo.SelectionLength = sAppendText.Length
End If
End Sub
Private Sub ComboBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawI temEventAr gs) Handles ComboBox1.DrawItem
Dim c As System.Drawing.Color
If e.Index < 0 Then
e.DrawBackground()
e.DrawFocusRectangle()
Exit Sub
End If
'Determine colour to use
If e.Index Mod 2 = 0 Then
c = Color.Pink
Else
c = Color.Gray
End If
e.DrawBackground()
e.DrawFocusRectangle()
e.Graphics.DrawRectangle(N ew Pen(c), New Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
e.Graphics.FillRectangle(N ew SolidBrush(c), New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
e.Graphics.DrawString(Me.C omboBox1.I tems.Item( e.Index), Me.ComboBox1.Font, New SolidBrush(Color.Black), New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
End Sub
End Class
'FORM 1
Public Class ctlComboBoxTypeAhead
Inherits System.Windows.Forms.Form
Public Declare Function SendMessageByString Lib "user32" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As Int32, ByVal lParam As String) As Int32
Public Const CB_FINDSTRING As Integer = &H14CS
Public Const CB_FINDSTRINGEXACT As Integer = &H158S
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
InitializeCombobox()
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.ICon
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents ComboBox1 As System.Windows.Forms.Combo
Friend WithEvents cbo As System.Windows.Forms.Combo
<System.Diagnostics.Debugg
Me.ComboBox1 = New System.Windows.Forms.Combo
Me.cbo = New System.Windows.Forms.Combo
Me.SuspendLayout()
'
'ComboBox1
'
Me.ComboBox1.DrawMode = System.Windows.Forms.DrawM
Me.ComboBox1.DropDownStyle
Me.ComboBox1.Location = New System.Drawing.Point(16, 16)
Me.ComboBox1.Name = "ComboBox1"
Me.ComboBox1.Size = New System.Drawing.Size(232, 21)
Me.ComboBox1.TabIndex = 0
'
'cbo
'
Me.cbo.Location = New System.Drawing.Point(16, 88)
Me.cbo.Name = "cbo"
Me.cbo.Size = New System.Drawing.Size(232, 21)
Me.cbo.TabIndex = 1
Me.cbo.Text = "ComboBox2"
'
'ctlComboBoxTypeAhead
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(292, 135)
Me.Controls.Add(Me.cbo)
Me.Controls.Add(Me.ComboBo
Me.Name = "ctlComboBoxTypeAhead"
Me.Text = "ctlComboBoxTypeAhead"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub ComboBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr
Dim KeyAscii As Short = Asc(e.KeyChar)
Select Case KeyAscii
Case System.Windows.Forms.Keys.
Case Is >= 32
'perform auto search
If ComboBox1.SelectionLength > 0 Then
ComboBox1.SelectedText = Chr(KeyAscii)
Else
ComboBox1.Text += Chr(KeyAscii)
End If
KeyAscii = 0
Call TypeAheadCombo(ComboBox1)
End Select
If KeyAscii = 0 Then
e.Handled = True
End If
End Sub
Private Sub InitializeCombobox()
With ComboBox1
.Items.Clear()
.Items.Add("one")
.Items.Add("two")
.Items.Add("other")
.Items.Add("new")
.Items.Add("newer")
.Items.Add("newbee")
.Items.Add("safe")
.Items.Add("error")
.Items.Add("apple")
.Items.Add("alter")
.Items.Add("ables")
.Items.Add("absents")
End With
With cbo
.Items.Clear()
.Items.Add("one")
.Items.Add("two")
.Items.Add("other")
.Items.Add("new")
.Items.Add("newer")
.Items.Add("newbee")
.Items.Add("safe")
.Items.Add("error")
.Items.Add("apple")
.Items.Add("alter")
.Items.Add("ables")
.Items.Add("absents")
End With
End Sub
Public Sub TypeAheadCombo(ByVal ljCombo As Object)
Dim lviIndex As Short
Dim lvsKeyString As String
Dim lviKeyLen As Short
lvsKeyString = ljCombo.Text
lviKeyLen = lvsKeyString.Length
If lviKeyLen > 0 Then
With ljCombo
lviIndex = SrchComboList(.Handle, lvsKeyString, False)
If lviIndex <> -1 Then
.SelectedIndex = -1
.SelectedIndex = lviIndex
.SelectionStart = lviKeyLen
.SelectionLength = Len(.Text) - lviKeyLen
End If
End With
End If
End Sub
Public Function SrchComboList(ByVal hWnd As IntPtr, ByVal lvsFind As String, Optional ByVal fExact As Boolean = False) As Short
'Finds an entry in a combo box that matches the specified prefix.
'This search is not case-sensitive.
'Returns:
'if found - a valid listindex
'Else -1
Dim lvlSearchType As Short
If Len(lvsFind) = 0 Then
SrchComboList = -1
Exit Function
End If
Select Case fExact
Case True
lvlSearchType = CB_FINDSTRINGEXACT
Case False
lvlSearchType = CB_FINDSTRING
End Select
SrchComboList = SendMessageByString(hWnd, lvlSearchType, -1, lvsFind)
End Function
'cbo is the comboBox control
Private Sub cbo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv
AutoCompleteCombo_KeyUp(se
End Sub
'********************* Better way of doing Type Head *************************
Public Shared Sub AutoCompleteCombo_KeyUp(By
'**************
' you can use it 2 ways.
'either add the handles keyword after the sub name to handle your KeyUp event
'eg. Public Shared Sub AutoCompleteCombo_KeyUp(By
'or
'i use it as a generic sub so call it from the KeyUp event as follows:
' Private Sub cboSuburb_KeyUp(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEv
' AutoCompleteCombo_KeyUp(se
' End Sub
'**************
Dim sTypedText As String
Dim iFoundIndex As Integer
Dim oFoundItem As Object
Dim sFoundText As String
Dim sAppendText As String
'Allow select keys without Autocompleting
Select Case e.KeyCode
Case Keys.Back, Keys.Left, Keys.Right, Keys.Up, Keys.Delete, Keys.Down
Return
End Select
'Get the Typed Text and Find it in the list
sTypedText = cbo.Text
iFoundIndex = cbo.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 = cbo.Items(iFoundIndex)
'Use the ListControl.GetItemText to resolve the Name in case the Combo
' was Data bound
sFoundText = cbo.GetItemText(oFoundItem
'Append then found text to the typed text to preserve case
sAppendText = sFoundText.Substring(sType
cbo.Text = sTypedText & sAppendText
'Select the Appended Text
cbo.SelectionStart = sTypedText.Length
cbo.SelectionLength = sAppendText.Length
End If
End Sub
Private Sub ComboBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawI
Dim c As System.Drawing.Color
If e.Index < 0 Then
e.DrawBackground()
e.DrawFocusRectangle()
Exit Sub
End If
'Determine colour to use
If e.Index Mod 2 = 0 Then
c = Color.Pink
Else
c = Color.Gray
End If
e.DrawBackground()
e.DrawFocusRectangle()
e.Graphics.DrawRectangle(N
e.Graphics.FillRectangle(N
e.Graphics.DrawString(Me.C
End Sub
End Class
AutoComplete autoExpand ComboBox:
Option Strict On
Option Explicit On
Imports System.Windows.Forms
Public Class AutoCompleteCombo
Inherits ComboBox
Private mResetOnClear As Boolean = False
Protected Overrides Sub RefreshItem(ByVal index As Integer)
MyBase.RefreshItem(index)
End Sub
Protected Overrides Sub SetItemsCore(ByVal items As System.Collections.IList)
MyBase.SetItemsCore(items)
End Sub
Public Shadows Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr essEventAr gs) Handles MyBase.KeyPress
Dim intIndex As Integer
Dim strEntry As String
Me.DroppedDown = True
If Char.IsControl(e.KeyChar) Then
If MyBase.SelectionStart <= 1 Then
If mResetOnClear Then
MyBase.SelectedIndex = 0
MyBase.SelectAll()
Else
MyBase.Text = String.Empty
MyBase.SelectedIndex = -1
MyBase.SelectedIndex = -1
End If
e.Handled = True
Exit Sub
End If
If MyBase.SelectionLength = 0 Then
strEntry = MyBase.Text.Substring(0, MyBase.Text.Length - 1)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart - 1)
End If
ElseIf (Not Char.IsLetterOrDigit(e.Key Char)) And (Not Char.IsWhiteSpace(e.KeyCha r)) Then '< 32 Or KeyAscii > 127 Then
Exit Sub
Else
If MyBase.SelectionLength = 0 Then
strEntry = UCase(MyBase.Text & e.KeyChar)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart) & e.KeyChar
End If
End If
intIndex = MyBase.FindString(strEntry )
If intIndex <> -1 Then
MyBase.SelectedIndex = intIndex
MyBase.SelectionStart = strEntry.Length
MyBase.SelectionLength = MyBase.Text.Length - MyBase.SelectionStart
End If
e.Handled = True
Exit Sub
End Sub
'Make the Enter key work like the Tab key
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Messa ge, ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Public Property ResetOnClear() As Boolean
Get
Return mResetOnClear
End Get
Set(ByVal Value As Boolean)
mResetOnClear = Value
End Set
End Property
End Class
Option Strict On
Option Explicit On
Imports System.Windows.Forms
Public Class AutoCompleteCombo
Inherits ComboBox
Private mResetOnClear As Boolean = False
Protected Overrides Sub RefreshItem(ByVal index As Integer)
MyBase.RefreshItem(index)
End Sub
Protected Overrides Sub SetItemsCore(ByVal items As System.Collections.IList)
MyBase.SetItemsCore(items)
End Sub
Public Shadows Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr
Dim intIndex As Integer
Dim strEntry As String
Me.DroppedDown = True
If Char.IsControl(e.KeyChar) Then
If MyBase.SelectionStart <= 1 Then
If mResetOnClear Then
MyBase.SelectedIndex = 0
MyBase.SelectAll()
Else
MyBase.Text = String.Empty
MyBase.SelectedIndex = -1
MyBase.SelectedIndex = -1
End If
e.Handled = True
Exit Sub
End If
If MyBase.SelectionLength = 0 Then
strEntry = MyBase.Text.Substring(0, MyBase.Text.Length - 1)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart - 1)
End If
ElseIf (Not Char.IsLetterOrDigit(e.Key
Exit Sub
Else
If MyBase.SelectionLength = 0 Then
strEntry = UCase(MyBase.Text & e.KeyChar)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart) & e.KeyChar
End If
End If
intIndex = MyBase.FindString(strEntry
If intIndex <> -1 Then
MyBase.SelectedIndex = intIndex
MyBase.SelectionStart = strEntry.Length
MyBase.SelectionLength = MyBase.Text.Length - MyBase.SelectionStart
End If
e.Handled = True
Exit Sub
End Sub
'Make the Enter key work like the Tab key
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Messa
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Public Property ResetOnClear() As Boolean
Get
Return mResetOnClear
End Get
Set(ByVal Value As Boolean)
mResetOnClear = Value
End Set
End Property
End Class
ASKER
I really like the suggestion amyhxu presented. However, I cannot seem to get it to work properly. I have created a new class with the content of her class and have not altered it in any way.
I have another class that writes out my own control which is just two comboboxes that use the AutoCompleteCombo control like so:
Friend WithEvents cboTo As AutoCompleteCombo
Friend WithEvents cboFrom As AutoCompleteCombo
Me.cboTo = New AutoCompleteCombo
Me.cboFrom = New AutoCompleteCombo
I have no event handlers that work with cboFrom or cboTo; yet I cannot figure out why the code would not work. I can see the ComboBox drop down its contents and navigate to a certain item once I start typing and it even goes to the next combobox when I hit {ENTER}, but it just won't select the top item nor the item in the .Text property.
I have another class that writes out my own control which is just two comboboxes that use the AutoCompleteCombo control like so:
Friend WithEvents cboTo As AutoCompleteCombo
Friend WithEvents cboFrom As AutoCompleteCombo
Me.cboTo = New AutoCompleteCombo
Me.cboFrom = New AutoCompleteCombo
I have no event handlers that work with cboFrom or cboTo; yet I cannot figure out why the code would not work. I can see the ComboBox drop down its contents and navigate to a certain item once I start typing and it even goes to the next combobox when I hit {ENTER}, but it just won't select the top item nor the item in the .Text property.
I'm a little confused when you were saying that you want to select the top item. What is a top item? When a user types something into the combobox, the first matching item in the list will be highlighted. And when the user hits ENTER key, that highlighted item is selected, and the corresponding .Text and .SelectedValue property changes. Note that only the items in the list can be selected. This is how the combobox I provided would work.
If the behavior I described is what you want, can you try creating a new project with a form and the AutoCompleteCombo class and only add the combobox to the form to see how it works (also make sure you are using .NET framework 1.1 and service pack 1). If it's not what you expected, can you explain the top item to me?
If the behavior I described is what you want, can you try creating a new project with a form and the AutoCompleteCombo class and only add the combobox to the form to see how it works (also make sure you are using .NET framework 1.1 and service pack 1). If it's not what you expected, can you explain the top item to me?
ASKER
What I mean by top item is exactly what you said in the first paragraph. For example, the item I'm looking for is '3456-John Doe'
I want the user to be able to start typing '345' and it automatically navigate to '3456-John Doe'. If the user hits enter; however, nothing gets changed. But I can see the selection rectangle being drawn over the item (the blue rectangle that changes the item text color to white).
How would I determine which version and sp of .NET I'm running? And I'm assuming that I have to be running that on the development AND client machines?
I want the user to be able to start typing '345' and it automatically navigate to '3456-John Doe'. If the user hits enter; however, nothing gets changed. But I can see the selection rectangle being drawn over the item (the blue rectangle that changes the item text color to white).
How would I determine which version and sp of .NET I'm running? And I'm assuming that I have to be running that on the development AND client machines?
Concerning the auto-complete without actually selecting the entry - this might be feasible if you derive your control. However, it won't be the smartest approach as you'll have to display the user's input somewhere, and with the typed in keys overwriting the current value without actually changing it, nobody would know about the original one. With that in mind, you'll be striving away from what is considered the general and/or ergonomical and/or intuitive way of handling user-input resp. UI-design.
Concerning the how-to for finding the .NET-version and SP-status: There's no way except for checking the win-registry.
Check this path:
HKEY_LOCAL_MACHINE\SOFTWAR E\Microsof t\NET Framework Setup\NDP\v1.1.4322
Keys therein:
- Install: Will be $1 if the framework has been installed
- SP: Will be $1 if SP1 has been installed
Under the NDP-path the framework-installations should be found. I don't have either 1.0 nor 2.0 installed, so somebody might have to confirm, but I guess MS learned a little about continuity anway ... :-)
Concerning the how-to for finding the .NET-version and SP-status: There's no way except for checking the win-registry.
Check this path:
HKEY_LOCAL_MACHINE\SOFTWAR
Keys therein:
- Install: Will be $1 if the framework has been installed
- SP: Will be $1 if SP1 has been installed
Under the NDP-path the framework-installations should be found. I don't have either 1.0 nor 2.0 installed, so somebody might have to confirm, but I guess MS learned a little about continuity anway ... :-)
Control Panel --> Add Or Remove Programs --> Microsoft .NET Framework 1.1 (SP1 is shown as Hotfix, don't remember the exact number, will tell you tomorrow). You should always make sure both development machine and client machines have Microsoft .NET Framework 1.1 and SP1. That's the basic requirement for running .NET applications. They come in as Windows Update (Critical Update).
Seems you need to handle special characters other than the numbers, letters and space, i.e. "-". Do you have more special characters in the combobox items? Can you list them for me? The combobox class needs a little modification to handle those characters. I can send you the modified code if you give me the list of them.
BTW, did you try creating a very simple app with only one combobox as I suggested in my last post?
Seems you need to handle special characters other than the numbers, letters and space, i.e. "-". Do you have more special characters in the combobox items? Can you list them for me? The combobox class needs a little modification to handle those characters. I can send you the modified code if you give me the list of them.
BTW, did you try creating a very simple app with only one combobox as I suggested in my last post?
Another way for check framework and sp:
Open IE --> Tools --> Windows Update will lead you to the available critical updates page. If you don't see Microsoft .NET framework 1.1 and SP1 in that list, that means your computer already has them. NOTE: this only applies to Win XP machines, other versions of operating system like Win98 or Win98 SE will list them as normal updates instead of critical ones.
Open IE --> Tools --> Windows Update will lead you to the available critical updates page. If you don't see Microsoft .NET framework 1.1 and SP1 in that list, that means your computer already has them. NOTE: this only applies to Win XP machines, other versions of operating system like Win98 or Win98 SE will list them as normal updates instead of critical ones.
SP1 in control panel is called Microsoft .NET Framework 1.1 Hotfix (KB886903)
ASKER
Ok I do have v1.1.4322 installed. I'm about to create a simple application that uses the drop down class you provided, amyhxu: hopefully it will work. Below are some examples of what my drop down box would contain:
Select One
1234-009B-First Union
2345-009C-Palmetto Bank
3456-009E-CCB
4567-009F-Wachovia
5678-009G-Bank Of America
6789-009H-Southtrust-ACH
Select One
1234-009B-First Union
2345-009C-Palmetto Bank
3456-009E-CCB
4567-009F-Wachovia
5678-009G-Bank Of America
6789-009H-Southtrust-ACH
ASKER
I have some results for you. I could not get the small application to work with simple alphanumeric characters populating the comboboxes. I'm posting my code for the form (minus the design generated code), as well as my custom class that puts two of your comboboxes inside a panel (creating a row). Please take a look at it at your convenience. Thank you.
#Region "TheForm"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Text="Create Row"
row = New cboRow
Dim i As Integer
row.cboFrom.Items.Add("Sel ect One") : row.cboTo.Items.Add("Selec t One")
For i = 1 To 20
'just adding some random stuff
row.cboFrom.Items.Add("Tes t" & (i * 2 + 33).ToString())
row.cboTo.Items.Add("Test" & (i * 2 + 33).ToString())
Next
row.Top = Button1.Top + Button1.Height + 20
row.Left = Button1.Left
Me.Controls.Add(row)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Text="Read Values"
MsgBox(row.cboFrom.Selecte dIndex)
End Sub
#End Region
#Region "My Class"
Public Class cboRow
Inherits System.Windows.Forms.UserC ontrol
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.ICon tainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents pnl As System.Windows.Forms.Panel
Friend WithEvents txtAmount As System.Windows.Forms.TextB ox
Friend WithEvents cboTo As AutoCompleteCombo
Friend WithEvents cboFrom As AutoCompleteCombo
Friend WithEvents btnDelete As System.Windows.Forms.Butto n
<System.Diagnostics.Debugg erStepThro ugh()> Private Sub InitializeComponent()
Me.pnl = New System.Windows.Forms.Panel
Me.txtAmount = New System.Windows.Forms.TextB ox
Me.cboTo = New AutoCompleteCombo
Me.cboFrom = New AutoCompleteCombo
Me.btnDelete = New System.Windows.Forms.Butto n
Me.pnl.SuspendLayout()
Me.SuspendLayout()
'
'Panel1
'
Me.pnl.Controls.Add(Me.btn Delete)
Me.pnl.Controls.Add(Me.txt Amount)
Me.pnl.Controls.Add(Me.cbo To)
Me.pnl.Controls.Add(Me.cbo From)
Me.pnl.Dock = System.Windows.Forms.DockS tyle.Top
Me.pnl.Location = New System.Drawing.Point(0, 0)
Me.pnl.Name = "Panel1"
Me.pnl.Size = New System.Drawing.Size(584, 40)
Me.pnl.TabIndex = 1
'
'txtAmt
'
Me.txtAmount.Location = New System.Drawing.Point(408, 8)
Me.txtAmount.Name = "txtAmt"
Me.txtAmount.Size = New System.Drawing.Size(80, 21)
Me.txtAmount.TabIndex = 2
Me.txtAmount.TextAlign = HorizontalAlignment.Right
'
'cboTo
'
Me.cboTo.Location = New System.Drawing.Point(208, 8)
Me.cboTo.Name = "cboTo"
Me.cboTo.Size = New System.Drawing.Size(160, 21)
Me.cboTo.TabIndex = 1
'
'cboFrom
'
Me.cboFrom.Location = New System.Drawing.Point(32, 8)
Me.cboFrom.Name = "cboFrom"
Me.cboFrom.Size = New System.Drawing.Size(160, 21)
Me.cboFrom.TabIndex = 0
'
'btnDelete
'
Me.btnDelete.Location = New System.Drawing.Point(0, 8)
Me.btnDelete.Name = "btnDelete"
Me.btnDelete.Size = New System.Drawing.Size(24, 24)
Me.btnDelete.TabIndex = 3
'Me.btnDelete.Text = "X"
Me.btnDelete.Image = Image.FromFile(Application .StartupPa th & "\delete_icon.gif")
'
'cboRow
'
Me.Controls.Add(Me.pnl)
Me.Name = "cboRow"
Me.Size = New System.Drawing.Size(490, 30)
Me.pnl.ResumeLayout(False)
Me.ResumeLayout(False)
End Sub
#End Region
Public Event DeleteButtonPressed(ByVal s As cboRow)
Public Event ValueChanged(ByVal s As cboRow)
#Region "Privates"
Private _FromValue As String
Private _ToValue As String
Private _Amount As String
#End Region
#Region "Properties"
Public ReadOnly Property FromValue() As String
Get
Return cboFrom.SelectedValue
End Get
End Property
Public ReadOnly Property ToValue() As String
Get
Return cboTo.SelectedValue
End Get
End Property
Public ReadOnly Property Amount() As String
Get
Return txtAmount.Text
End Get
End Property
#End Region
#Region "Subs"
'Public Shadows Sub TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboFrom.TextChanged, cboTo.TextChanged
' 'Retrieve a reference to the ComboBox that sent this event.
' Dim Combo As ComboBox = CType(sender, ComboBox)
' If Combo.Text <> "" Then
' 'Search for a matching entry.
' Dim MatchText As String = Combo.Text
' Dim Match As Integer = Combo.FindString(MatchText )
' 'If a matching entry is found, insert it now.
' If Match <> -1 Then
' Combo.SelectedIndex = Match
' 'Select the added text so it can be replaced
' 'if the user keeps typing.
' Combo.SelectionStart = MatchText.Length
' Combo.SelectionLength = Combo.Text.Length - Combo.SelectionStart
' End If
' End If
'End Sub
#End Region
#Region "Event Handlers"
Private Sub cboRow_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
cboFrom.Focus()
End Sub
Private Sub txtAmount_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtAmount.Enter
Dim amt As TextBox = CType(sender, TextBox)
If amt.Text = "" Or amt.Text = "$0.00" Or amt.Text = "0" Then
amt.Text = ""
End If
End Sub
'Private Sub cbo_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv entArgs) Handles cboFrom.KeyDown, cboTo.KeyDown
' Dim cbo As ComboBox = CType(sender, ComboBox)
' If e.KeyCode = Keys.Enter Then
' cbo.DroppedDown = False
' If cbo.Name = "cboFrom" Then cboTo.Focus()
' If cbo.Name = "cboTo" Then txtAmount.Focus()
' Else
' 'If cbo.DroppedDown = False Then
' ' cbo.DroppedDown = True
' 'End If
' End If
'End Sub
Private Sub txtAmount_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtAmount.Leave
Dim txt As String = CType(sender, TextBox).Text
Dim amt As Double = txt
Try
amt = CType(txt, Double)
CType(sender, TextBox).Text = String.Format("{0:c}", amt)
Catch ex As Exception
End Try
End Sub
Private Sub txtAmount_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv entArgs) Handles txtAmount.KeyDown
If e.KeyCode = Keys.Enter Then
End If
End Sub
Private Sub btnDelete_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDelete.Click
RaiseEvent DeleteButtonPressed(Me)
End Sub
#End Region
End Class
#End Region
#Region "TheForm"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Text="Create Row"
row = New cboRow
Dim i As Integer
row.cboFrom.Items.Add("Sel
For i = 1 To 20
'just adding some random stuff
row.cboFrom.Items.Add("Tes
row.cboTo.Items.Add("Test"
Next
row.Top = Button1.Top + Button1.Height + 20
row.Left = Button1.Left
Me.Controls.Add(row)
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Text="Read Values"
MsgBox(row.cboFrom.Selecte
End Sub
#End Region
#Region "My Class"
Public Class cboRow
Inherits System.Windows.Forms.UserC
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.ICon
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents pnl As System.Windows.Forms.Panel
Friend WithEvents txtAmount As System.Windows.Forms.TextB
Friend WithEvents cboTo As AutoCompleteCombo
Friend WithEvents cboFrom As AutoCompleteCombo
Friend WithEvents btnDelete As System.Windows.Forms.Butto
<System.Diagnostics.Debugg
Me.pnl = New System.Windows.Forms.Panel
Me.txtAmount = New System.Windows.Forms.TextB
Me.cboTo = New AutoCompleteCombo
Me.cboFrom = New AutoCompleteCombo
Me.btnDelete = New System.Windows.Forms.Butto
Me.pnl.SuspendLayout()
Me.SuspendLayout()
'
'Panel1
'
Me.pnl.Controls.Add(Me.btn
Me.pnl.Controls.Add(Me.txt
Me.pnl.Controls.Add(Me.cbo
Me.pnl.Controls.Add(Me.cbo
Me.pnl.Dock = System.Windows.Forms.DockS
Me.pnl.Location = New System.Drawing.Point(0, 0)
Me.pnl.Name = "Panel1"
Me.pnl.Size = New System.Drawing.Size(584, 40)
Me.pnl.TabIndex = 1
'
'txtAmt
'
Me.txtAmount.Location = New System.Drawing.Point(408, 8)
Me.txtAmount.Name = "txtAmt"
Me.txtAmount.Size = New System.Drawing.Size(80, 21)
Me.txtAmount.TabIndex = 2
Me.txtAmount.TextAlign = HorizontalAlignment.Right
'
'cboTo
'
Me.cboTo.Location = New System.Drawing.Point(208, 8)
Me.cboTo.Name = "cboTo"
Me.cboTo.Size = New System.Drawing.Size(160, 21)
Me.cboTo.TabIndex = 1
'
'cboFrom
'
Me.cboFrom.Location = New System.Drawing.Point(32, 8)
Me.cboFrom.Name = "cboFrom"
Me.cboFrom.Size = New System.Drawing.Size(160, 21)
Me.cboFrom.TabIndex = 0
'
'btnDelete
'
Me.btnDelete.Location = New System.Drawing.Point(0, 8)
Me.btnDelete.Name = "btnDelete"
Me.btnDelete.Size = New System.Drawing.Size(24, 24)
Me.btnDelete.TabIndex = 3
'Me.btnDelete.Text = "X"
Me.btnDelete.Image = Image.FromFile(Application
'
'cboRow
'
Me.Controls.Add(Me.pnl)
Me.Name = "cboRow"
Me.Size = New System.Drawing.Size(490, 30)
Me.pnl.ResumeLayout(False)
Me.ResumeLayout(False)
End Sub
#End Region
Public Event DeleteButtonPressed(ByVal s As cboRow)
Public Event ValueChanged(ByVal s As cboRow)
#Region "Privates"
Private _FromValue As String
Private _ToValue As String
Private _Amount As String
#End Region
#Region "Properties"
Public ReadOnly Property FromValue() As String
Get
Return cboFrom.SelectedValue
End Get
End Property
Public ReadOnly Property ToValue() As String
Get
Return cboTo.SelectedValue
End Get
End Property
Public ReadOnly Property Amount() As String
Get
Return txtAmount.Text
End Get
End Property
#End Region
#Region "Subs"
'Public Shadows Sub TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cboFrom.TextChanged, cboTo.TextChanged
' 'Retrieve a reference to the ComboBox that sent this event.
' Dim Combo As ComboBox = CType(sender, ComboBox)
' If Combo.Text <> "" Then
' 'Search for a matching entry.
' Dim MatchText As String = Combo.Text
' Dim Match As Integer = Combo.FindString(MatchText
' 'If a matching entry is found, insert it now.
' If Match <> -1 Then
' Combo.SelectedIndex = Match
' 'Select the added text so it can be replaced
' 'if the user keeps typing.
' Combo.SelectionStart = MatchText.Length
' Combo.SelectionLength = Combo.Text.Length - Combo.SelectionStart
' End If
' End If
'End Sub
#End Region
#Region "Event Handlers"
Private Sub cboRow_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
cboFrom.Focus()
End Sub
Private Sub txtAmount_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtAmount.Enter
Dim amt As TextBox = CType(sender, TextBox)
If amt.Text = "" Or amt.Text = "$0.00" Or amt.Text = "0" Then
amt.Text = ""
End If
End Sub
'Private Sub cbo_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv
' Dim cbo As ComboBox = CType(sender, ComboBox)
' If e.KeyCode = Keys.Enter Then
' cbo.DroppedDown = False
' If cbo.Name = "cboFrom" Then cboTo.Focus()
' If cbo.Name = "cboTo" Then txtAmount.Focus()
' Else
' 'If cbo.DroppedDown = False Then
' ' cbo.DroppedDown = True
' 'End If
' End If
'End Sub
Private Sub txtAmount_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtAmount.Leave
Dim txt As String = CType(sender, TextBox).Text
Dim amt As Double = txt
Try
amt = CType(txt, Double)
CType(sender, TextBox).Text = String.Format("{0:c}", amt)
Catch ex As Exception
End Try
End Sub
Private Sub txtAmount_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv
If e.KeyCode = Keys.Enter Then
End If
End Sub
Private Sub btnDelete_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDelete.Click
RaiseEvent DeleteButtonPressed(Me)
End Sub
#End Region
End Class
#End Region
I'll take a close look at your code when I have time. But what I suggested was that you can try creating only one form with one combobox populated with some fake data and see if that works. In your code, you are using the custom control which is too complicated to tell where the problem is. If in that simple test the combobox works fine, then the problem is in your custom control class; if not, we'll check what's wrong with the combobox. I've used this combobox class many times and never had a problem. So it might be something else that's naughty.
ASKER
I just tried a simple test and could not get it to work. I am also posting that code for your perusal. Here it is, minus the windows generated code.
Dim cbo as New AutoCompleteCombo
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Text="Read Values"
MsgBox(cbo.SelectedIndex)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Text="Create Combo"
cbo.Name = "myAutoCombo"
cbo.Top = Button1.Top + Button1.Height + 5
cbo.Left = Button1.Left
Me.Controls.Add(cbo)
Dim i As Integer
For i = 1 To 5
cbo.Items.Add("Hello " & i)
Next
End Sub
Selected index came back with -1 after I started typing in "Hel" and then hit the TAB button and "Hello 1" was the shown text. Ideas?
Dim cbo as New AutoCompleteCombo
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'Text="Read Values"
MsgBox(cbo.SelectedIndex)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'Text="Create Combo"
cbo.Name = "myAutoCombo"
cbo.Top = Button1.Top + Button1.Height + 5
cbo.Left = Button1.Left
Me.Controls.Add(cbo)
Dim i As Integer
For i = 1 To 5
cbo.Items.Add("Hello " & i)
Next
End Sub
Selected index came back with -1 after I started typing in "Hel" and then hit the TAB button and "Hello 1" was the shown text. Ideas?
This is becoming interesting: I see what problem you are facing. The selectedIndex property gives the result of previous selection.
I used the same code in my apps except for this line: Me.DroppedDown = True. Without this line, everything is working, but you won't be able to auto expand the combobox. Even if this line is not in this class but in the combobox's keydown event, you'll still get the same error. Haven't figured out a solution yet. The following is the code I used that's working without the autoexpand feature:
Option Strict On
Option Explicit On
Imports System.Windows.Forms
Public Class AutoCompleteCombo
Inherits ComboBox
Private mResetOnClear As Boolean = False
Protected Overrides Sub RefreshItem(ByVal index As Integer)
MyBase.RefreshItem(index)
End Sub
Protected Overrides Sub SetItemsCore(ByVal items As System.Collections.IList)
MyBase.SetItemsCore(items)
End Sub
Public Shadows Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr essEventAr gs) Handles MyBase.KeyPress
Dim intIndex As Integer
Dim strEntry As String
If Char.IsControl(e.KeyChar) Then
If MyBase.SelectionStart <= 1 Then
If mResetOnClear Then
MyBase.SelectedIndex = 0
MyBase.SelectAll()
Else
MyBase.Text = String.Empty
MyBase.SelectedIndex = -1
MyBase.SelectedIndex = -1
End If
e.Handled = True
Exit Sub
End If
If MyBase.SelectionLength = 0 Then
strEntry = MyBase.Text.Substring(0, MyBase.Text.Length - 1)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart - 1)
End If
ElseIf (Not Char.IsLetterOrDigit(e.Key Char)) And (Not Char.IsWhiteSpace(e.KeyCha r)) And (e.KeyChar <> "-") Then
Exit Sub
Else
If MyBase.SelectionLength = 0 Then
strEntry = UCase(MyBase.Text & e.KeyChar)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart) & e.KeyChar
End If
End If
intIndex = MyBase.FindString(strEntry )
If intIndex <> -1 Then
MyBase.SelectedIndex = intIndex
MyBase.SelectionStart = strEntry.Length
MyBase.SelectionLength = MyBase.Text.Length - MyBase.SelectionStart
End If
e.Handled = True
Exit Sub
End Sub
'Make the Enter key work like the Tab key
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Messa ge, ByVal keyData As System.Windows.Forms.Keys) As Boolean
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Public Property ResetOnClear() As Boolean
Get
Return mResetOnClear
End Get
Set(ByVal Value As Boolean)
mResetOnClear = Value
End Set
End Property
End Class
I used the same code in my apps except for this line: Me.DroppedDown = True. Without this line, everything is working, but you won't be able to auto expand the combobox. Even if this line is not in this class but in the combobox's keydown event, you'll still get the same error. Haven't figured out a solution yet. The following is the code I used that's working without the autoexpand feature:
Option Strict On
Option Explicit On
Imports System.Windows.Forms
Public Class AutoCompleteCombo
Inherits ComboBox
Private mResetOnClear As Boolean = False
Protected Overrides Sub RefreshItem(ByVal index As Integer)
MyBase.RefreshItem(index)
End Sub
Protected Overrides Sub SetItemsCore(ByVal items As System.Collections.IList)
MyBase.SetItemsCore(items)
End Sub
Public Shadows Sub KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPr
Dim intIndex As Integer
Dim strEntry As String
If Char.IsControl(e.KeyChar) Then
If MyBase.SelectionStart <= 1 Then
If mResetOnClear Then
MyBase.SelectedIndex = 0
MyBase.SelectAll()
Else
MyBase.Text = String.Empty
MyBase.SelectedIndex = -1
MyBase.SelectedIndex = -1
End If
e.Handled = True
Exit Sub
End If
If MyBase.SelectionLength = 0 Then
strEntry = MyBase.Text.Substring(0, MyBase.Text.Length - 1)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart - 1)
End If
ElseIf (Not Char.IsLetterOrDigit(e.Key
Exit Sub
Else
If MyBase.SelectionLength = 0 Then
strEntry = UCase(MyBase.Text & e.KeyChar)
Else
strEntry = MyBase.Text.Substring(0, MyBase.SelectionStart) & e.KeyChar
End If
End If
intIndex = MyBase.FindString(strEntry
If intIndex <> -1 Then
MyBase.SelectedIndex = intIndex
MyBase.SelectionStart = strEntry.Length
MyBase.SelectionLength = MyBase.Text.Length - MyBase.SelectionStart
End If
e.Handled = True
Exit Sub
End Sub
'Make the Enter key work like the Tab key
Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Messa
If msg.WParam.ToInt32() = CInt(Keys.Enter) Then
SendKeys.Send("{Tab}")
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
Public Property ResetOnClear() As Boolean
Get
Return mResetOnClear
End Get
Set(ByVal Value As Boolean)
mResetOnClear = Value
End Set
End Property
End Class
wtconway,
have you even tried my approach with the two event-handlers (requiring two lines of code for each combo using it) ..?
I've been using it for quite some time now and never experienced any difficulties. The major advantage in using it is that I won't have to setup any reference in order to use an inherited combo with the form-designer.
Still, if you use Amy's control, why not add it to your toolbox so you can use the designer to setup the basic props?
Cheers,
Olaf
have you even tried my approach with the two event-handlers (requiring two lines of code for each combo using it) ..?
I've been using it for quite some time now and never experienced any difficulties. The major advantage in using it is that I won't have to setup any reference in order to use an inherited combo with the form-designer.
Still, if you use Amy's control, why not add it to your toolbox so you can use the designer to setup the basic props?
Cheers,
Olaf
ASKER
Amy, would your posted code still cause the combobox to attempt to complete the text? Event without the dropdown effect?
Olaf, I tried your suggestion with the CAutoCompleteCombo and still no relief. Code follows:
Dim WithEvents cbo As New ComboBox
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
MsgBox(cbo.SelectedIndex)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
cbo.Name = "myAutoCombo"
cbo.Top = Button1.Top + Button1.Height + 5
cbo.Left = Button1.Left
Me.Controls.Add(cbo)
Dim i As Integer
For i = 1 To 5
cbo.Items.Add("Hello " & i)
Next
AddHandler cbo.KeyPress, AddressOf CAutoCompleteCombo.KeyPres s
AddHandler cbo.TextChanged, AddressOf CAutoCompleteCombo.TextCha nged
End Sub
Olaf, I tried your suggestion with the CAutoCompleteCombo and still no relief. Code follows:
Dim WithEvents cbo As New ComboBox
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
MsgBox(cbo.SelectedIndex)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
cbo.Name = "myAutoCombo"
cbo.Top = Button1.Top + Button1.Height + 5
cbo.Left = Button1.Left
Me.Controls.Add(cbo)
Dim i As Integer
For i = 1 To 5
cbo.Items.Add("Hello " & i)
Next
AddHandler cbo.KeyPress, AddressOf CAutoCompleteCombo.KeyPres
AddHandler cbo.TextChanged, AddressOf CAutoCompleteCombo.TextCha
End Sub
Now I see what you mean - it seems I haven't ever used the code as you would like to.
If you add this line to the TextChanged-sub within the CAutoCompleteCombo-class (right before "End Sub") ...
Console.WriteLine(Combo.Te xt & ": " & Combo.SelectedIndex.ToStri ng)
... and this sub to your form-class ...
Private Sub cbo_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbo.LostFocus
Console.WriteLine(cbo.Text & ": " & cbo.SelectedIndex.ToString )
End Sub
... you'll notice that the SelectedIndex-property is being set appropriately, but as soon as the combo looses focus, it'll be reset to -1. I have no idea as to why this happens, but it sure is reproducable.
If, OTOH, you enter a letter and a corresponding entry is being selected by the auto-complete stuff AND you simply open the combo (Alt-down or mouse, won't matter), hit <TAB> to go to another control on the form, the SelectedIndex-prop will actually keep the value.
I'd sure consider this behaviour to be a bug but, even after some 30mins trying, I wasn't able to come up with a work-around. :-(
Amy?
Cheers,
Olaf
If you add this line to the TextChanged-sub within the CAutoCompleteCombo-class (right before "End Sub") ...
Console.WriteLine(Combo.Te
... and this sub to your form-class ...
Private Sub cbo_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbo.LostFocus
Console.WriteLine(cbo.Text
End Sub
... you'll notice that the SelectedIndex-property is being set appropriately, but as soon as the combo looses focus, it'll be reset to -1. I have no idea as to why this happens, but it sure is reproducable.
If, OTOH, you enter a letter and a corresponding entry is being selected by the auto-complete stuff AND you simply open the combo (Alt-down or mouse, won't matter), hit <TAB> to go to another control on the form, the SelectedIndex-prop will actually keep the value.
I'd sure consider this behaviour to be a bug but, even after some 30mins trying, I wasn't able to come up with a work-around. :-(
Amy?
Cheers,
Olaf
ASKER
Well this just sucks. It looks like I'll have to force my users to actually mimic the dropdown affect. Could that possibly be detected on the box when the user hits tab and automatically drop it down and then close it right back to reset the property?
Told ya, I fumbled around with this "§$! for at least half an hour, and that included trying to open and close the combo via code. Didn't help at all.
But I'll look into it again later tonight - it's 9:22pm here already and I there's work for another half an hour left ...
Cheers,
Olaf
But I'll look into it again later tonight - it's 9:22pm here already and I there's work for another half an hour left ...
Cheers,
Olaf
I just checked out an article on CodeProject (http://www.codeproject.com/vb/net/MultiColumnFlatCombo.asp). Dropped in a button with this code ...
MessageBox.Show(comboTest. SelectedIt em.ToStrin g)
... behind that button's click-event. Worked!
Would you want to try that out ..?
Cheers,
Olaf
MessageBox.Show(comboTest.
... behind that button's click-event. Worked!
Would you want to try that out ..?
Cheers,
Olaf
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Olaf,
Good news!!! It all works...all I had to do was append the .Leave event handler to your original AutoComplete class and now voila....perfect bliss. I appreciate all of your time and effort on this problem and if you ever need anything, you let me know. Thank you so much.
-Jeff Evans
Good news!!! It all works...all I had to do was append the .Leave event handler to your original AutoComplete class and now voila....perfect bliss. I appreciate all of your time and effort on this problem and if you ever need anything, you let me know. Thank you so much.
-Jeff Evans
Jeff,
you sure are welcome.
Cheers,
Olaf
you sure are welcome.
Cheers,
Olaf
Dim index As Integer
index = cbo.FindString(cbo.Text)
cbo.SelectedIndex = index
Roger