Link to home
Start Free TrialLog in
Avatar of balabaster
balabaster

asked on

Set window above other application windows

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
Avatar of shivsa
shivsa
Flag of United States of America image

r u asking programming solution.
ASKER CERTIFIED SOLUTION
Avatar of Dexstar
Dexstar

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
Avatar of balabaster
balabaster

ASKER

Visual Basic
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?
SOLUTION
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
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.
Just use

Form1.ZOrder 0

(where "Form1" is the form you want to place at top
SOLUTION
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
Rimvis's "Form1.ZOrder 0" might be used instead of the SetWindowPos if it has the same effect.
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.

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

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.
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.
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*
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?
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*
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)
Your main window doesn't have an OnActivateApp event, or anything like that?

Dex*
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.
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
@puneesh : Good job!  So, basically, in VB, you have to do it the hard way?  Geez.
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.
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 ;-)

I concur with the suggestion of a 3-way split.

-D*