Solved

Set window above other application windows

Posted on 2003-12-10
28
941 Views
Last Modified: 2012-08-13
Hi,

I'm trying to get a window in my application to remain above all other windows in my application.  Can this be done?  If so, how?  I don't want to get my windows to stay above ALL other open applications, just the parent application.

Thanks,

Ben
0
Comment
Question by:balabaster
  • 7
  • 6
  • 5
  • +3
28 Comments
 
LVL 24

Expert Comment

by:shivsa
Comment Utility
r u asking programming solution.
0
 
LVL 19

Accepted Solution

by:
Dexstar earned 43 total points
Comment Utility
@balabaster:

> I'm trying to get a window in my application to remain above all other windows
> in my application.  Can this be done?  If so, how?  I don't want to get my
> windows to stay above ALL other open applications, just the parent application.

You didn't say what platform you are using.  On the off chance you are working with Windows, I'll say this:  In general, you use the Windows API "SetWindowPos", and specify the Z order as HWND_TOPMOST.

Here is the documentation on that API:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Windows/WindowReference/WindowFunctions/SetWindowPos.asp

What language are you using?  I'll give you a sample.

Hope That Helps,
Dex*
0
 

Author Comment

by:balabaster
Comment Utility
Visual Basic
0
 

Author Comment

by:balabaster
Comment Utility
The HWND_TOPMOST sets the window above ALL other windows and applications, I want to specify that it be the topmost window only in my application.  Can that be done?
0
 
LVL 1

Assisted Solution

by:puneesh
puneesh earned 41 total points
Comment Utility
Put following lines of code in the form you want to keep on top within your application.

Private Sub Form_Deactivate()
    SetWindowPos Me.hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE
End Sub

alternatively you can use

Private Sub Form_Deactivate()
    Me.Zorder 0
End Sub

Both event procedures will place your form on top in the Zorder within your application.
0
 

Author Comment

by:balabaster
Comment Utility
When I use that, it prevents me from using the other forms in my application...I want it displayed on top only, I don't want it preventing me from using the other windows.
0
 
LVL 19

Expert Comment

by:Rimvis
Comment Utility
Just use

Form1.ZOrder 0

(where "Form1" is the form you want to place at top
0
 
LVL 3

Assisted Solution

by:_Gerry_
_Gerry_ earned 41 total points
Comment Utility
Had this problem myself some years ago. The only way I found was to run a timer on the form which called API function SetWindowPos. E.g.

Private Sub timer1_Timer()
   SetWindowPos Me.hwnd, HWND_TOP, 0,0,0,0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE
End Sub

I set up timer for 20 milliseconds. HWND_TOP just puts window above all others in this window's application. SWP_NOACTIVATE stops the window forever grabbing focus.

It's a horrible bodge, but the only way I found of doing it - anyone out there has a better way I'd like to know.

Hope this helps,

Gerry
0
 
LVL 3

Expert Comment

by:_Gerry_
Comment Utility
Rimvis's "Form1.ZOrder 0" might be used instead of the SetWindowPos if it has the same effect.
0
 
LVL 19

Expert Comment

by:Rimvis
Comment Utility
Is you application MDI? I gues _Gerry_'s  answer won't work in that case (I don't know an answer either :/)

And _Gerry_, why to use timer? I works perfectly without it. Just put it in Form_Load.

0
 
LVL 1

Expert Comment

by:puneesh
Comment Utility
Use this

Private Sub Form_Deactivate()
    SetWindowPos Me.hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE Or SWP_NOSIZE
End Sub

0
 
LVL 3

Expert Comment

by:_Gerry_
Comment Utility
This does not work puneesh.
The Deactivate() event is fired when the window in question loses (or is about to lose) focus to any other window on your desktop.
This is just the once. Works fine if you're only ever clicking between your one-other-window app's window and the target child window you want on top. What if you click on another window somewhere then back to the not-on-top window in your app. It goes over the window we want displayed. There are all sorts of other rela-world scenarios that upset this solution.
If you have only one or two other windows in your app an alternative is to pick up the Form_Activate() events for these instead (or as well if you like to be thorough) and do a SetWindowPos for the target window. This way, whenever you click on any window in your application the window we want gets shoved to the front - hopefully. If your app is a bit complex (mine had loads) then its either a lot of fiddly coding or use a timer.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:balabaster
Comment Utility
Here is the full scenario, I have an MDI application that loads multiple documents, the main window is the MDI form (obviously) and the documents are MDI children, I then have a project explorer window (much like the project explorer window in Visual Studio - except that I couldn't find a way to get it to dock nicely like they did with Visual Studio) and I have a tool window which allows you to manipulate the data in the documents.  Neither of these windows are MDI children.  Ideally, I could make both of the tool windows MDI children too and dock them, but in the event I can't do that, I need to make sure they stay on top of my application.  Setting the window position using [SetWindowPos Me.hWnd, -1, 0, 0, 0, 0, &H1 Or &H2] in the form_load event has the desired effect, but annoyingly when you switch to another application the explorer window and tool window remain on top thus getting in your way.  I thought about setting them to BOTTOM_MOST when the main application loses focus, but of course every time the tool windows take focus, the MDI_Form loses focus anyway, so that threw that idea out the window.

If anyone can provide a tidy solution for this, I'll gladly up the points to 250.
0
 
LVL 19

Expert Comment

by:Dexstar
Comment Utility
When your main window loses focus, you can tell what window is gaining the focus.  When it loses the focus, and the focus isn't being gained by one of your tool windows, then you should make the tool windows invisible.  Then when you're application gets the focus again, you can make them visible again.

Actually, you should do that check if any of the tool windows or the main window lose the focus.  See if the focus went to another window in your app, or a different app.  If it is a different app, then hide the tool windows.

Dex*
0
 

Author Comment

by:balabaster
Comment Utility
that sounds viable, how do I tell if it is another of my application windows that are gaining focus as opposed to another application altogheter?
0
 
LVL 19

Expert Comment

by:Dexstar
Comment Utility
I'm not a VB person, so I'm not sure how to do it in VB.  I know that when you get the WM_KILLFOCUS, the wparam is the HWND of the window that received the focus.  So, you can compare that value to the hwnd of your other windows to see if it is the same one.  

Could someone please explain how to do that in VB?

Also, WM_ACTIVATEAPP is sent to each program when a window belonging to a different application is activated (it is set both to the app being activated, and de-activated).  So, when your app is de-activated, hide the tool windows, and when it is being activated, show them again.

Dex*
0
 

Author Comment

by:balabaster
Comment Utility
Anyone got a VB solution?  I never really got into API programming, so this is really my first step into the API world (exciting huh? hehe)
0
 
LVL 19

Expert Comment

by:Dexstar
Comment Utility
Your main window doesn't have an OnActivateApp event, or anything like that?

Dex*
0
 

Author Comment

by:balabaster
Comment Utility
In VB the MDI form only has Form_Activate and Form_Deactivate events...if the window does have OnActivateApp and OnDeactivateApp then I don't know where to find it.
0
 
LVL 1

Expert Comment

by:puneesh
Comment Utility
For capturing WM_ACTIVATEAPP and all other WINDOWS MESSAGES, you've to use Sub-Classing in VB. Whenever any action is taken on any window, Windows send appropirate message to that Window and the default WindowProc for that window gets executed. In Sub-Classing you create your own function and replaces it with default Procedure of you application Window.

Two API's are used for Sub-classing:
-----------------------------------------
SetWindowLong
-----------------------------------------
The SetWindowLong function changes an attribute of the specified window. Calling SetWindowLong with the GWL_WNDPROC index creates a subclass of  the window class used to create the window. An application should not
subclass a window created by another process. The SetWindowLong function creates the window subclass by changing the window procedure associated with a particular window, causing Windows to call the new window procedure instead of the previous one. An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc. This allows the application to create a chain of window procedures.

Parameters
hwnd - Identifies the window to which the window belongs.
nIndex - Set to GWL_WNDPROC to set a new address for the window procedure.
dwNewLong - Address of the new window procedure.

-----------------------------------------
CallWindowProc
-----------------------------------------
The CallWindowProc function passes message information to the specified window procedure. Use this function for window subclassing. Usually, all windows with the same class share one window procedure. A subclass is a window or set of windows with the same class whose messages are intercepted and processed by another window procedure before being passed to the window procedure of the class.

Parameters:
lpPrevWndFunc - Pointer to the previous window procedure.
                If this value is obtained by calling the GetWindowLong function
                with the nIndex parameter set to GWL_WNDPROC, it is the address
                of a window or a handle representing that address.

hwnd - Identifies the window procedure to receive the message.

Msg - Specifies the message.
wParam - Depends on the value of the Msg parameter.
lParam - Depends on the value of the Msg parameter.

-----------------------------------------------------------------------
Implementation of Sub-Classing
-----------------------------------------------------------------------
STEP 1: Add a module and copy following code in it.

Option Explicit
Public glPrevWndProc As Long

Public Const GWL_WNDPROC = (-4)
Public Const WM_EXITSIZEMOVE = &H232
Public Const WM_MOUSEMOVE = &H200
Public Const WM_LBUTTONDOWN = &H201
Public Const WM_NCLBUTTONDOWN = &HA1
Public Const WM_ACTIVATEAPP = &H1C

Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Sub pUnSubClass()
Call SetWindowLong(Form1.hwnd, GWL_WNDPROC, glPrevWndProc)
End Sub


Public Function pMyWindowProc(ByVal hw As Long, ByVal uMsg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
If Form1.optMsg(0).Value And uMsg = WM_MOUSEMOVE Then
    Debug.Print "Mouse Moved"
    Exit Function
End If

If Form1.optMsg(1).Value And uMsg = WM_EXITSIZEMOVE Then
    Debug.Print "Done Resizing"
    Exit Function
End If

If Form1.optMsg(2).Value And uMsg = WM_LBUTTONDOWN Then
    Debug.Print "Left Button Down on Form"
    Exit Function
End If

If Form1.optMsg(3).Value And uMsg = WM_NCLBUTTONDOWN Then
    Debug.Print "Left Button Down on Caption"
    Exit Function
End If

If Form1.optMsg(4).Value And uMsg = WM_ACTIVATEAPP Then
    Debug.Print "Left Button Down on Caption"
    Exit Function
End If

pMyWindowProc = CallWindowProc(glPrevWndProc, hw, uMsg, wParam, lParam)
End Function

Public Function fSubClass() As Long
fSubClass = SetWindowLong(Form1.hwnd, GWL_WNDPROC, AddressOf pMyWindowProc)
End Function

STEP 2: Add a Form. Draw a control array of 5 elements of OptionButton. Name it optMsg and set caption property as follows
optMsg(0)    Mouse Move Anywhere Over the Form
optMsg(1)    Finished Sizing or Moving Form  (NOTE: Disables Resize Event)
optMsg(2)    Left Mouse Button Down on Form's Client Area
optMsg(3)    Left Mouse Button Down on Non-Client Area (i.e. Caption)
optMsg(4)    Window Activate

Draw 3 command buttons and name-caption them cmdSub-Sub Class, cmdUnSub-Un Sub Class and cmdQuit-Quit respectively and Copy following code

Option Explicit
Private Sub cmdQuit_Click()

' This is critical!!! You must stop trapping Windows messages prior to exiting.

On Error Resume Next
Call pUnSubClass
Unload Me
End Sub

Private Sub cmdSub_Click()

' Hook into the message stream.

glPrevWndProc = fSubClass()
End Sub

Private Sub cmdUnSub_Click()

' Stop intercepting messages.

Call pUnSubClass
End Sub

Private Sub Form_Load()
optMsg(1).Value = True
End Sub
0
 
LVL 19

Expert Comment

by:Dexstar
Comment Utility
@puneesh : Good job!  So, basically, in VB, you have to do it the hard way?  Geez.
0
 
LVL 3

Expert Comment

by:_Gerry_
Comment Utility
Not really. Whenever any VB application activates ONE of it's windows is going to catch a VB Form_Activate() event. It has to if the application has any windows at all. So all you have to do is put a call to set the tool window's position to the top whenever ANY window in your application receives a Form_Activate() event. The HWND_TOP ( Form.ZOrder) only puts the tool window to the top of your Applications window tree so when you ALT-TAB to another application or whatever the whole app is backgrounded. As soon as you go back to your application one of its windows is always going to get focus and receive a Form_Activate() event, voila (pardon my French).
I really don't understand why you need to go to all the trouble of subclassing etc. to catch an OnActivateApp event when you can catch individual window activations a little further down the line a lot more easily.
The application I used this in is identical to yours in principle, I had an MDI app which shows maps and I wanted a little navigator window to always show over the top of whichever map was being shown. The only reason I ever used a timer was really because I was very lazy and short of time and couldn't be bothered to individually catch all the window (Form) activations by themseleves. So I just timered it so it stayed on top whatever - simple, dirty but effective.
0
 
LVL 3

Expert Comment

by:_Gerry_
Comment Utility
This was a lively old question that at least two of us spent no small amount of time trying to answer. Should split between puneesh & myself and perhaps dexstar (even though I think I had the best answer ;-)

0
 
LVL 19

Expert Comment

by:Dexstar
Comment Utility
I concur with the suggestion of a 3-way split.

-D*
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

This is an explanation of a simple data model to help parse a JSON feed
If you’re thinking to yourself “That description sounds a lot like two people doing the work that one could accomplish,” you’re not alone.
In this fifth video of the Xpdf series, we discuss and demonstrate the PDFdetach utility, which is able to list and, more importantly, extract attachments that are embedded in PDF files. It does this via a command line interface, making it suitable …
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now