none
Cannot enter text in custom TextBox when custom DropDown is displayed RRS feed

  • Question

  • I have defined a custom DropDown (inherits ToolStripDropDown) and a TextBox (inherits TextBox) controls.

    The TextBox control displays the DropDown and everything works in terms of the display and item selection. This on a WinForms application.

    I want any text entered to show up in the TextBox control (ie behave like a normal TextBox with the Text property updated) – just like a ComboBox does. However, the problem is that when the DropDown is displayed, the editing capabilities of the TextBox do not work.

    I noticed that the KeyDown, KeyPress and KeyUp events are raised in the DropDown control, even though the TextBox has Focus. I tried capturing these in the TextBox control and raising the associated event using calls to OnKeyDown, OnKeyPress and OnKeyUp. Although these events are raised correctly in the TextBox control, the TextBox.Text property remains unchanged.

    The reason I am not using the ComboBox control is I am unable to get rid of the down arrow with the DropDownStyle set to DropDown.

    Any help in resolving this problem will be very much appreciated.
    • Edited by L Lazarus Friday, May 26, 2017 12:11 PM Omitted app type
    Friday, May 26, 2017 12:08 PM

All replies

  • I dont understand what you are doing. You want to have the drop down list open and then edit text in a text box while the drop down list is open?

    It does not make sense to me. I think you need to show some code or something to describe what the problem is exactly. Perhaps describe the overall goal and maybe someone will have ideas.

    What is wrong with seeing the down arrow on a drop down list?

    What do you want to see or do with it?

    Friday, May 26, 2017 12:57 PM
  • "The TextBox control displays the DropDown and everything works in terms of the display and item selection. "

    Can you show a short example of the code which accomplishes this?  How does the textbox "own" the dropdown? How do you display the dropdown from the textbox?

    It sounds like the issue may be with the DropDown being a child control of the textbox.  In that case (I think) the textbox may have focus but the DropDown could be the active control.


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"

    Friday, May 26, 2017 1:02 PM
    Moderator
  • Lazarus,

    Could that maybe be done with a single ComboBox?

    You'd have to handle adding the new item back into the .Items collection but I think that would be easier than what you're trying to do now.


    "A problem well stated is a problem half solved.” - Charles F. Kettering

    Friday, May 26, 2017 1:06 PM
  • Many thanks for the responses and apologies for not responding sooner – notifications of responses did not seem to work.

    My objective is to filter the dropdown list based on text entered (all occurrences rather than the capability provided by AutoComplete, which filters on StartsWith).

    Issues with customising a standard ComboBox are…

    1. The Text property is updated when you use the arrow keys to move up and down the list (the TextChanged event is fired before the SelectedIndexChanged event, so no opportunity to suppress the Text property update).
    2. The Text property is updated when you change the dropdown list as a result of filtering based on the text that has been entered (always causes the first item to be selected).
    3. For reasons aesthetics in the specific  UI I am working with, the dropdown button portion of the ComboBox is not desired (although I would probably live with that if I could fix 1 and 2).

    I hope this explains things more clearly. Any suggestions will be very much appreciated.

    Here is some pruned down code (in case you find it helpful)...

    Option Compare Text
    Option Explicit On
    Option Strict On
    
    Imports LazWorks.Library.Core
    Imports System.ComponentModel
    
    
    Public Class SampleCode
    	Inherits TextBox
    
    	Friend WithEvents DropDown As ListViewDropDown
    
    	Public Property MaxDropDownItems As Integer
    		Get
    			Return DropDown.MaxDropDownItems
    		End Get
    		Set(value As Integer)
    			DropDown.MaxDropDownItems = value
    		End Set
    	End Property
    
    	<Browsable(False)>
    	Public Property Datasource As Object
    		Get
    			Return DropDown.DataSource
    		End Get
    		Set(value As Object)
    			DropDown.DataSource = value
    		End Set
    	End Property
    
    	Public Sub New()
    		MyBase.New
    		DropDown = New ListViewDropDown(Me)
    	End Sub
    
    	Protected Overrides Sub OnGotFocus(e As EventArgs)
    		MyBase.OnGotFocus(e)
    		DropDown.Display()
    	End Sub
    
    	Protected Overrides Sub OnMouseClick(e As MouseEventArgs)
    		MyBase.OnMouseClick(e)
    		DropDown.Display()
    	End Sub
    
    	Protected Overrides Sub OnLostFocus(e As EventArgs)
    		MyBase.OnLostFocus(e)
    		If DropDown.DroppedDown AndAlso Not DropDown.Focused Then
    			DropDown.Conceal()
    		End If
    	End Sub
    
    End Class
    
    Public Class DropDownListView
    	Inherits ToolStripDropDown
    
    	Public Const DefaultMaxDropDownItems As Integer = 8
    	Public Property MaxDropDownItems As Integer = DefaultMaxDropDownItems
    	Public Property MinDropDownItems As Integer = DefaultMaxDropDownItems
    	Public Property DataSource As Object
    	Public Property DroppedDown As Boolean
    	Public Property CurrentText As String
    
    	Private HostControl As Control
    	Private SourceHost As ToolStripControlHost
    	Private ForceDropDown As Boolean = False
    	Private AllowClose As Boolean = True
    
    	Private ReadOnly Property DefaultItemHeight As Integer
    		Get
    			Return New ListViewItem("ListViewItem1") With {.Font = Prefs.Font.List}.Bounds.Height
    		End Get
    	End Property
    
    	Private ReadOnly Property DropDownSize As Size
    		Get
    			Dim itemHeight As Integer = DefaultItemHeight
    			Dim itemCount As Integer = MaxDropDownItems
    
    			If LvData.Items.Count > 0 Then
    				itemHeight = LvData.Items(0).Bounds.Height
    				If LvData.Items.Count < MaxDropDownItems Then
    					If LvData.Items.Count > MinDropDownItems Then
    						itemCount = LvData.Items.Count
    					Else
    						itemCount = MinDropDownItems
    					End If
    
    				End If
    			End If
    			Return New Size(HostControl.Width, (itemHeight * itemCount) + LvData.Margin.Top + LvData.Margin.Bottom) '6)
    		End Get
    	End Property
    
    	Private ReadOnly Property ItemSelected As Boolean
    		Get
    			If LvData.SelectedIndices.Count = 0 Then
    				Return False
    			Else
    				Return Not ForceDropDown AndAlso LvData.Items(LvData.SelectedIndices(0)).Text = Text
    			End If
    		End Get
    	End Property
    
    	Public Sub New(host As Control)
    		MyBase.New()
    		HostControl = host
    		Margin = Padding.Empty
    		Padding = Padding.Empty
    		AutoSize = False
    		AutoClose = True
    
    		Initialise()
    		PnDrop.BackColor = Color.FromArgb(51, 153, 255)
    		SourceHost = New ToolStripControlHost(PnDrop) With {
    			.Margin = Padding.Empty,
    			.Padding = Padding.Empty,
    			.AutoSize = False,
    			.Size = LvData.Size
    		}
    		CurrentText = vbTab
    	End Sub
    
    	Public Sub Display()
    		Display(HostControl.Location.X, HostControl.Location.Y + HostControl.Height + 2)
    	End Sub
    
    	Public Sub Display(x As Integer, y As Integer)
    		FilterOnText()
    		If Not DroppedDown Then
    			MyBase.Show(HostControl.Parent, x, y)
    		End If
    	End Sub
    
    	Public Sub Conceal()
    		AllowClose = True
    		Close()
    	End Sub
    
    	Public Overloads Sub Close()
    		Close(ToolStripDropDownCloseReason.CloseCalled)
    	End Sub
    
    	Public Overloads Sub Close(reason As ToolStripDropDownCloseReason)
    		MyBase.Close(reason)
    		Hide()
    	End Sub
    
    	Public Sub FilterOnText()
    		If DataSource Is Nothing OrElse Text = CurrentText Then
    			Return
    		End If
    		CurrentText = Text
    		ForceDropDown = True
    		Dim sourceList As New List(Of String)
    		Dim filteredList As New List(Of String)
    		Select Case DataSource.GetType
    			Case GetType(List(Of String))
    				sourceList = DirectCast(DataSource, List(Of String))
    			Case GetType(ListView), GetType(ListView)
    				Dim lv As ListView = DirectCast(DataSource, ListView)
    				Dim itemCount As Integer
    				If lv.VirtualMode Then
    					itemCount = lv.VirtualListSize
    				Else
    					itemCount = lv.Items.Count
    				End If
    				For index As Integer = 0 To itemCount
    					sourceList.Add(lv.Items(index).Text)
    				Next
    		End Select
    		If String.IsNullOrEmpty(Text) Then
    			filteredList.AddRange(sourceList)
    		Else
    			filteredList.AddRange(sourceList.FindAll(Function(sp)
    														 Return sp.IndexOf(Text, 0, StringComparison.CurrentCultureIgnoreCase) > -1
    													 End Function).ToArray)
    		End If
    		LvData.Items.Clear()
    		For Each item In filteredList
    			LvData.Items.Add(item)
    		Next
    		If LvData.Items.Count > 0 Then
    			LvData.Items(0).Selected = True
    		End If
    		SourceHost.Size = DropDownSize
    		Size = SourceHost.Size
    		Items.Clear()
    		Items.Add(SourceHost)
    	End Sub
    
    	Protected Overrides Sub OnOpened(e As EventArgs)
    		MyBase.OnOpened(e)
    		ForceDropDown = False
    		AllowClose = False
    		DroppedDown = True
    	End Sub
    
    	Protected Overrides Sub OnClosing(e As ToolStripDropDownClosingEventArgs)
    		MyBase.OnClosing(e)
    		If AllowClose OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.AppClicked OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.AppFocusChange OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.Keyboard Then
    			Return
    		Else
    			e.Cancel = True
    		End If
    	End Sub
    
    	Protected Overrides Sub OnClosed(e As ToolStripDropDownClosedEventArgs)
    		MyBase.OnClosed(e)
    		DroppedDown = False
    	End Sub
    
    #Region "Show Method Overloads"
    
    	Public Overloads Sub Show()
    		Display()
    	End Sub
    
    	Public Overloads Sub Show(position As Point, direction As ToolStripDropDownDirection)
    		Display(position.X, position.Y)
    	End Sub
    
    	Public Overloads Sub Show(position As Point)
    		Display(position.X, position.Y)
    	End Sub
    
    	Public Overloads Sub Show(x As Integer, y As Integer)
    		Display(x, y)
    	End Sub
    
    	Public Overloads Sub Show(control As Control, position As Point, direction As ToolStripDropDownDirection)
    		Show(control, position.X, position.Y)
    	End Sub
    
    	Public Overloads Sub Show(control As Control, position As Point)
    		Show(control, position.X, position.Y)
    	End Sub
    
    	Public Overloads Sub Show(control As Control, x As Integer, y As Integer)
    		If control.Parent IsNot Nothing Then
    			HostControl = control
    		End If
    		Display(x, y)
    	End Sub
    
    #End Region
    
    #Region "DropDown Control Designer"
    
    	Private WithEvents PnDrop As Panel
    	Private WithEvents LvData As ListView
    	Private WithEvents LvDataColumnHeader As ColumnHeader
    
    	Private Sub Initialise()
    		PnDrop = New Panel()
    		LvData = New ListView()
    		LvDataColumnHeader = New ColumnHeader()
    		PnDrop.SuspendLayout()
    
    		'
    		'PnDrop
    		'
    		Me.PnDrop.BackColor = Prefs.Colour.BorderFocused ' System.Drawing.SystemColors.HotTrack
    		Me.PnDrop.Controls.Add(Me.LvData)
    		Me.PnDrop.Location = New System.Drawing.Point(344, 123)
    		Me.PnDrop.Margin = New System.Windows.Forms.Padding(0)
    		Me.PnDrop.Name = "PnDropDown"
    		Me.PnDrop.Padding = New System.Windows.Forms.Padding(1)
    		Me.PnDrop.Size = New System.Drawing.Size(154, 164)
    		Me.PnDrop.TabIndex = 10
    		'
    		'LvData
    		'
    		Me.LvData.BorderStyle = System.Windows.Forms.BorderStyle.None
    		Me.LvData.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.LvDataColumnHeader})
    		Me.LvData.Dock = System.Windows.Forms.DockStyle.Fill
    		Me.LvData.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None
    		Me.LvData.Location = New System.Drawing.Point(1, 1)
    		Me.LvData.Margin = New System.Windows.Forms.Padding(0)
    		Me.LvData.MultiSelect = False
    		Me.LvData.Name = "LvPanel"
    		Me.LvData.Size = New System.Drawing.Size(152, 162)
    		Me.LvData.TabIndex = 0
    		Me.LvData.UseCompatibleStateImageBehavior = False
    		Me.LvData.View = System.Windows.Forms.View.Details
    		'
    		'LvDataColumnHeader
    		'
    		Me.LvDataColumnHeader.Text = "Items"
    
    		Me.PnDrop.ResumeLayout()
    	End Sub
    
    #End Region
    
    End Class

    Tuesday, June 6, 2017 11:22 AM