Mostra un modulo senza rubare il focus?


140

Sto usando un modulo per mostrare le notifiche (appare in basso a destra sullo schermo), ma quando mostro questo modulo ruba lo stato attivo dal modulo principale. C'è un modo per mostrare questo modulo di "notifica" senza rubare il focus?

Risposte:


165

Hmmm, non è semplicemente scavalcando Form.ShowWithoutActivation abbastanza?

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

E se non vuoi che l'utente faccia clic su questa finestra di notifica, puoi ignorare CreateParams:

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, non riesco a credere che non l'ho trovato, sprecato un intero pomeriggio!
Deerchao,

2
Avevo anche bisogno di impostare form1.Enabled = falseper impedire ai controlli interni di rubare la messa a fuoco
Jader Dias

23
E lascia TopMost spento.
mklein,

4
E se vuoi TopMost, vedi l' altra risposta .
Roman Starkov,

2
I valori di WS_EX_NOACTIVATEe WS_EX_TOOLWINDOWsono 0x08000000e 0x00000080rispettivamente.
Juan,

69

Stolen da PInvoke.net 's ShowWindow metodo:

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 ha risposto a questo, lo sto semplicemente espandendo incollando direttamente il codice. Qualcuno con diritti di modifica può copiarlo lì ed eliminarlo per quello che mi interessa;))


Mi chiedevo, ha davvero bisogno che se il modulo che mostra nella parte inferiore sinistra del suo schermo sia in un altro thread?
Patrick Desjardins,

50
Trovo incredibile che dobbiamo ancora collegarci a file DLL esterni per interagire con i moduli. Siamo alla versione 4 di .NET framework !! È ora di avvolgerlo Microsoft.
Maltrap,

9
La risposta accettata non è corretta Cerca ShowWithoutActivation
mklein

Aggiungi frm.Hide (); all'inizio della funzione ShowInactiveTopmost se si desidera che il modulo sia sfocato direttamente. Non dimenticare: utilizzando System.Runtime.InteropServices; per far funzionare questo codice
Zitun,

1
@Talha Questo codice non ha nulla a che fare con l'evento Load. L'evento Load viene generato quando il modulo viene caricato, non quando viene visualizzato.
TheSoftwareJedi


12

Questo è ciò che ha funzionato per me. Fornisce TopMost ma senza rubare la concentrazione.

    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;
       }
    }

Ricorda di omettere l'impostazione TopMost nella finestra di progettazione di Visual Studio o altrove.

Questo viene rubato, err, preso in prestito, da qui (fare clic su Soluzioni alternative):

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


1
Topmost + unfocused funziona e sembra la più pulita di tutte le risposte.
feos

Topmost è deprecato da Windows 8 in cui Microsoft ti punisce quando lo usi. L'effetto è che dopo aver aperto una finestra più in alto e quindi averla chiusa, Windows sposta le altre finestre dell'applicazione sullo sfondo. Questo non è sicuramente il comportamento desiderato per la tua applicazione. Microsoft lo ha implementato perché in passato molti programmatori hanno abusato del massimo intrusivo. Il massimo è molto aggressivo. Non lo uso mai.
Elmue

9

Fare questo sembra un trucco, ma sembra funzionare:

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

Modifica: Nota, questo solleva semplicemente una forma già creata senza rubare lo stato attivo.


non sembra funzionare qui ... potrebbe essere perché questo "modulo di notifica" è aperto in un altro thread?
Matías,

1
Probabilmente, in tal caso è necessario eseguire una chiamata this.Invoke () per chiamare nuovamente il metodo come thread corretto. In genere, l'utilizzo di moduli dal thread errato comporta comunque l'eccezione.
Matthew Scharley,

Mentre questo funziona, è un metodo caotico come detto e ha causato BSOD per me in determinate condizioni, quindi attenzione.
Raphael Smit,

Topmost è deprecato da Windows 8 in cui Microsoft ti punisce quando lo usi. L'effetto è che dopo aver aperto una finestra più in alto e quindi averla chiusa, Windows sposta le altre finestre dell'applicazione sullo sfondo. Questo non è sicuramente il comportamento desiderato per la tua applicazione. Microsoft lo ha implementato perché in passato molti programmatori hanno abusato del massimo intrusivo. Il massimo è molto aggressivo. Non lo uso mai.
Elmue

9

Il codice di esempio di pinvoke.net nelle risposte di Alex Lyman / TheSoftwareJedi renderà la finestra una finestra "più in alto", il che significa che non puoi metterla dietro le finestre normali dopo che è spuntata. Data la descrizione di Matias di ciò per cui vuole usarlo, potrebbe essere quello che vuole. Ma se vuoi che l'utente sia in grado di mettere la tua finestra dietro altre finestre dopo averla spuntata, usa HWND_TOP (0) invece di HWND_TOPMOST (-1) nell'esempio.


6

In WPF puoi risolverlo in questo modo:

Nella finestra inserisci questi attributi:

<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>

L'ultimo attributo è quello che ti serve ShowActivated = "False".


4

Ho qualcosa di simile e mostro semplicemente il modulo di notifica e poi lo faccio

this.Focus();

per riportare l'attenzione sulla forma principale.


Semplice ma efficace!
Tom,

3

Creare e avviare il modulo di notifica in un thread separato e reimpostare lo stato attivo sul modulo principale dopo l'apertura del modulo. Chiedi al modulo di notifica di fornire un evento OnFormOpened che viene generato Form.Showndall'evento. Qualcosa come questo:

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
}

Puoi anche mantenere un handle per il tuo oggetto NotifcationForm in modo che possa essere programmaticamente chiuso dal modulo principale (frm.Close() ).

Mancano alcuni dettagli, ma speriamo che questo ti porti nella giusta direzione.


Funzionerà solo se il tuo modulo era il modulo originariamente attivo. Questo tipo di va contro lo scopo principale di questo tipo di notifica.
Alex Lyman,

1
Eh? Questo è lo scopo della notifica: rimetterlo in funzione e ritrovare l'attenzione sul modulo originariamente attivo.
Bob Nadler,

2
Ciò consente di concentrarsi solo su un modulo nell'applicazione: cosa succede se qualche altro programma è attivo in quel momento? Mostrare una finestra di notifica (di solito per fornire all'utente un aggiornamento sullo stato della tua applicazione) è davvero utile solo quando non stanno guardando la tua applicazione.
Alex Lyman,

3

Potresti prendere in considerazione il tipo di notifica che desideri visualizzare.

Se è assolutamente fondamentale informare l'utente di qualche evento, utilizzare Messagebox.Show sarebbe il modo consigliato, a causa della sua natura per bloccare qualsiasi altro evento nella finestra principale, fino a quando l'utente non lo conferma. Fai attenzione alla cecità dei pop-up, però.

Se è meno che critico, potresti voler utilizzare un modo alternativo per visualizzare le notifiche, come una barra degli strumenti nella parte inferiore della finestra. Hai scritto che visualizzi le notifiche nella parte in basso a destra dello schermo: il modo standard per farlo è quello di utilizzare una punta a palloncino con la combinazione di un'icona nella barra delle applicazioni .


2
- I suggerimenti sull'aerostato non sono un'opzione perché possono essere disabilitati - La barra di stato potrebbe essere nascosta se il programma è ridotto a icona Comunque grazie per le vostre raccomandazioni
Matías

3

Funziona bene

Vedi: OpenIcon - MSDN e 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

È possibile gestire da sola logica troppo, anche se devo ammettere che i suggerimenti di cui sopra in cui si finisce con un metodo BringToFront senza in realtà rubare messa a fuoco è la più elegante.

Ad ogni modo, mi sono imbattuto in questo e l'ho risolto utilizzando una proprietà DateTime per non consentire ulteriori chiamate BringToFront se le chiamate sono state effettuate già di recente.

Supponi una classe principale, "Core", che gestisce ad esempio tre forme, "Form1, 2 e 3". Ogni modulo richiede una proprietà DateTime e un evento Activate che chiama Core per mettere in primo piano Windows:

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;
}

E quindi creare il lavoro nella classe principale:

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

In una nota a margine, se si desidera ripristinare una finestra ridotta a icona al suo stato originale (non ingrandita), utilizzare:

inForm.WindowState = FormWindowState.Normal;

Ancora una volta, so che questa è solo una soluzione di patch in mancanza di BringToFrontWithoutFocus. È inteso come suggerimento se si desidera evitare il file DLL.


1

Non so se questo sia considerato necro-posting, ma questo è quello che ho fatto da quando non riesco a farlo funzionare con i metodi "ShowWindow" e "SetWindowPos" di user32. E no, in questo caso l'override di "ShowWithoutActivation" non funziona poiché la nuova finestra dovrebbe essere sempre in primo piano. Comunque, ho creato un metodo di supporto che assume una forma come parametro; quando viene chiamato, mostra il modulo, lo porta in primo piano e lo rende TopMost senza rubare il focus della finestra corrente (apparentemente lo fa, ma l'utente non se ne accorgerà).

    [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

So che può sembrare stupido, ma ha funzionato:

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

Se si invia la finestra anteriore sul retro, potrebbe non essere più visualizzato se la finestra di sfondo si sovrappone a quella in primo piano.
TamusJRoyce,

0

Ho dovuto farlo con la mia finestra TopMost. Ho implementato il metodo PInvoke sopra ma ho scoperto che il mio evento Load non veniva chiamato come Talha sopra. Alla fine ci sono riuscito. Forse questo aiuterà qualcuno. Ecco la mia soluzione:

        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

Quando si crea un nuovo modulo utilizzando

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

ruba lo stato attivo perché il codice non può continuare l'esecuzione sul modulo principale fino a quando questo modulo non viene chiuso.

L'eccezione è l'utilizzo del threading per creare un nuovo modulo, quindi Form.Show (). Assicurati che il thread sia visibile globalmente, perché se lo dichiari all'interno di una funzione, non appena la tua funzione viene chiusa, il thread terminerà e il modulo scomparirà.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.