tags:

views:

4323

answers:

5

Folks,

Please does anyone know how to show a Form from an otherwise invisible application, and have it get the focus (i.e. appear on top of other windows)? I'm working in C# .NET 3.5.

I suspect I've taken "completely the wrong approach"... I do not Application.Run(new TheForm ()) instead I (new TheForm()).ShowModal()... The Form is basically a modal dialogue, with a few check-boxes; a text-box, and OK and Cancel Buttons. The user ticks a checkbox and types in a description (or whatever) then presses OK, the form disappears and the process reads the user-input from the Form, Disposes it, and continues processing.

This works, except when the form is show it doesn't get the focus, instead it appears behind the "host" application, until you click on it in the taskbar (or whatever). This is a most annoying behaviour, which I predict will cause many "support calls", and the existing VB6 version doesn't have this problem, so I'm going backwards in usability... and users won't accept that (and nor should they).

So... I'm starting to think I need to rethink the whole shebang... I should show the form up front, as a "normal application" and attach the remainer of the processing to the OK-button-click event. It should work, But that will take time which I don't have (I'm already over time/budget)... so first I really need to try to make the current approach work... even by quick-and-dirty methods.

So please does anyone know how to "force" a .NET 3.5 Form (by fair means or fowl) to get the focus? I'm thinking "magic" windows API calls (I know

Twilight Zone: This only appears to be an issue at work, we're I'm using Visual Studio 2008 on Windows XP SP3... I've just failed to reproduce the problem with an SSCCE (see below) at home on Visual C# 2008 on Vista Ulimate... This works fine. Huh? WTF?

Also, I'd swear that at work yesterday showed the form when I ran the EXE, but not when F5'ed (or Ctrl-F5'ed) straight from the IDE (which I just put up with)... At home the form shows fine either way. Totaly confusterpating!

It may or may not be relevant, but Visual Studio crashed-and-burned this morning when the project was running in debug mode and editing the code "on the fly"... it got stuck what I presumed was an endless loop of error messages. The error message was something about "can't debug this project because it is not the current project, or something... So I just killed it off with process explorer. It started up again fine, and even offered to recover the "lost" file, an offer which I accepted.

using System;
using System.Windows.Forms;

namespace ShowFormOnTop {
    static class Program {
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());
            Form1 frm = new Form1();
            frm.ShowDialog();
        }
    }
}

Background: I'm porting an existing VB6 implementation to .NET... It's a "plugin" for a "client" GIS application called MapInfo. The existing client "worked invisibly" and my instructions are "to keep the new version as close as possible to the old version", which works well enough (after years of patching); it's just written in an unsupported language, so we need to port it.

About me: I'm pretty much a noob to C# and .NET generally, though I've got a bottoms wiping certificate, I have been a professional programmer for 10 years; So I sort of "know some stuff".

Any insights would be most welcome... and Thank you all for taking the time to read this far. Consiseness isn't (apparently) my forte.

Cheers. Keith.

+2  A: 

yourForm.TopMost = true;

I'll go give it a burl. Thank you for your suggestion. I do appreciate it.
corlettk
Simply setting <code>this.TopMost = true</code> in Form_Load works like a charm. Thank You, Thank You, and Thank You!
corlettk
+2  A: 

There's an overload of Form.ShowDialog() which takes an IWin32Window object. That IWin32Window is treated as the parent window for the form.

If you have the parent window as a System.Windows.Forms.Form, go ahead and just pass it. If not, get the HWND (maybe by P/Invoking to FindWindow()), and create a dummy IWin32Window implementation that just returns the HWND (More details).

ChrisV
There are no other windows in the application... just this "little dialogue" (to allow the user to enter some metadata for the update).
corlettk
+1  A: 
  1. You said that it works fine when you use Application.Run. Why don't you want to use Application.Run, then?
  2. Have you tried calling BringToFront() from OnLoad or OnShown?
P Daddy
I said that I'm not using Application.Run, I didn't say it makes the form visible. I didn't "Run" the app because this isn't fundamentally an "event driven"; it's a longish mainline which just needs to show a modal dialogue with a few controls... If I need to restructure the app to be "event-driven" then I'll do so... I didn't do so initially because it doesn't suit the problem. FIRST I'm hoping to find another solution to the form being shown "behind" the host GIS application. I shall try BringToFront() in the OnShow method (which are both new on me;-) I appreciate your help. Cheers. Keith.
corlettk
Sorry, I imagined you saying that Application.Run avoided the problem. Your objection to it is a bit odd, though. Forms receive events the same way whether you do Application.Run or Form.ShowDialog. In fact, the way in which these two work is almost (but not quite) identical. They both end up calling ThreadContext.FromCurrent().RunMessageLoop (ThreadContext is an internal class within System.Windows.Forms.dll).
P Daddy
Aha... My objections to Application.Run are based on pure untrammelled ignorance. I've just realise that Application.Run BLOCKS THE CALLING THREAD. (Forgive my shouting, there aren't many options for emphasis in SO comments). I really appreciate your advise, and I'll be off to work this morning to try it out. Sorry if I was a bit narky, it was late and I was very tired, and increasingly frustrated.
corlettk
A: 

It would appear that is behaviour is specific to XP... Hence I can't reproduce it on Vista.

http://www.gamedev.net/community/forums/topic.asp?topic_id=218484

EDIT: PS: It's past my bedtime (2 AM;-).

Thanx all for your responses... there's a "few things" I can try... I might even go into the office tomorrow to try them... Yeah, yeah... I had a life once, but I traded it for a haircut and a job ;-)

Cheers all. Keith.

corlettk
I'd vote down this (my own) answer if I could... I've just finished "totally verifying" that: form.TopMost=true; fixes the problem on XP (all our current+theNext SOE versions) and Vista. Lending yet more credence to the contention that "I'm @ Putz!"... Some days you just can't take a trick ;-)
corlettk
A: 

I've hacked this from an application I've been working on. We have a large application that loads a series of modules written by different teams. We have written one of these modules, and needed to have a login dialog open during this initialization. It was set to '.TopMost=true', but that didn't work.

It uses the WindowsFormsSynchronizationContext to open a dialog box, and then get the result of the dialog box back.

I rarely do GUI coding, and suspect this may be overkill, but it might help someone if they get stuck. I had problems with understanding how state is passed to the SendOrPostCallback, as all the examples I could find didn't use it.

Also this is taken from a working application, but I've removed several bits of code, and changed some of the details. Apologies if it doesn't compile.

public bool Dummy()
{

// create the login dialog
DummyDialogForm myDialog = new DummyDialogForm();

// How we show it depends on where we are. We might be in the main thread, or in a background thread
// (There may be better ways of doing this??)
if (SynchronizationContext.Current == null)
{
    // We are in the main thread. Just display the dialog
    DialogResult result = myDialog.ShowDialog();
    return result == DialogResult.OK;
}
else
{
    // Get the window handle of the main window of the calling process
    IntPtr windowHandle = Process.GetCurrentProcess().MainWindowHandle;

    if (windowHandle == IntPtr.Zero)
    {
        // No window displayed yet
        DialogResult result = myDialog.ShowDialog();
        return result == DialogResult.OK;
    }
    else
    {
        // Parent window exists on separate thread
        // We want the dialog box to appear in front of the main window in the calling program
        // We would like to be able to do 'myDialog.ShowDialog(windowHandleWrapper)', but that means doing something on the UI thread
        object resultState = null;
        WindowsFormsSynchronizationContext.Current.Send(
            new SendOrPostCallback(delegate(object state) { resultState = myDialog.ShowDialog(); }), resultState);

        if (resultState is DialogResult)
        {
            DialogResult result = (DialogResult) resultState;
            return result == DialogResult.OK;
        }
        else
            return false;

    }
}

}

alex