Ein Formular anzeigen, ohne den Fokus zu stehlen?


140

Ich verwende ein Formular, um Benachrichtigungen anzuzeigen (es wird unten rechts auf dem Bildschirm angezeigt), aber wenn ich dieses Formular anzeige, wird der Fokus vom Hauptformular gestohlen. Gibt es eine Möglichkeit, dieses "Benachrichtigungs" -Formular anzuzeigen, ohne den Fokus zu stehlen?

Antworten:


165

Hmmm, überschreibt Form.ShowWithoutActivation nicht einfach genug?

protected override bool ShowWithoutActivation
{
  get { return true; }
}

Wenn der Benutzer auch nicht auf dieses Benachrichtigungsfenster klicken soll, können Sie CreateParams überschreiben:

protected override CreateParams CreateParams
{
  get
  {
    CreateParams baseParams = base.CreateParams;

    const int WS_EX_NOACTIVATE = 0x08000000;
    const int WS_EX_TOOLWINDOW = 0x00000080;
    baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );

    return baseParams;
  }
}

3
ShowWithoutActivation, kann nicht glauben, dass ich es nicht gefunden habe, verschwendet einen ganzen Nachmittag!
Deerchao

2
Ich musste auch einstellen form1.Enabled = false, um zu verhindern, dass innere Kontrollen den Fokus stehlen
Jader Dias

23
Und lassen Sie TopMost aus.
Mklein

4
Und wenn Sie TopMost möchten, lesen Sie die andere Antwort .
Roman Starkov

2
Die Werte WS_EX_NOACTIVATEund WS_EX_TOOLWINDOWsind 0x08000000und 0x00000080jeweils.
Juan

69

Aus der ShowWindow - Methode von PInvoke.net gestohlen :

private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;

[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
     int hWnd,             // Window handle
     int hWndInsertAfter,  // Placement-order handle
     int X,                // Horizontal position
     int Y,                // Vertical position
     int cx,               // Width
     int cy,               // Height
     uint uFlags);         // Window positioning flags

[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

static void ShowInactiveTopmost(Form frm)
{
     ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
     SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
     frm.Left, frm.Top, frm.Width, frm.Height,
     SWP_NOACTIVATE);
}

(Alex Lyman antwortete darauf, ich erweitere es nur, indem ich den Code direkt einfüge. Jemand mit Bearbeitungsrechten kann ihn dort kopieren und löschen, soweit es mich interessiert;))


Ich habe mich gefragt, braucht er das wirklich, wenn das Formular, das er unten links auf seinem Bildschirm anzeigt, in einem anderen Thread ist?
Patrick Desjardins

50
Ich finde es unglaublich, dass wir immer noch auf externe DLL-Dateien verlinken müssen, um mit Formularen zu interagieren. Wir sind bei .NET Framework Version 4 !! Zeit, es zu verpacken Microsoft.
Maltrap

9
Die akzeptierte Antwort ist falsch. Suchen Sie nach ShowWithoutActivation
mklein

Fügen Sie einfach frm.Hide () hinzu; am Anfang der ShowInactiveTopmost-Funktion, wenn Sie möchten, dass Ihr Formular nicht direkt fokussiert wird. Vergessen Sie nicht: using System.Runtime.InteropServices; um diesen Code zum Laufen zu bringen
Zitun

1
@Talha Dieser Code hat nichts mit dem Load-Ereignis zu tun. Das Ladeereignis wird ausgelöst, wenn das Formular geladen wird, nicht wenn es angezeigt wird.
TheSoftwareJedi


12

Das hat bei mir funktioniert. Es bietet TopMost, jedoch ohne Fokus zu stehlen.

    protected override bool ShowWithoutActivation
    {
       get { return true; }
    }

    private const int WS_EX_TOPMOST = 0x00000008;
    protected override CreateParams CreateParams
    {
       get
       {
          CreateParams createParams = base.CreateParams;
          createParams.ExStyle |= WS_EX_TOPMOST;
          return createParams;
       }
    }

Denken Sie daran, die Einstellung von TopMost im Visual Studio-Designer oder anderswo wegzulassen.

Dies wird von hier gestohlen, ähm, ausgeliehen (klicken Sie auf Problemumgehungen):

https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost


1
Topmost + unfocused funktioniert und es sieht aus wie die sauberste aller Antworten.
Feos

Topmost ist seit Windows 8 veraltet, wo Microsoft Sie bei der Verwendung bestraft. Der Effekt ist, dass Windows nach dem Öffnen und Schließen eines obersten Fensters die anderen Fenster Ihrer Anwendung in den Hintergrund rückt. Dies ist sicherlich nicht das gewünschte Verhalten für Ihre Anwendung. Microsoft hat dies implementiert, weil in der Vergangenheit viele Programmierer das sehr aufdringliche oberste missbraucht haben. Topmost ist sehr aggressiv. Ich benutze es nie.
Elmue

9

Dies zu tun scheint ein Hack zu sein, aber es scheint zu funktionieren:

this.TopMost = true;  // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top

Bearbeiten: Beachten Sie, dass dadurch lediglich ein bereits erstelltes Formular ausgelöst wird, ohne den Fokus zu stehlen.


scheint hier nicht zu funktionieren ... könnte sein, dass dieses "Benachrichtigungsformular" in einem anderen Thread geöffnet ist?
Matías

1
In diesem Fall müssen Sie wahrscheinlich einen Aufruf von this.Invoke () ausführen, um die Methode erneut als richtigen Thread aufzurufen. Im Allgemeinen wird beim Arbeiten mit Formularen aus dem falschen Thread eine Ausnahme ausgelöst.
Matthew Scharley

Während dies funktioniert, ist es eine Hacky-Methode, wie erwähnt, und hat BSOD für mich unter bestimmten Bedingungen verursacht, also hüte dich davor.
Raphael Smit

Topmost ist seit Windows 8 veraltet, wo Microsoft Sie bei der Verwendung bestraft. Der Effekt ist, dass Windows nach dem Öffnen und Schließen eines obersten Fensters die anderen Fenster Ihrer Anwendung in den Hintergrund rückt. Dies ist sicherlich nicht das gewünschte Verhalten für Ihre Anwendung. Microsoft hat dies implementiert, weil in der Vergangenheit viele Programmierer das sehr aufdringliche oberste missbraucht haben. Topmost ist sehr aggressiv. Ich benutze es nie.
Elmue

9

Der Beispielcode von pinvoke.net in den Antworten von Alex Lyman / TheSoftwareJedi macht das Fenster zu einem "obersten" Fenster, was bedeutet, dass Sie es nach dem Auftauchen nicht hinter normale Fenster stellen können. Angesichts der Beschreibung von Matias, wofür er dies verwenden möchte, könnte dies das sein, was er möchte. Wenn Sie jedoch möchten, dass der Benutzer Ihr Fenster nach dem Öffnen hinter andere Fenster stellen kann, verwenden Sie im Beispiel einfach HWND_TOP (0) anstelle von HWND_TOPMOST (-1).


6

In WPF können Sie es folgendermaßen lösen:

In das Fenster geben Sie diese Attribute ein:

<Window
    x:Class="myApplication.winNotification"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Notification Popup" Width="300" SizeToContent="Height"
  WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>

Das letzte Attribut ist das, das Sie benötigen ShowActivated = "False".


4

Ich habe etwas Ähnliches und zeige einfach das Benachrichtigungsformular und mache es dann

this.Focus();

um den Fokus wieder auf die Hauptform zu bringen.


Einfach aber effektiv!
Tom

3

Erstellen und starten Sie das Benachrichtigungsformular in einem separaten Thread und setzen Sie den Fokus nach dem Öffnen des Formulars wieder auf Ihr Hauptformular zurück. Lassen Sie das Benachrichtigungsformular ein OnFormOpened-Ereignis bereitstellen, das vom Form.ShownEreignis ausgelöst wird . Etwas wie das:

private void StartNotfication()
{
  Thread th = new Thread(new ThreadStart(delegate
  {
    NotificationForm frm = new NotificationForm();
    frm.OnFormOpen += NotificationOpened;
    frm.ShowDialog();
  }));
  th.Name = "NotificationForm";
  th.Start();
} 

private void NotificationOpened()
{
   this.Focus(); // Put focus back on the original calling Form
}

Sie können auch ein Handle für Ihr NotifcationForm-Objekt behalten, damit es vom Hauptformular programmgesteuert geschlossen werden kann (frm.Close() ) .

Einige Details fehlen, aber hoffentlich bringen Sie dies in die richtige Richtung.


Dies funktioniert nur, wenn Ihr Formular das ursprünglich aktive Formular war. Diese Art widerspricht dem Hauptzweck dieser Art der Benachrichtigung.
Alex Lyman

1
Huh? Dies ist der Zweck der Benachrichtigung - sie aufzustellen und sich wieder auf das ursprünglich aktive Formular zu konzentrieren.
Bob Nadler

2
Dies gibt nur den Fokus auf ein Formular in Ihrer Anwendung - was ist, wenn gerade ein anderes Programm aktiv ist? Das Anzeigen eines Benachrichtigungsfensters (normalerweise, um den Benutzer über den Status Ihrer Anwendung zu informieren) ist nur dann wirklich nützlich, wenn er Ihre Anwendung nicht überwacht.
Alex Lyman

3

Möglicherweise möchten Sie überlegen, welche Art von Benachrichtigung Sie anzeigen möchten.

Wenn es unbedingt erforderlich ist, den Benutzer über ein Ereignis zu informieren, ist die Verwendung von Messagebox.Show aufgrund seiner Natur die empfohlene Methode, um andere Ereignisse im Hauptfenster zu blockieren, bis der Benutzer dies bestätigt. Seien Sie sich jedoch der Popup-Blindheit bewusst.

Wenn dies weniger als kritisch ist, möchten Sie möglicherweise eine alternative Methode zum Anzeigen von Benachrichtigungen verwenden, z. B. eine Symbolleiste am unteren Rand des Fensters. Sie haben geschrieben, dass Sie Benachrichtigungen unten rechts auf dem Bildschirm anzeigen. Die Standardmethode hierfür ist die Verwendung einer Ballonspitze mit der Kombination eines Taskleistensymbols .


2
- Ballontipps sind keine Option, da sie deaktiviert werden können. - Die Statusleiste könnte ausgeblendet werden, wenn Sie das Programm minimiert haben. Trotzdem danke für Ihre Empfehlungen
Matías

3

Das funktioniert gut.

Siehe: OpenIcon - MSDN und SetForegroundWindow - MSDN

using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static void ActivateInstance()
{
    IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;

    // Restore the program.
    bool result = OpenIcon(hWnd); 
    // Activate the application.
    result = SetForegroundWindow(hWnd);

    // End the current instance of the application.
    //System.Environment.Exit(0);    
}

1

Du kannst logisch damit umgehen, obwohl ich zugeben muss, dass die obigen Vorschläge, bei denen Sie eine BringToFront-Methode erhalten, ohne den Fokus zu stehlen, die elegantesten sind.

Jedenfalls bin ich darauf gestoßen und habe es mithilfe einer DateTime-Eigenschaft gelöst, um weitere BringToFront-Aufrufe nicht zuzulassen, wenn bereits kürzlich Anrufe getätigt wurden.

Angenommen, eine Kernklasse, 'Core', die beispielsweise drei Formulare, 'Form1, 2 und 3', verarbeitet. Jedes Formular benötigt eine DateTime-Eigenschaft und ein Activate-Ereignis, die Core aufrufen, um Fenster nach vorne zu bringen:

internal static DateTime LastBringToFrontTime { get; set; }

private void Form1_Activated(object sender, EventArgs e)
{
    var eventTime = DateTime.Now;
    if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
        Core.BringAllToFront(this);
    LastBringToFrontTime = eventTime;
}

Und dann erstellen Sie die Arbeit in der Kernklasse:

internal static void BringAllToFront(Form inForm)
{
    Form1.BringToFront();
    Form2.BringToFront();
    Form3.BringToFront();
    inForm.Focus();
}

Nebenbei bemerkt, wenn Sie ein minimiertes Fenster in seinen ursprünglichen Zustand zurückversetzen möchten (nicht maximiert), verwenden Sie:

inForm.WindowState = FormWindowState.Normal;

Auch hier weiß ich, dass dies nur eine Patch-Lösung ist, da kein BringToFrontWithoutFocus vorhanden ist. Es ist als Vorschlag gedacht, wenn Sie die DLL-Datei vermeiden möchten.


1

Ich weiß nicht, ob dies als Necro-Posting angesehen wird, aber das habe ich getan, da ich es mit den Methoden "ShowWindow" und "SetWindowPos" von user32 nicht zum Laufen bringen konnte. Und nein, das Überschreiben von "ShowWithoutActivation" funktioniert in diesem Fall nicht, da das neue Fenster immer oben sein sollte. Wie auch immer, ich habe eine Hilfsmethode erstellt, die eine Form als Parameter annimmt. Wenn es aufgerufen wird, zeigt es das Formular an, bringt es nach vorne und macht es zu TopMost, ohne den Fokus des aktuellen Fensters zu stehlen (anscheinend, aber der Benutzer wird es nicht bemerken).

    [DllImport("user32.dll")]
    static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);

    public static void ShowTopmostNoFocus(Form f)
    {
        IntPtr activeWin = GetForegroundWindow();

        f.Show();
        f.BringToFront();
        f.TopMost = true;

        if (activeWin.ToInt32() > 0)
        {
            SetForegroundWindow(activeWin);
        }
    }

0

Ich weiß, dass es vielleicht dumm klingt, aber das hat funktioniert:

this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();

Wenn Sie das vordere Fenster nach hinten senden, wird es möglicherweise nicht mehr angezeigt, wenn das Hintergrundfenster das neue Vordergrundfenster überlappt.
TamusJRoyce

0

Ich musste dies mit meinem Fenster TopMost tun. Ich habe die oben beschriebene PInvoke-Methode implementiert, aber festgestellt, dass mein Load-Ereignis nicht wie oben beschrieben wurde. Es ist mir endlich gelungen. Vielleicht hilft das jemandem. Hier ist meine Lösung:

        form.Visible = false;
        form.TopMost = false;
        ShowWindow(form.Handle, ShowNoActivate);
        SetWindowPos(form.Handle, HWND_TOPMOST,
            form.Left, form.Top, form.Width, form.Height,
            NoActivate);
        form.Visible = true;    //So that Load event happens

-4

Wenn Sie ein neues Formular mit erstellen

Form f = new Form();
f.ShowDialog();

Es stiehlt den Fokus, da Ihr Code erst im Hauptformular ausgeführt werden kann, wenn dieses Formular geschlossen ist.

Die Ausnahme besteht darin, mithilfe von Threading ein neues Formular zu erstellen und dann Form.Show (). Stellen Sie jedoch sicher, dass der Thread global sichtbar ist. Wenn Sie ihn innerhalb einer Funktion deklarieren, wird Ihr Thread beendet und das Formular verschwindet, sobald Ihre Funktion beendet wird.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.