0
votes

For some reason the "SelectionStart" property behaves differently from TextBox to ComboBox.

Create a TextBox and a ComboBox. Make sure DropDownStyle = DropDown (NOT DropDownList!)

Add these Leave events (same result if you use LostFocus event):

Private Sub TextBox1_Leave(sender As Object, e As EventArgs) Handles TextBox1.Leave
    Debug.Print("Textbox:  " & TextBox1.SelectionStart)
End Sub

Private Sub ComboBox1_Leave(sender As Object, e As EventArgs) Handles ComboBox1.Leave
    Debug.Print("ComboBox: " & ComboBox1.SelectionStart)
End Sub

Now type text into each control and change focus from control to control, with the selection cursor in different places in the string.

I get this:

Textbox:  6
ComboBox: 0
Textbox:  8
ComboBox: 0
Textbox:  5
ComboBox: 0
Textbox:  4
ComboBox: 0
... and so on

When the textbox loses focus, it returns the correct SelectionStart. When the combobox loses focus, it always returns zero.

Is there a reason, solution or reasonable workaround? I cannot seem to intercept this without creating a new variable for each combo control, and storing the SelectionStart on every click and keypress event (assuming the user might click the mouse, use arrow keys or type characters).

3
I just tested what you suggested and I didn't see exactly what you described. If I set the caret somewhere other than the beginning of the text in the ComboBox then on the first Leave event I got the expected value for SelectionStart, as well as for SelectionLength and SelectedText. When the ComboBox received focus again though, the entire text was shown as selected, so SelectionStart, SelectionLength and SelectedText reflected that. There's no managed API to control that behaviour but there may be something to be done inside the control. I'll take a closer look.jmcilhinney
I wonder how that's possible? I first noticed this thing using VS2015 and it still occurs (for me at least) in VS2017. I get the whole text highlighted when I focus the control too, but that's not such an issue here. I'm trying to insert text at the caret (I'm assuming this is the term for the blinking text cursor start position bar thing) but I'm currently limited to inserting at 0 or inserting at TextLength.torpid prey

3 Answers

0
votes

I just did some testing and it appears that the selection is reset between the Leave and LostFocus events as well as between the Enter and GotFocus events. I tried remembering the values on the first of each pair and restoring it on the second and it worked as desired:

Private comboBox1SelectionStart As Integer
Private comboBox1SelectionLength As Integer

Private Sub ComboBox1_Enter(sender As Object, e As EventArgs) Handles ComboBox1.Enter
    comboBox1SelectionStart = ComboBox1.SelectionStart
    comboBox1SelectionLength = ComboBox1.SelectionLength
End Sub

Private Sub ComboBox1_Leave(sender As Object, e As EventArgs) Handles ComboBox1.Leave
    comboBox1SelectionStart = ComboBox1.SelectionStart
    comboBox1SelectionLength = ComboBox1.SelectionLength
End Sub

Private Sub ComboBox1_GotFocus(sender As Object, e As EventArgs) Handles ComboBox1.GotFocus
    ComboBox1.SelectionStart = comboBox1SelectionStart
    ComboBox1.SelectionLength = comboBox1SelectionLength
End Sub

Private Sub ComboBox1_LostFocus(sender As Object, e As EventArgs) Handles ComboBox1.LostFocus
    ComboBox1.SelectionStart = comboBox1SelectionStart
    ComboBox1.SelectionLength = comboBox1SelectionLength
End Sub

You don't want to have to do that every time for every CokmboBox so you can build it into a custom control and then use that in place of the standard control:

Public Class ComboBoxEx
    Inherits ComboBox

    Private selectionStartTemp As Integer
    Private selectionLengthTemp As Integer

    Protected Overrides Sub OnEnter(e As EventArgs)
        MyBase.OnEnter(e)

        'Remember the current selection.
        selectionStartTemp = SelectionStart
        selectionLengthTemp = SelectionLength
    End Sub

    Protected Overrides Sub OnGotFocus(e As EventArgs)
        'Restore the selection.
        SelectionStart = selectionStartTemp
        SelectionLength = selectionLengthTemp

        MyBase.OnGotFocus(e)
    End Sub

    Protected Overrides Sub OnLeave(e As EventArgs)
        MyBase.OnLeave(e)

        'Remember the current selection.
        selectionStartTemp = SelectionStart
        selectionLengthTemp = SelectionLength
    End Sub

    Protected Overrides Sub OnLostFocus(e As EventArgs)
        'Restore the selection.
        SelectionStart = selectionStartTemp
        SelectionLength = selectionLengthTemp

        MyBase.OnLostFocus(e)
    End Sub

End Class

A side-effect of this is that you will see the text selected even when the control doesn't have focus. The ComboBox has no HideSelection property like the TextBox does. If you don't like this then I'd imagine that you could find a way to make it behave like the TextBox does but that's a different question so I won't go into it here.

0
votes

I think I've got it, using your idea but just setting the value in a different location.

Dim ComboSelectionStart As Integer

Private Sub cmbComboBox_KeyUp(sender As Object, e As KeyEventArgs) Handles cmbComboBox.KeyUp
    'Store selection on any key press
    ComboSelectionStart = cmbComboBox.SelectionStart
End Sub

Private Sub cmbComboBox_MouseClick(sender As Object, e As MouseEventArgs) Handles cmbComboBox.MouseClick
    'Store selection on any mouse click
    ComboSelectionStart = cmbComboBox.SelectionStart
End Sub

Private Sub cmbComboBox_Leave(sender As Object, e As EventArgs) Handles cmbComboBox.Leave
    'Returns correct value here because it was not altered by LostFocus event
    Debug.Print(ComboSelectionStart .ToString)
End Sub
0
votes

When you modify the OnLeave() sub in the class provided by @jmcilhinney it actually works nicely.

Protected Overrides Sub OnLeave(e As EventArgs)
        'Remember the current selection.
        selectionStartTemp = SelectionStart
        selectionLengthTemp = SelectionLength

        MyBase.OnLeave(e) 'this has to go last because it modifies the SelectionStart...
End Sub