Como fazer uma janela ficar sempre no topo no .Net?

93

Eu tenho um aplicativo winforms C # que executa uma macro em outro programa. O outro programa abrirá janelas continuamente e geralmente fará as coisas parecerem, por falta de uma palavra melhor, malucas. Quero implementar um botão de cancelamento que interromperá a execução do processo, mas não consigo fazer com que a janela permaneça no topo. Como faço isso em C #?

Edit: Eu tentei TopMost = true; , mas o outro programa continua exibindo suas próprias janelas por cima. Existe uma maneira de enviar minha janela para o topo a cada n milissegundos?

Edit: A maneira como resolvi isso foi adicionando um ícone na bandeja do sistema que cancelará o processo clicando duas vezes nele. O ícone da bandeja do sistema não fica encoberto. Obrigado a todos que responderam. Eu li o artigo sobre por que não há uma janela 'super no topo' ... logicamente, não funciona.

jle
fonte
61
Sim, defina um cronômetro para cada poucos milissegundos que definirá seu Form.TopMost como verdadeiro. Então, só para ficar mais interessante, quando o programa "louco" carregar, reproduza o clipe de áudio de Mortal Kombat "FIGHT!" :-P
BFree
2
Você pode achar que seu comentário foi hilário, você pode pensar que pode ridicularizar as más práticas. Meu problema era criar um menu de contexto que flutua sobre um formulário com um painel de layout de fluxo. Um flowlayoutpanel só pode ser rolado se você chamar seu método Activate (), Focus () NÃO é suficiente em certas circunstâncias. Você simplesmente não conseguirá rolar. Isso rouba o foco do menu de contexto, mesmo que tenha topmost = true exclusivo! Como qualquer pessoa sensata sabe, é uma prática divina deixar seus aplicativos winform rodarem no modo MTAThread e dar a cada forma seu próprio thread, o que torna a solução simples:
Traubenfuchs
1
Veja, um demônio: pastebin.com/sMJX0Yav Funciona perfeitamente, sem piscar, e o sono (1) é suficiente para evitar que prejudique um desempenho sério. Quem fica olhando em seu gerenciador de tarefas enquanto ele se concentra em um menu de contexto? Uma vez que o menu de contexto fecha, espera-se que ele encontre o manipulador de exceção vazio e morra. Você pode construir um intervalo isDisposed.
Traubenfuchs
Acabei de postar minha solução para este problema aqui: stackoverflow.com/questions/2546566/…
kfn
@Traubenfuchs Isso irá falhar devido à exceção de operação Cross-thread. Isso deve funcionar.
facepalm42

Respostas:

171

Form.TopMost funcionará a menos que o outro programa esteja criando as janelas superiores.

Não há como criar uma janela que não seja coberta pelas novas janelas superiores de outro processo. Raymond Chen explicou o porquê.

RossFabricant
fonte
10
No caso de qualquer outro novato completo ver isso em 2016 e além, tenteForm.ActiveForm.TopMost
Devil's Advocate
1
@ScottBeeson: Isso é bom. Informações atualizadas são muito necessárias neste mundo tecnodinâmico. Obrigado:).
Sandeep Kushwah
47

Eu estava procurando fazer meu aplicativo WinForms "Always On Top", mas a configuração de "TopMost" não funcionou para mim. Eu sabia que era possível porque o WinAmp faz isso (junto com uma série de outros aplicativos).

O que fiz foi ligar para "user32.dll". Eu não tive escrúpulos em fazer isso e funciona muito bem. É uma opção, de qualquer maneira.

Primeiro, importe o seguinte namespace:

using System.Runtime.InteropServices;

Adicione algumas variáveis ​​à sua declaração de 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;

Adicionar protótipo para a função 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);

Então, em seu código (adicionei a chamada em Form_Load ()), adicione a chamada:

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

Espero que ajude. Referência

clamum
fonte
2
Isso funciona não apenas para aplicativos WinForms, mas também para janelas de console . Belo achado!
rojo
Legal, posso confirmar que isso funciona. Mas como eu seria capaz de alterá-lo de volta para não ser o melhor? há um sinalizador HWND_BOTTOMMOST que você pode compartilhar?
Mark
Boa pergunta, se você deseja alternar entre esta habilidade superior e o comportamento padrão (por exemplo, você tem uma caixa de seleção "Sempre no topo" para o comportamento da janela). Eu acho que talvez com as bandeiras adequadas isso seja possível. Se você tiver os sinalizadores corretos que descrevem o comportamento padrão da janela (digamos SWP_DEFAULT = 0x0003), basta chamar "SetWindowPos ()" novamente com esses sinalizadores. Só não tenho certeza; Eu não olhei para isso. Boa sorte se você o fizer, e se alguém o fizer, por favor, adicione-o aqui!
clamum
Isso não está funcionando no modo de jogo em tela cheia como de costume
Sajitha Rathnayake
2
@Mark Sim, há um sinalizador HWND_NOTOPMOST (= -2). Consulte docs.microsoft.com/en-us/windows/win32/api/winuser/…
Kevin Vuilleumier
23

Se por "enlouquecer" você quer dizer que cada janela continua roubando o foco da outra, o TopMost não resolverá o problema.

Em vez disso, tente:

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

Isso mostrará a forma 'criança' sem roubar o foco. O formulário filho também ficará em cima de seu pai, mesmo se o pai estiver ativado ou focado. Este código só funciona facilmente se você tiver criado uma instância do formulário filho de dentro do formulário do proprietário. Caso contrário, pode ser necessário definir o proprietário usando a API.

Victor Stoddard
fonte
1
Muito obrigado, usei exatamente isso e funcionou perfeitamente!
AvetisG de
1
Obrigado .. exatamente o que eu estava procurando
Sameera Kumarasingha
Configurar CalledForm.Ownerpara si mesmo ( CalledForm) causará System.ArgumentException: 'Uma referência de controle circular foi feita. Um controle não pode ser de propriedade ou pai dele mesmo. '
facepalm42
2
É por isso que você usa CallerForm em vez de CalledForm :)
Jesper
16

Definir Form.TopMost

Reed Copsey
fonte
Eu tentei, isso ... eu preciso estar fazendo isso continuamente? O 'programa de crazy' assume imediatamente ...
jle
2
Não - se você definir seu form.TopMost = true, ele deve funcionar. O programa "louco" deve ter seus diálogos configurados como TopMost também; nesse caso, você não pode substituí-lo.
Reed Copsey,
Não é uma luta justa. Obrigado.
jle
11

Eu tive um lapso momentâneo de 5 minutos e esqueci de especificar o formulário completo assim:

  myformName.ActiveForm.TopMost = true;

Mas o que eu realmente queria era ISSO!

  this.TopMost = true;
Dave
fonte
Funcionou perfeito para mim. if (checkBox1.Checked == true) {this.TopMost = true; } else {this.TopMost = false; }
yosh
funcionou perfeito para mim. Obrigado.
Levani Titberia 01 de
6

Defina a .TopMostpropriedade do formulário como true.

Você provavelmente não vai querer deixá-lo assim o tempo todo: configure-o quando o processo externo iniciar e coloque-o de volta quando terminar.

Joel Coehoorn
fonte
5

A maneira como resolvi isso foi criando um ícone na bandeja do sistema com a opção de cancelar.

jle
fonte
5

O código a seguir faz com que a janela sempre fique no topo, além de torná-la sem moldura.

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;
            }
        }
    }
}
BK Krish
fonte
Concordo com Alexan - o que torna seu programa o mais importante? Parece que na verdade é apenas a instrução "topmost = true", que não funciona em muitos casos. Todo o resto do código realmente não responde ao problema.
Fhaab
4

Qual é o outro aplicativo cuja visibilidade você está tentando suprimir? Você investigou outras maneiras de alcançar o efeito desejado? Faça isso antes de sujeitar seus usuários a comportamentos maliciosos como você está descrevendo: o que você está tentando fazer parece mais com o que certos sites maliciosos fazem com as janelas do navegador ...

Pelo menos tente seguir a regra da Menos Surpresa . Os usuários esperam poder determinar a ordem z da maioria dos aplicativos por conta própria. Você não sabe o que é mais importante para eles, então, se mudar alguma coisa, você deve se concentrar em empurrar o outro aplicativo por trás de tudo, em vez de promover o seu próprio.

É claro que isso é mais complicado, já que o Windows não tem um gerenciador de janelas particularmente sofisticado. Duas abordagens se sugerem:

  1. enumerando as janelas de nível superior e verificando a qual processo elas pertencem , descartando sua ordem z em caso afirmativo . (Não tenho certeza se existem métodos de estrutura para essas funções WinAPI.)
  2. Mexer com as permissões do processo filho para evitar que ele acesse a área de trabalho ... mas eu não tentaria isso até que a outra abordagem falhasse, já que o processo filho pode acabar em um estado zumbi enquanto requer interação do usuário.
Pontus Gagge
fonte
4

Por que não transformar seu formulário em uma caixa de diálogo:

myForm.ShowDialog();
Salim
fonte
1
Sim! Isso é o que eu queria. A configuração TopMost = trueforçou meu formulário acima de tudo, incluindo o cromo, quando na realidade é apenas uma caixa de configurações e eu precisava dela em cima do formulário principal. Parabéns para você, pessoa da Internet.
MDMoore313
3

Aqui está o equivalente a SetForegroundWindow:

form.Activate();

Já vi pessoas fazendo coisas estranhas como:

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

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

user2070066
fonte
E se eu não quiser que minha janela fique ativa, eu apenas a quero no topo (informativa, não interativa)? Eu pergunto apenas porque emitir um "topmost = True" não está funcionando no meu caso (funciona em sistemas, não em outros).
Fhaab
Descobri que isso funcionou para nós: this.Show(); this.Activate(); this.BringToFront(); mas essa resposta nos levou a essa solução. Obrigado!
Jibbs
1

Eu sei que isso é antigo, mas não vi essa resposta.

Na janela (xaml) adicione:

Deactivated="Window_Deactivated"

No código por trás de Window_Deactivated:

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

Isso manterá sua janela no topo.

espere o que
fonte
1
Você não viu esta resposta porque a pergunta é sobre winform.
Cinética
1

Com base na resposta de Clamum e no comentário de Kevin Vuilleumier sobre a outra sinalização responsável pelo comportamento, fiz essa alternância que alterna entre no topo e não no topo com um toque de botão.

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;
        }
    }
Tóth Dániel
fonte