How to make a form ALWAYS ON TOP!
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 13 of 13

Thread: How to make a form ALWAYS ON TOP!

  1. #1
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    How to make a form ALWAYS ON TOP!

    Code:
        Const HWND_NOTOPMOST As Int32 = -2
        Const HWND_TOPMOST As Int32 = -1
        Const HWND_TOP As Int32 = 0
        Const HWND_BOTTOM As Int32 = 1
        Const SWP_NOSIZE As Int32 = 1
        Private Structure RECT
            Public rLeft, rTop, rRight, rBottom As Int32
        End Structure
        Private Declare Function apiGetTopWindow Lib "user32" Alias "GetTopWindow" (ByVal hWnd As Int32) As Int32
        Private Declare Function apiGetWindowRect Lib "user32" Alias "GetWindowRect" (ByVal hWnd As Int32, ByRef lpRect As RECT) As Boolean
        Private Declare Function apiSetWindowPos Lib "user32" Alias "SetWindowPos" (ByVal hWnd As Int32, ByVal hWndInsertAfter As Int32, ByVal X As Int32, ByVal Y As Int32, ByVal cx As Int32, ByVal cy As Int32, ByVal wFlags As Int32) As Boolean
        Private Declare Function apiLockWindowUpdate Lib "user32" Alias "LockWindowUpdate" (ByVal hWndLock As Int32) As Int32
        Private t As New Threading.Thread(AddressOf KeepOnTop)
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            t.Start() '''''''''''''''''''''''''''''Start thread to keep top most status
        End Sub
        Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
            t.Abort() ''''''''''''''''''''''''''''''''Abort keeping the window topmost
        End Sub
        Private Sub KeepOnTop()
            Dim hwnd As Int32, r As New RECT
            Do
                hwnd = Me.Handle.ToInt32
                apiLockWindowUpdate(0)'''''''''''''''''''''''''''Unlock any previous calls
                apiLockWindowUpdate(hwnd) ''''''''''''''''''''Lock the desktop from updating for a moment, so that tooltips dont get stranded
                If apiGetTopWindow(HWND_TOP) <> hwnd Then ''''If the topmost window is not the specified window
                    apiGetWindowRect(hwnd, r) ''''''''''''''''Get rectangular dimensions of the specified window
                    apiSetWindowPos(hwnd, HWND_TOPMOST, r.rLeft, r.rTop, 0, 0, SWP_NOSIZE) 'Set the specified window as the topmost
                    apiLockWindowUpdate(0) '''''''''''''''''''''''Unlock desktop so that it can update itself visually
                End If
                System.Threading.Thread.Sleep(1) '''''''''''''Sleep for the shortest period possible
            Loop
        End Sub
    Last edited by TT(n); January 19th, 2008 at 01:47 AM. Reason: extra description

  2. #2
    Join Date
    Nov 2006
    Posts
    20

    Re: How to make a form ALWAYS ON TOP!

    No workie in 2005, generates a cross-thread error.

  3. #3
    Join Date
    Nov 2006
    Posts
    20

    Re: How to make a form ALWAYS ON TOP!

    This takes care of the cross threading issue, using Delegates. Is this the best way to do this in this example? Aside from using the SetWindowPos API to set the form on top

    Code:
    Public Class Form1
    
        Public Declare Function apiFindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer
        Public Declare Function apiGetTopWindow Lib "user32" Alias "GetTopWindow" (ByVal hwnd As Integer) As Integer
        Public Declare Function apiGetDesktopWindow Lib "user32" Alias "GetDesktopWindow" () As Integer
        Dim rTopMost As New System.Threading.Thread(AddressOf ReturnTopMost) 'Declare thread for returning topmost
    
        Delegate Function HandleDelegate() As Int32
        Public hndl As HandleDelegate
    
        Delegate Sub setTopMostDelegate()
        Public setTopMost As setTopMostDelegate
    
        Delegate Sub setRefreshDelegate()
        Public setRefresh As setRefreshDelegate
    
        Delegate Function getTextDelegate() As String
        Public getText As getTextDelegate
    
        Public Sub setTopMostSub()
            Me.TopMost = True
        End Sub
    
        Public Sub setRefreshSub()
            Me.Refresh()
        End Sub
    
        Public Function getHandleFunc() As Int32
            Return Me.Handle.ToInt32
        End Function
    
        Public Function getTextSub() As String
            Return Me.Text.ToString
        End Function
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            hndl = New HandleDelegate(AddressOf getHandleFunc)
            setTopMost = New setTopMostDelegate(AddressOf setTopMostSub)
            setRefresh = New setRefreshDelegate(AddressOf setRefreshSub)
            getText = New getTextDelegate(AddressOf getTextSub)
    
            rTopMost.Start() 'Start Return Top Most thread.
        End Sub
    
        Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
            rTopMost.Abort() 'Abort TopMost thread if the main form is closed 
        End Sub
    
        Public Sub ReturnTopMost()
    
            Dim iHndl As Int32
    
            iHndl = Me.Invoke(hndl)
    
            Try
                Do
                    System.Threading.Thread.Sleep(1) 'Sleep for a short period
                    If apiGetTopWindow(apiGetDesktopWindow) <> iHndl Then
                        Me.Invoke(setTopMost)
                        Me.Invoke(setRefresh)
                    End If
                    apiFindWindow(vbNullString, Me.Invoke(getText))  'This throws an error if the main window has been terminated.
                Loop
            Catch ex As Exception
                MessageBox.Show(ex.Message)
            End Try
    
        End Sub
    
    End Class
    Last edited by Cutha; November 14th, 2006 at 06:28 PM.

  4. #4
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    Thanks, it looks pretty good!
    I think SetWindowPos, would be better for use with external forms.
    A bit more more work perhaps.

  5. #5
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    OOoops, Cutha you must of looked at the example after I edited it.

    The refresh should be in the loop every time. Sorry.
    Check to make sure it does not consume cycles in an idle state.
    For example Sleep(0), uses a bunch.


    Change the ReturnTopMost sub:
    Code:
       Public Sub ReturnTopMost()
    
            Dim iHndl As Int32
    
            iHndl = Me.Invoke(hndl)
    
            Try
                Do
                    System.Threading.Thread.Sleep(1) 'Sleep for a short period
                    If apiGetTopWindow(apiGetDesktopWindow) <> iHndl Then
                        Me.Invoke(setTopMost)
                    End If
                        Me.Invoke(setRefresh)
                    apiFindWindow(vbNullString, Me.Invoke(getText))  'This throws an error if the main window has been terminated.
                Loop
            Catch ex As Exception
                MessageBox.Show(ex.Message)
            End Try
    
        End Sub

  6. #6
    Join Date
    Feb 2005
    Location
    Israel
    Posts
    1,475

    Re: How to make a form ALWAYS ON TOP!

    You did it right by using Invoke.

    I just have a non-relevant comment that I couldn't stop myself from writing: Don't use On Error GoTo. This is VB6 syntax. Never use GoTo and Labels. Use exceptions instead (Try...Catch)

  7. #7
    Join Date
    Oct 2003
    Location
    .NET2.0 / VS2005 Developer
    Posts
    7,104

    Re: How to make a form ALWAYS ON TOP!

    You guys like making stuff hard work, eh?

    Code:
    Dim f as New Form()
    f.TopMost = True
    f.Show()
    "it's a fax from your dog, Mr Dansworth. It looks like your cat" - Gary Larson...DW1: Data Walkthroughs 1.1...DW2: Data Walkthroughs 2.0...DDS: The DataSet Designer Surface...ANO: ADO.NET2 Orientation...DAN: Deeper ADO.NET...DNU...PQ

  8. #8
    Join Date
    Feb 2005
    Location
    Israel
    Posts
    1,475

    Re: How to make a form ALWAYS ON TOP!

    cjard, I think that you missed the point. If you want to set the TopMost from another thread you MUST call Invoke, or yu will get an exception.
    Why does TT(n) really need to call the property from a different thread? Well, that's a different question.

  9. #9
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    ERrr... what guys?


    jhammer,
    Invoke was by Cutha's modification for 2005.
    I know full well about GoTo, but in this case it was aborting the thread if the main window is closed(purposefully, or accidentally). Try that out.
    I figured there would be confusion, about why I was using it that way.
    Also, calling it from another thread makes it more responsive.

    cjard,
    Nope your wrong, that only sets it to the top, it doesnt keep it there if other programs, like ATI use the ALWAYSONTOP option.
    ATI, and other applications refresh themselves, after we try calling the simple Me.TopMost = true.
    Other people have complained about this and I haven't seen a solution until now.

    As you can see 2003, allows you to do it elegantly. Hoorahh! for 2003!

  10. #10
    Join Date
    Feb 2005
    Location
    Israel
    Posts
    1,475

    Talking Re: How to make a form ALWAYS ON TOP!

    Quote Originally Posted by TT(n)
    jhammer,
    Invoke was by Cutha's modification for 2005.
    I know full well about GoTo, but in this case it was aborting the thread if the main window is closed(purposefully, or accidentally). Try that out.
    I figured there would be confusion, about why I was using it that way.
    Also, calling it from another thread makes it more responsive.
    About the Invoke, maybe I misunderstood. Let me clarify it, when you want to change a GUI from a different thread, you cannot just call the method or property, because you only perform GUI operations from the GUI thread (in 2005 you will even get exceptions for it, in 2003 you might face bugs). I thought that you asked if this is way to go when dealing with this situation. Sorry if I misunderstood.

    About the On Error Goto issue.
    Here is how I change your code to use Try...Catch:
    Code:
    Public Sub ReturnTopMost()
         Try
            Do
                System.Threading.Thread.Sleep(25) 'Sleep for a short period
                If apiGetTopWindow(apiGetDesktopWindow) <> Me.Handle.ToInt32 Then
                    Me.TopMost = True   'Set TopMost to true if our handle is no longer topmost.
    
                    'If the form is an external application, insert it's handle into the SetWindowPos API
                    'apiSetWindowPos(Me.Handle.ToInt32, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE)
                End If
                Me.Refresh()
                apiFindWindow(vbNullString, Me.Text.ToString)  'This throws an error if the main window has been terminated.
            Loop
         Catch ex As Exception
              Exit sub
         End try
        End Sub
    As you can see, no need for the Goto thing.

    It was just a comment. No hard feelings.

  11. #11
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    No hard feelings at all.
    I agree to endorse Try Catch in many situations.
    I think it's somewhat trivial here, to use one or the other, but in order to stick to the convention, use try catch instead.


    Try
    Catch ex As Exception
    Exit sub
    End try
    On Error GoTo ExitSub
    ExitSub:

  12. #12
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    No workie in 2005, generates a cross-thread error.
    Cutha bro, your gonna laugh....

    Your were debugging it when the threading error occured.
    That's because VB is running the "process.vshost", rather than the "process".
    Causing a debug error, but not an error in the actual executable file that is built.


    Nice work with delegates though.

  13. #13
    Join Date
    Jun 2004
    Location
    NH
    Posts
    674

    Re: How to make a form ALWAYS ON TOP!

    I've updated the example in this thread, to allow multiple forms to be on top.(Any group of windows you specify with FindWindow) Or bottom.

    It can be found here

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center