8 Replies - 481 Views - Last Post: 18 February 2018 - 08:48 AM Rate Topic: -----

#1 Dialyser150   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 16-February 18

Unable to use scrollbar without triggering MouseLeave event

Posted 16 February 2018 - 06:04 AM

Hi everyone
I hope that someone here can help me solve a problem in VB.net 2013 that google has not been able to provide a workable solution to.
I have a form which contains multiple listboxes from each of which users need to be able to select multiple items. Resizing the listboxes using the MouseEnter and MouseLeave events keeps the form uncluttered and easy to use until the lists become long enough to require scrollbars.
As soon as the mouse pointer touches the scrollbar the MouseLeave event triggers collapsing the listbox.
Does anyone know of a workable solution for this issue?
I have provided code that demonstrates the problem below, all that is required is a winform with a listbox (TestForm and ListBox1 in my example)

Public Class TestForm

    '  TestForm size 300,300
    '  ListBox1 size 182,17
    '  ListBox1 location 12,12
    '  ListBox1 selectionmode MultiExtended

    Private Sub TestForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ListBox1.Items.Clear()
        For l = 100 To 140
            ListBox1.Items.Add("Test item " & l)
        Next
    End Sub

    Private Sub ListBox1_MouseEnter(sender As Object, e As EventArgs) Handles ListBox1.MouseEnter
        ListBox1.Height = 230 ' expand listbox for selection
    End Sub

    Private Sub ListBox1_MouseLeave(sender As Object, e As EventArgs) Handles ListBox1.MouseLeave
        ListBox1.Height = 17 ' collapse listbox
    End Sub
End Class


Thank you in advance

Is This A Good Question/Topic? 0
  • +

Replies To: Unable to use scrollbar without triggering MouseLeave event

#2 modi123_1   User is offline

  • Suitor #2
  • member icon



Reputation: 14097
  • View blog
  • Posts: 56,495
  • Joined: 12-June 08

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 16 February 2018 - 07:51 AM

Perhaps have a defined button that when clicked expands or shrinks the list boxes versus relying on mouse enter/leaving.
Was This Post Helpful? 0
  • +
  • -

#3 Dialyser150   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 16-February 18

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 16 February 2018 - 08:52 AM

View Postmodi123_1, on 16 February 2018 - 07:51 AM, said:

Perhaps have a defined button that when clicked expands or shrinks the list boxes versus relying on mouse enter/leaving.


I did consider that, but with 7 boxes at present and at least double that eventually it would take the ease of use away from the form.
Thank you for the suggestion though.
Was This Post Helpful? 0
  • +
  • -

#4 maceysoftware   User is offline

  • Foo
  • member icon

Reputation: 353
  • View blog
  • Posts: 1,533
  • Joined: 07-September 13

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 16 February 2018 - 10:36 AM

I disagree that it would take the ease of use away from the form, as otherwise, you are saying the combo box and the date picker are not easy to use.

I would also like to point out how annoying it could be when moving your cursor around the form that controls randomly expand and shrink.

Personally having something to click might not be a bad thing, but that doesn't mean you need to have a button next to each one listbox, what if you made the listbox the button, What if you made the listbox act like the combo-box on DropDown Mode. Whereby the first click drops you onto the form and the second click (or more in your case) selects the item, just don't close it back up when you do your second click.

OK I am not going to go along and style your control for you, you can look into custom drawing listbox's if you want to, however, I actually found this pretty tricky to do myself so I will provide a working prototype, however, that is all it is a prototype.

My solution contains the following:

Inheritance - Let's face it if you want more than one listbox doing this you want to create a custom listbox control and wrap your implementation in there, if you need to make any changes then, its in one places, not the 7 places you currently have along with the 'Atleast double' you will be adding.

Overring events including WndProc - Therefore if your not happy doing this then you most likely won't like this conception, We are having to do this to give the control a usable feel to it, otherwise when you expand the listbox by clicking on the control it will select the item you are clicking on.

Public Class MyExtenderListBox
    Inherits ListBox

    Private _expanded As Boolean = False

    Sub New()
        ' For my custom control i can default in the size i want it to be when it gets dropped on the form
        Me.Size = New Size(182, 17)
        ' I can also default in the selectionmode so i don't have to remember to set it.
        Me.SelectionMode = SelectionMode.MultiExtended
        ' maybe there are others?
    End Sub

    Protected Overrides Sub onkeydown(e As KeyEventArgs)
        MyBase.onkeydown(e)
        ' If the user clicks the escape key, this will collapse the listbox
        If e.KeyData = Keys.Escape Then
            CollapseListBox()
        End If
    End Sub

    Protected Overrides Sub OnLostFocus(e As EventArgs)
        ' So if you click (or tab) on another control this event will be fired, this will collapse the listbox
        CollapseListBox()
        MyBase.OnLostFocus(e)
    End Sub

    Private Sub ExpandListBox()
        ' Helper method just to expand, incase you need to call it from multiple places.
        Me.Size = New Size(182, 230)
        _expanded = True
    End Sub

    Private Sub CollapseListBox()
        ' Helper method just to Collapse, we do need to call it from multiple places.
        _expanded = False
        Me.Size = New Size(182, 17)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        Const WM_LBUTTONDOWN As Integer = 513
        Const WM_LBUTTONUP As Integer = 514

        If m.Msg = WM_LBUTTONDOWN OrElse m.Msg = WM_LBUTTONUP Then
            ' So if the buttonDown or the Buttom up message is about to be processed.
            If Not _expanded Then
                ' And the listbox isn't currently Expanded then, Expand the list, also don't process the left click event.
                ' Why? because otherwise each time you expand it, it will automatically select the item you clicked on
                ExpandListBox()
                Exit Sub
            End If
        End If

        ' How did I find out 513 and 514 are the messages I want to block? Trail and error mainly, put a debug.print in here out putting the m.msg 
        ' then moved my mouse over the control, waited for it to calm down then clicked.

        ' I did try using the Mouse_Click and the Click event first, however even if you do take the Mybase.Event out of their overrides the item 
        ' was still getting selected.

        MyBase.WndProc(m)
    End Sub

End Class


Currently, the only ways to collapse the control is to either press the escape key or shifting focus onto another control, which I haven't actually tested.

I would like to stress I wrote this post in a rush so sorry if some of it doesn't make sense (it's been a very trying day!). If you want any further explanation please just ask and I will try to comply.
Was This Post Helpful? 1
  • +
  • -

#5 andrewsw   User is offline

  • RequestedRangeNotSatisfiable
  • member icon

Reputation: 6554
  • View blog
  • Posts: 26,569
  • Joined: 12-December 12

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 17 February 2018 - 01:39 AM

Thinking outside the listbox ;)

I don't know how many items you have, or might have, but isn't increasing the height negating the need for a scrollbar?

I support the creation of a custom control for this.

I would have thought it possible to check the cursor position on mouse leave to check whether it still remains within the expanse of the listbox or its scrollbar. But I speculate.
Was This Post Helpful? 0
  • +
  • -

#6 Dialyser150   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 16-February 18

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 17 February 2018 - 01:46 AM

Thank you for taking the time to respond, I like the click to expand functionality and will take another look at my UI design.
Your code has opened up avenues I had not considered for exploration and experimentation.
Was This Post Helpful? 0
  • +
  • -

#7 Dialyser150   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 6
  • Joined: 16-February 18

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 17 February 2018 - 02:11 AM

View Postandrewsw, on 17 February 2018 - 01:39 AM, said:

Thinking outside the listbox ;)/>

I don't know how many items you have, or might have, but isn't increasing the height negating the need for a scrollbar?

I support the creation of a custom control for this.

I would have thought it possible to check the cursor position on mouse leave to check whether it still remains within the expanse of the listbox or its scrollbar. But I speculate.


The lists range from around 20 items to 3 or 4 hundred and even broken down to minimal lengths would still generate scroll bars at full screen.

I agree with your final paragraph, I too thought that there should be a way to check but my experiments showed that as soon as the mouse touched the scrollbar the leave event triggered, even though the coordinates were still within the bounds of the control. Attempts to override this only resulted in preventing the MouseLeave event from working at all.
Was This Post Helpful? 0
  • +
  • -

#8 andrewsw   User is offline

  • RequestedRangeNotSatisfiable
  • member icon

Reputation: 6554
  • View blog
  • Posts: 26,569
  • Joined: 12-December 12

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 17 February 2018 - 02:19 AM

Quote

Attempts to override this only resulted in preventing the MouseLeave event from working at all.

I don't know what attempts you might be referring to. I wasn't talking about trying to override anything. In the mouseleave I would check whether the mouse is over the scrollbar, and don't collapse the height if it is. My first attempt would be to just check whether it was within certain pixels to the right of the listbox. Then I think there is some method that tells us whether the mouse if over a control.
Was This Post Helpful? 0
  • +
  • -

#9 IronRazer   User is offline

  • Custom Control Freak
  • member icon

Reputation: 1503
  • View blog
  • Posts: 3,801
  • Joined: 01-February 13

Re: Unable to use scrollbar without triggering MouseLeave event

Posted 18 February 2018 - 08:48 AM

You can do this similar to what andrewsw has mentioned. You will need to create your own small ListBox class so that you can override the WndProc (Window Process) and detect the WM_MOUSELEAVE message and WM_NCMOUSELEAVE message.

When a WM_MOUSELEAVE message is received by the ListBox Window and the mouse is still inside the bounds of the ListBox control, then you can stop the WM_MOUSELEAVE message from being processed. In the example below, this is used just to block the WM_MOUSELEAVE from being processed when the mouse moves from the client area of the ListBox to the ScrollBar of the ListBox.

This only leaves one problem. The ListBox does not detect the WM_MOUSELEAVE message when the mouse leaves the ListBox by moving off the ScrollBar side. This is where the WM_NCMOUSELEAVE message comes into play.

The WM_NCMOUSELEAVE message is received when the mouse leaves a non-client area of the ListBox. For a ListBox that would be the Border of the ListBox. By making sure the mouse position is outside the borders of the ListBox when this message is received, you can resize the ListBox back down.

Public Class ListBoxEx
    Inherits ListBox

    Private Const WM_NCMOUSELEAVE As Integer = &H2A2
    Private Const WM_MOUSELEAVE As Integer = &H2A3

    Public Property MouseActiveHight As Integer = 230

    Public Sub New()
        Me.SelectionMode = SelectionMode.MultiExtended
        Me.Height = Me.ItemHeight + (BorderWidth * 2)
    End Sub

    Protected Overrides Sub onmouseenter(e As EventArgs)
        Me.Height = MouseActiveHight
        MyBase.onmouseenter(e)
    End Sub

    Private ReadOnly Property BorderWidth As Integer 'gets the width of one side of the border
        Get
            Return (Me.Height - Me.ClientSize.Height) \ 2
        End Get
    End Property

    Private ReadOnly Property TrueClientRect As Rectangle 'for getting the true client rectangle instead of the actual ClientRectagle property which the scrollbar is subtracted from
        Get
            Return New Rectangle(0, 0, Me.Width - (BorderWidth * 2), Me.Height - (BorderWidth * 2))
        End Get
    End Property

    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_NCMOUSELEAVE Then
            If Not Me.TrueClientRect.Contains(Me.PointToClient(MousePosition)) Then
                Me.Height = Me.ItemHeight + (BorderWidth * 2)
            End If
        End If
        If m.Msg = WM_MOUSELEAVE Then
            If Me.TrueClientRect.Contains(Me.PointToClient(MousePosition)) Then 'this stops the WM_MOUSELEAVE message when moving onto the ScrollBar
                m.Result = CType(0, IntPtr)
                Return
            Else
                Me.Height = Me.ItemHeight + (BorderWidth * 2)
            End If
        End If
        MyBase.WndProc(m)
    End Sub
End Class



Posted Image
Was This Post Helpful? 2
  • +
  • -

Page 1 of 1