Link to home
Start Free TrialLog in
Avatar of majnun
majnun

asked on

VB .NET: using onMouseLeave on form

I'm creating a form that expands, and when the user moves the mouse off the expanded form, I want to trigger the resize to normal. It also seems that if the mouse enters a control on the form it triggers the form's onMouseleave event.

I am setting the size by: me.height = 100

But the onMouseleave event seems to fire when the mouse moves onto a control, or moves past the OLD size of the form.

How can I get the onMouseleave event not to fire if i move into a control it owns, and to use the form's new size when calculating if the user has moved the mouse out of it?

Is this a good approach: when the form's onMouseLeave event is triggered determine if the mouse is in the border of the form? How would I do that?

Thanks!
Avatar of andrewharris
andrewharris

I would use the fors LostFocus Event instead.

Andrew
Avatar of majnun

ASKER

Yeah, but the expanded form might be covering up the other windows, so it's better to use a modified "mouseOut" functionality so that the user can actually click the other forms to change the focus.

I'm working on figuring out how to check the mouse position against the actual boundaries of the form.
   Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        If (e.X > 0 And e.X < Me.Width) And (e.Y > 0 And e.Y < Me.Height) Then
            Me.Width = 400
            Me.Height = 400
        Else
            Me.Width = 300
            Me.Height = 300
        End If
    End Sub
Sorry, change the form values to whatever you want - but you get the idea
Avatar of majnun

ASKER

toddhd:

on the right track, but the problem is that it only shrinks when i move the mouse out slowly to the left... if i move out quickly or exit the form rectangle in any other direction it stays at the bigger dimensions.

I'll keep thinking - in the meantime, if it's not a high-perf app, you can add a loop or timer somewhere to call OnMouseMove on a given interval - say, every one second. That might help. Better yet, skip the event altogether and just track the mouse in a loop (I'm old an school game programmer - that's how I'd do it anyway).

Just FYI - I'm not having that problem on my machine - even fast, it's working.
Avatar of majnun

ASKER

Ok here's what i did and it works (add to the forms onMouseLeave event):

  Dim X, Y, Xoffset, Yoffset, Mx, My As Integer
        Dim off As Boolean = False
        Xoffset = 5 'border pixels
        Yoffset = 35 'titlebar & border pixels

        X = Me.Width - Xoffset
        Y = Me.Height - Yoffset
        Mx = Me.PointToClient(Me.MousePosition).X
        My = Me.PointToClient(Me.MousePosition).Y
     
        If Mx >= X Or Mx < 1 Then off = True
        If My >= Y Or My < 1 Then off = True

        If off then 'shrink window

Can someone help me refine this so that it automatically takes the title bar and form border into consideration, so that i can use the same function for different form types (with and without title bars, dialogs, etc. etc.) and at different screen resolutions (becase as it stands i think the offsets will need to change based on the screen resolution, no?)

If anyone has a better way of doing this please let me know.
Avatar of majnun

ASKER

I like toddhd solution of overriding the onMouseMove sub better than mine, seems more efficient... i wonder why i cant get it to work properly... any thoughts?
majnun ,
check out bounds property!
from msdn:
"
.NET Framework Class Library  

Control.Bounds Property
Gets or sets the size and location of the control including its nonclient elements.

[Visual Basic]
Public Property Bounds As Rectangle
[C#]
public Rectangle Bounds {get; set;}
[C++]
public: __property Rectangle get_Bounds();
public: __property void set_Bounds(Rectangle);
[JScript]
public function get Bounds() : Rectangle;
public function set Bounds(Rectangle);
Property Value
A Rectangle that represents the size and location of the control including its nonclient elements.

Remarks
The bounds of the control includes the nonclient elements such as scroll bars, borders, title bars, and menus. The SetBoundsCore method is called to set the Bounds property. The Bounds property is not always changed through its set method so you should override the SetBoundsCore method to ensure that your code is executed when the Bounds property is set.
"
Majnun,

FYI again, I think I just "lucked out" last night - I tried it again this morning, and it's not working so well - I'm still working on it when I can... just wanted to let you know "it's not you" :)
Ok, try this:

    Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
        Me.Width = 400
        Me.Height = 400
    End Sub

    Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
        Dim Flag As Boolean = False
        Dim c As Control
        For Each c In Me.Controls
            Dim p As Point
            p = Me.PointToClient(Me.MousePosition)
            If p.X >= c.Left And p.X <= c.Left + c.Width Then
                If p.Y >= c.Top And p.Y <= c.Top + c.Height Then
                    Flag = True
                End If
            End If
        Next

        If Not Flag Then
            Me.Width = 300
            Me.Height = 300
        End If

    End Sub
Avatar of majnun

ASKER

hagipmc:

Interesting, might be very helpful, but two problems I am having with it though:

1) It doesn't seem to do the trick because:

MsgBox(Me.Bounds.Height & " | " & Me.Height)

Me.Bounds.Height returns the same int as Me.Height

2) If I can get the Bounds to represent the form plus titlebar, borders, etc. when does the onMouseLeave fire? When the mouse moves out of the bounds or out of the area of the form inside the bounds?

Avatar of majnun

ASKER

Ok this works almost perfectly (in the form's onMouseLeave):

    Dim X, Y, Xoffset, Yoffset, Mx, My As Integer
        Dim off As Boolean = False

        X = Me.ClientSize.Width
        Y = Me.ClientSize.Height

        Mx = Me.PointToClient(Me.MousePosition).X
        My = Me.PointToClient(Me.MousePosition).Y

        If Mx >= X Or Mx < 1 Then off = True
        If My >= Y Or My < 1 Then off = True

        If off Then popBack() 'resize


I say it works almost perfectly, because if I move the mouse out really fast it doesn't seem to trigger the onMouseLeave event... i'm assuming because the mouse position "jumps" over the boundary of the client area of the form and so never triggers the onMouseLeave event.
Avatar of majnun

ASKER

Woops, don't need the  Xoffset, Yoffset dimmed in the above.
Avatar of majnun

ASKER

ok, last thing to do i think is trigger the onMouseLeave event for the form on the onMouseEnter sub of the owning form... anyone know how I can trigger one form's events from another form?

Thanks!
Avatar of majnun

ASKER

ok, on second thought, incase i want to use this for non-owned forms, maybe i'll start a timer when the form is expanded and poll the mouse position every few seconds incase i moved off to fast, then destroy the timer when the form shrinks.
Avatar of majnun

ASKER

Ok, i've figured out a solution which works for me, and in case others find the particular execution useful here's the entirety:
In plain english:
The form (mediaPlayer) has a bunch of controls, one of the controls is a panel (pnlPlaylist) which is "hidden" below the forms border (i size the control so that the panel is not visible, but sits hidden "below" everything else). This panel is revealed when the form pops (expands and reveals the hidden panel). Based on the position of the form in relation to its parent form, it either pops up or pops down. If the form pops down the size increases and reveals the panel, if the form pops up the size increases, the panel jumps to the top of the form, and the other controls are repositioned to the bottom appropriately. I create an arraylist which holds control names which need to be repositioned to the bottom of the form when the form pops down (note i exclude the panel since it is positioned explicity). When the mouse moves out of the form its checks to make sure the mouse position is actually outside the form and then pops back to the smaller position. Just incase the user moves the mouse out to fast, a timer is created and started when the form pops bigger and poles the mouse position every two seconds and closes it if the mouse is off the form (the timer is also disposed whenever the form shrinks),

In code:
    Dim popHeight As Integer = 200
    Dim poppedDown As Boolean = False
    Dim poppedUp As Boolean = False
    Dim alControls As New ArrayList()
    Dim intPnlPlaylistTop As Integer
    Dim WithEvents tmrPop As System.Timers.Timer

    Sub tmrPop_Elapsed(ByVal sender As Object, _
                            ByVal e As System.Timers.ElapsedEventArgs) _
                            Handles tmrPop.Elapsed
        ' Respond to event
        popBack()
    End Sub

    Sub pop()
        If Me.Height < popHeight Then
            tmrPop = New System.Timers.Timer()
            tmrPop.Interval = 2000
            tmrPop.Start()
            If Me.Top < (Me.ParentForm.Size.Height / 2) Then
                popDown()
            Else
                popUp()
            End If
        End If
    End Sub

    Sub popDown()
        Me.Height = Me.Height + popHeight
        poppedDown = True
    End Sub

    Sub popUp()
        Dim ctr As Control
        Me.Height = Me.Height + popHeight
        For Each ctr In alControls
            ctr.Top = ctr.Top + popHeight
        Next
        intPnlPlaylistTop = pnlPlaylist.Top
        pnlPlaylist.Top = 10 'can't be at 0 because i need to pass over the form on the way out to trigger the onMouseLeave event
        Me.Top = Me.Top - popHeight
        poppedUp = True
    End Sub

    Sub popBack()
        Dim X, Y, Mx, My As Integer
        Dim off As Boolean = False

        X = Me.ClientSize.Width
        Y = Me.ClientSize.Height

        Mx = Me.PointToClient(Me.MousePosition).X
        My = Me.PointToClient(Me.MousePosition).Y

        If Mx >= X Or Mx < 1 Then off = True
        If My >= Y Or My < 1 Then off = True

        If off Then
            If poppedUp Or poppedDown Then
                tmrPop.Dispose()
            End If

            If poppedDown = True Then
                Me.Height = Me.Height - popHeight
                poppedDown = False
            End If

            If poppedUp = True Then
                Dim ctr As Control
                Me.Height = Me.Height - popHeight
                For Each ctr In alControls
                    ctr.Top = ctr.Top - popHeight
                Next
                pnlPlaylist.Top = intPnlPlaylistTop
                Me.Top = Me.Top + popHeight
                poppedUp = False
            End If

        End If
    End Sub

    Private Sub MediaPlayer_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'add all the controls to the alControls arraylist
        'use a case to check for control names not to add to arraylist
        Dim ctr As Control
        For Each ctr In Me.Controls
            Select Case ctr.Name
                Case "pnlPlaylist" 'controlName, controlName, etc. for controls not to add to the alControls arraylist
                Case Else
                    alControls.Add(ctr)
            End Select
        Next

    End Sub

    Private Sub lblPlaylist_MouseHover(ByVal sender As Object, ByVal e As System.EventArgs) Handles lblPlaylist.MouseHover
        pop()
    End Sub

    Public Sub MediaPlayer_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.MouseLeave
        popBack()
    End Sub



I will leave this thread open for awhile to see if anyone has come up with a more elegant solution, or has suggestions regarding this implementation.

Thanks!
Avatar of majnun

ASKER

MODERATOR:

Is it possible to change the title of the question to:
"Expanding/Contracting forms: onMouseOut problem"

Since i've included the whole solution in case someone is wondering how to do it?
ASKER CERTIFIED SOLUTION
Avatar of modulo
modulo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial