Come fare in modo che una finestra rimanga sempre in primo piano in .Net?


93

Ho un'app winforms C # che esegue una macro in un altro programma. L'altro programma aprirà continuamente finestre e generalmente farà sembrare le cose, in mancanza di una parola migliore, pazze. Voglio implementare un pulsante Annulla che interromperà l'esecuzione del processo, ma non riesco a far sì che la finestra rimanga in primo piano. Come si esegue questa operazione in C #?

Modifica: ho provato TopMost = true; , ma l'altro programma continua a far apparire le proprie finestre sopra. C'è un modo per inviare la mia finestra in alto ogni n millisecondi?

Modifica: il modo in cui ho risolto questo problema è stato aggiungendo un'icona nella barra delle applicazioni che annullerà il processo facendo doppio clic su di essa. L'icona della barra delle applicazioni non viene coperta. Grazie a tutti coloro che hanno risposto. Ho letto l'articolo sul motivo per cui non esiste una finestra "super-in-top" ... logicamente non funziona.


62
Sì, imposta un timer ogni pochi millisecondi che imposterà il tuo Form.TopMost su true. Quindi, giusto per renderlo interessante, quando il programma "folle" viene caricato, riproduci il clip audio di Mortal Kombat "FIGHT!" :-P
Gratuito

2
Potresti pensare che il tuo commento fosse esilarante, potresti pensare di poter ridicolizzare la cattiva pratica. Il mio problema era creare un menu contestuale che galleggia su un modulo con un flowlayoutpanel. Un flowlayoutpanel può essere fatto scorrere solo se chiami il suo metodo Activate (), Focus () NON è sufficiente in determinate circostanze. Semplicemente non sarai in grado di scorrerlo. Ciò ruba il focus dal menu contestuale anche se ha l'esclusivo topmost = true! Come ogni persona ragionevole sa che è divino praticante lasciare che le tue applicazioni winform vengano eseguite in modalità MTAThread e dare ad ogni forma il proprio thread che rende la soluzione semplice:
Traubenfuchs

1
Ecco, un diavolo: pastebin.com/sMJX0Yav Funziona perfettamente senza sfarfallio e il sonno (1) è sufficiente per impedirgli di prosciugare prestazioni serie. Chi continua a cercare nel suo taskmanager mentre si concentra su un menu contestuale? Una volta che il menu contestuale si chiude, si spera che venga eseguito nel gestore di eccezioni vuoto e muoia. Tuttavia, potresti creare una pausa isDisposed.
Traubenfuchs

Ho appena pubblicato la mia soluzione a questo problema qui: stackoverflow.com/questions/2546566/...
KFN

@Traubenfuchs che fallirà a causa dell'eccezione di operazione Cross-thread. Questo dovrebbe funzionare.
mekb

Risposte:


171

Form.TopMost funzionerà a meno che l'altro programma non crei le finestre più in alto.

Non è possibile creare una finestra che non sia coperta dalle nuove finestre più in alto di un altro processo. Raymond Chen ha spiegato perché.


10
Nel caso in cui altri neofiti completi vedano questo nel 2016 e oltre, provaForm.ActiveForm.TopMost
Devil's Advocate

1
@ScottBeeson: questo va bene. Le informazioni aggiornate sono molto necessarie in questo mondo tecno dinamico. Grazie:).
Sandeep Kushwah

47

Stavo cercando di rendere la mia applicazione WinForms "Sempre in primo piano", ma l'impostazione "TopMost" non ha fatto nulla per me. Sapevo che era possibile perché WinAmp lo fa (insieme a una miriade di altre applicazioni).

Quello che ho fatto è stato chiamare "user32.dll". Non ho avuto remore a farlo e funziona benissimo. Comunque è un'opzione.

Innanzitutto, importa il seguente spazio dei nomi:

using System.Runtime.InteropServices;

Aggiungi alcune variabili alla tua dichiarazione di classe:

private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

Aggiungi prototipo per la funzione user32.dll:

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

Quindi nel tuo codice (ho aggiunto la chiamata in Form_Load ()), aggiungi la chiamata:

SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);

Spero che aiuti. Riferimento


2
Questo funziona non solo per le applicazioni WinForms, ma anche per le finestre della console . Bella scoperta!
rojo

Bene, posso confermare che funziona. Ma come potrei cambiarlo di nuovo per non essere il migliore? c'è un flag HWND_BOTTOMMOST che puoi condividere?
Marco

Bella domanda, se volessi passare da questa capacità più elevata al comportamento predefinito (ad esempio, hai una casella di controllo "Sempre in primo piano" per il comportamento della finestra). Immagino che forse con le bandiere appropriate sarebbe possibile. Se avessi i flag corretti che descrivono il comportamento predefinito della finestra (diciamo SWP_DEFAULT = 0x0003), allora potresti semplicemente chiamare di nuovo "SetWindowPos ()" con questi flag. Solo non sono sicuro; Non l'ho esaminato. Buona fortuna se lo fai, e se qualcuno lo fa, aggiungilo qui!
clamum

Questo non funziona come al solito in modalità di gioco a schermo intero
Sajitha Rathnayake

2
@Mark Sì, c'è un flag HWND_NOTOPMOST (= -2). Vedi docs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier

23

Se per "impazzire" intendi che ogni finestra continua a rubare il focus dall'altra, TopMost non risolverà il problema.

Prova invece:

CalledForm.Owner = CallerForm;
CalledForm.Show();

Questo mostrerà la forma "bambino" senza che rubi il focus. Il modulo figlio rimarrà anche sopra il suo genitore anche se il genitore è attivato o focalizzato. Questo codice funziona facilmente solo se hai creato un'istanza del modulo figlio dall'interno del modulo del proprietario. In caso contrario, potresti dover impostare il proprietario utilizzando l'API.


1
Grazie mille ho usato esattamente questo e ha funzionato perfettamente!
AvetisG

1
Grazie .. esattamente quello che stavo cercando
Sameera Kumarasingha

L'impostazione CalledForm.Ownersu se stesso ( CalledForm) causerà un System.ArgumentException: 'È stato fatto un riferimento circolare al controllo. Un controllo non può essere posseduto o ereditato da se stesso.
mekb

2
Ecco perché usi CallerForm invece di CalledForm :)
Jesper

16

Imposta Form.TopMost


Ho provato, questo ... devo farlo continuamente? Il 'programma folle' prende il sopravvento immediatamente ...
jle

2
No, se imposti il ​​tuo form.TopMost = true, dovrebbe funzionare. Anche il programma "pazzo" deve avere le sue finestre di dialogo impostate su TopMost, nel qual caso non puoi sovrascriverle.
Reed Copsey

Non è una lotta leale. Grazie.
jle

11

Ho avuto un momentaneo intervallo di 5 minuti e ho dimenticato di specificare il modulo per intero in questo modo:

  myformName.ActiveForm.TopMost = true;

Ma quello che volevo davvero era QUESTO!

  this.TopMost = true;

Ha funzionato perfettamente per me. if (checkBox1.Checked == true) {this.TopMost = true; } else {this.TopMost = false; }
yosh

6

Imposta la .TopMostproprietà del modulo su true.

Probabilmente non vuoi lasciarlo sempre in questo modo: impostalo quando inizia il tuo processo esterno e rimettilo a posto quando finisce.


5

Il modo in cui ho risolto questo problema è stato creando un'icona nella barra delle applicazioni con un'opzione di annullamento.


5

Il codice seguente fa in modo che la finestra rimanga sempre in primo piano e la rende senza cornice.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StayOnTop
{
    public partial class Form1 : Form
    {
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const UInt32 SWP_NOSIZE = 0x0001;
        private const UInt32 SWP_NOMOVE = 0x0002;
        private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        public Form1()
        {
            InitializeComponent();
            FormBorderStyle = FormBorderStyle.None;
            TopMost = true;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
        }

        protected override void WndProc(ref Message m)
        {
            const int RESIZE_HANDLE_SIZE = 10;

            switch (m.Msg)
            {
                case 0x0084/*NCHITTEST*/ :
                    base.WndProc(ref m);

                    if ((int)m.Result == 0x01/*HTCLIENT*/)
                    {
                        Point screenPoint = new Point(m.LParam.ToInt32());
                        Point clientPoint = this.PointToClient(screenPoint);
                        if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)12/*HTTOP*/ ;
                            else
                                m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
                        }
                        else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)10/*HTLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)2/*HTCAPTION*/ ;
                            else
                                m.Result = (IntPtr)11/*HTRIGHT*/ ;
                        }
                        else
                        {
                            if (clientPoint.X <= RESIZE_HANDLE_SIZE)
                                m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
                            else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
                                m.Result = (IntPtr)15/*HTBOTTOM*/ ;
                            else
                                m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
                        }
                    }
                    return;
            }
            base.WndProc(ref m);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.Style |= 0x20000; // <--- use 0x20000
                return cp;
            }
        }
    }
}

D'accordo con Alexan: cosa rende il tuo programma al top? Sembra che in realtà sia solo l'affermazione "topmost = true", che non funziona in molti casi. Tutto il resto del codice non risponde realmente al problema.
Fhaab

4

Qual è l'altra applicazione di cui stai cercando di sopprimere la visibilità? Hai studiato altri modi per ottenere l'effetto desiderato? Per favore, fallo prima di sottoporre i tuoi utenti a comportamenti canaglia come stai descrivendo: quello che stai cercando di fare suona piuttosto come quello che fanno certi siti cattivi con le finestre del browser ...

Almeno cerca di aderire alla regola della minima sorpresa . Gli utenti si aspettano di essere in grado di determinare da soli l'ordine z della maggior parte delle applicazioni. Non sai cosa è più importante per loro, quindi se cambi qualcosa, dovresti concentrarti sul portare l'altra applicazione dietro tutto piuttosto che promuovere la tua.

Questo è ovviamente più complicato, poiché Windows non ha un gestore di finestre particolarmente sofisticato. Due approcci si suggeriscono:

  1. enumerando le finestre di primo livello e controllando a quale processo appartengono , eliminando il loro ordine z in caso affermativo . (Non sono sicuro che esistano metodi framework per queste funzioni WinAPI.)
  2. Giocherellare con le autorizzazioni del processo figlio per impedirgli di accedere al desktop ... ma non lo proverei fino a quando l'altro approccio non è fallito, poiché il processo figlio potrebbe finire in uno stato zombie mentre richiede l'interazione dell'utente.

4

Perché non rendere il tuo modulo una finestra di dialogo:

myForm.ShowDialog();

1
Sì! Questo è quello che volevo. L'impostazione ha TopMost = trueforzato il mio modulo in cima a tutto, incluso Chrome, quando in realtà è solo una finestra di impostazioni e ne avevo bisogno sopra il modulo principale. Complimenti a te persona di Internet.
MDMoore313

3

Ecco l'equivalente SetForegroundWindow:

form.Activate();

Ho visto persone fare cose strane come:

this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;

http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html


E se non voglio che la mia finestra sia attiva, la voglio solo in primo piano (informativo, non interattivo)? Chiedo solo perché effettivamente l'emissione di un "topmost = True" non funziona nel mio caso (funziona sui sistemi, non su altri).
Fhaab

Ho trovato che questo funzionasse per noi: this.Show(); this.Activate(); this.BringToFront(); ma questa risposta ci ha portato a quella soluzione. Grazie!
jibbs

1

So che è vecchio, ma non ho visto questa risposta.

Nella finestra (xaml) aggiungi:

Deactivated="Window_Deactivated"

Nel codice sottostante per Window_Deactivated:

private void Window_Deactivated(object sender, EventArgs e)
    {
        Window window = (Window)sender;
        window.Activate();
    }

Ciò manterrà la tua finestra in primo piano.


1
Non hai visto questa risposta perché la domanda riguarda winform.
Cinetico

1

Sulla base di clamum risposta, e di Kevin Vuilleumier commento circa l'altra bandiera responsabili del comportamento, ho fatto questa commutazione che commuta tra on-top e non on-top con pressione di un pulsante.

private void button1_Click(object sender, EventArgs e)
    {
        if (on)
        {
            button1.Text = "yes on top";
            IntPtr HwndTopmost = new IntPtr(-1);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = false;
        }
        else
        {
            button1.Text = "not on top";
            IntPtr HwndTopmost = new IntPtr(-2);
            SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
            on = true;
        }
    }
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.