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
  • 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
    			Return DropDown.MaxDropDownItems
    		End Get
    		Set(value As Integer)
    			DropDown.MaxDropDownItems = value
    		End Set
    	End Property
    	Public Property Datasource As Object
    			Return DropDown.DataSource
    		End Get
    		Set(value As Object)
    			DropDown.DataSource = value
    		End Set
    	End Property
    	Public Sub New()
    		DropDown = New ListViewDropDown(Me)
    	End Sub
    	Protected Overrides Sub OnGotFocus(e As EventArgs)
    	End Sub
    	Protected Overrides Sub OnMouseClick(e As MouseEventArgs)
    	End Sub
    	Protected Overrides Sub OnLostFocus(e As EventArgs)
    		If DropDown.DroppedDown AndAlso Not DropDown.Focused Then
    		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
    			Return New ListViewItem("ListViewItem1") With {.Font = Prefs.Font.List}.Bounds.Height
    		End Get
    	End Property
    	Private ReadOnly Property DropDownSize As Size
    			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
    						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
    			If LvData.SelectedIndices.Count = 0 Then
    				Return False
    				Return Not ForceDropDown AndAlso LvData.Items(LvData.SelectedIndices(0)).Text = Text
    			End If
    		End Get
    	End Property
    	Public Sub New(host As Control)
    		HostControl = host
    		Margin = Padding.Empty
    		Padding = Padding.Empty
    		AutoSize = False
    		AutoClose = True
    		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)
    		If Not DroppedDown Then
    			MyBase.Show(HostControl.Parent, x, y)
    		End If
    	End Sub
    	Public Sub Conceal()
    		AllowClose = True
    	End Sub
    	Public Overloads Sub Close()
    	End Sub
    	Public Overloads Sub Close(reason As ToolStripDropDownCloseReason)
    	End Sub
    	Public Sub FilterOnText()
    		If DataSource Is Nothing OrElse Text = CurrentText Then
    		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
    					itemCount = lv.Items.Count
    				End If
    				For index As Integer = 0 To itemCount
    		End Select
    		If String.IsNullOrEmpty(Text) Then
    														 Return sp.IndexOf(Text, 0, StringComparison.CurrentCultureIgnoreCase) > -1
    													 End Function).ToArray)
    		End If
    		For Each item In filteredList
    		If LvData.Items.Count > 0 Then
    			LvData.Items(0).Selected = True
    		End If
    		SourceHost.Size = DropDownSize
    		Size = SourceHost.Size
    	End Sub
    	Protected Overrides Sub OnOpened(e As EventArgs)
    		ForceDropDown = False
    		AllowClose = False
    		DroppedDown = True
    	End Sub
    	Protected Overrides Sub OnClosing(e As ToolStripDropDownClosingEventArgs)
    		If AllowClose OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.AppClicked OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.AppFocusChange OrElse
    			e.CloseReason = ToolStripDropDownCloseReason.Keyboard Then
    			e.Cancel = True
    		End If
    	End Sub
    	Protected Overrides Sub OnClosed(e As ToolStripDropDownClosedEventArgs)
    		DroppedDown = False
    	End Sub
    #Region "Show Method Overloads"
    	Public Overloads Sub Show()
    	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()
    		Me.PnDrop.BackColor = Prefs.Colour.BorderFocused ' System.Drawing.SystemColors.HotTrack
    		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
    		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
    		Me.LvDataColumnHeader.Text = "Items"
    	End Sub
    #End Region
    End Class

    Tuesday, June 6, 2017 11:22 AM