A melhor maneira de ocultar uma janela do alternador de programas Alt-Tab?

101

Eu sou um desenvolvedor .NET há vários anos e isso ainda é uma das coisas que eu não sei fazer direito. É fácil ocultar uma janela da barra de tarefas por meio de uma propriedade tanto no Windows Forms quanto no WPF, mas, pelo que posso dizer, isso não garante (ou necessariamente afeta) que ela será ocultada da caixa de diálogo Alt+ ↹Tab. Já vi janelas invisíveis aparecerem no Alt+ ↹Tab, e estou apenas imaginando qual é a melhor maneira de garantir que uma janela nunca apareça (visível ou não) na caixa de diálogo Alt+ ↹Tab.

Atualização: por favor, veja minha solução postada abaixo. Não tenho permissão para marcar minhas próprias respostas como a solução, mas até agora é a única que funciona.

Atualização 2: Agora há uma solução adequada de Franci Penov que parece muito boa, mas ainda não experimentei. Envolve algum Win32, mas evita a criação deficiente de janelas fora da tela.

devios1
fonte
13
Os aplicativos da bandeja do sistema são um ótimo exemplo
TravisO
3
Eu quero fazer isso por um motivo porque eu uso uma janela preta semitransparente de tela inteira para fornecer um efeito de "escurecimento" quando meu aplicativo está exibindo uma interface modal, como a caixa de diálogo do UAC. Uma vez que esta não é uma janela interativa, não faz sentido exibi-la na caixa de diálogo Alt-Tab.
devios1
8
Eu não sugeriria escurecer toda a área de trabalho quando seu aplicativo mostrar seu próprio diálogo modal. Escurecer a área de trabalho sugere uma operação no nível do sistema operacional. A maioria das pessoas não teria conhecimento sofisticado o suficiente para ser capaz de entender que não é um desktop seguro.
Franci Penov
3
“É fácil ocultar uma janela da barra de tarefas por meio de uma propriedade”. Esta propriedade é ShowInTaskbar (apenas para registro).
greenoldman
A questão é sobre como ocultar a janela do Alt-Tab, não da barra de tarefas.
Alexandru Dicu

Respostas:

93

Atualizar:

De acordo com @donovan, o WPF moderno dá suporte a isso nativamente, por meio da configuração ShowInTaskbar="False"e Visibility="Hidden"no XAML. (Eu não testei isso ainda, mas decidi aumentar a visibilidade do comentário)

Resposta original:

Existem duas maneiras de ocultar uma janela do alternador de tarefas na API Win32:

  1. para adicionar o WS_EX_TOOLWINDOWestilo de janela estendida - essa é a abordagem certa.
  2. para torná-la uma janela filha de outra janela.

Infelizmente, o WPF não oferece suporte a um controle tão flexível sobre o estilo de janela como o Win32, portanto, uma janela WindowStyle=ToolWindowcom o padrão WS_CAPTIONe WS_SYSMENUestilos, o que faz com que tenha uma legenda e um botão Fechar. Por outro lado, você pode remover esses dois estilos configurando WindowStyle=None, no entanto, isso não definirá o WS_EX_TOOLWINDOWestilo estendido e a janela não ficará oculta do alternador de tarefas.

Para ter uma janela WPF WindowStyle=Noneque também está oculta do alternador de tarefas, pode-se de duas maneiras:

  • vá com o código de exemplo acima e torne a janela uma janela filha de uma pequena janela de ferramenta oculta
  • modifique o estilo da janela para incluir também o WS_EX_TOOLWINDOWestilo estendido.

Eu pessoalmente prefiro a segunda abordagem. Então, novamente, eu faço algumas coisas avançadas como estender o vidro na área do cliente e habilitar o desenho WPF na legenda de qualquer maneira, então um pouco de interoperabilidade não é um grande problema.

Aqui está o código de exemplo para a abordagem de solução de interoperabilidade Win32. Primeiro, a parte XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300"
    ShowInTaskbar="False" WindowStyle="None"
    Loaded="Window_Loaded" >

Nada muito sofisticado aqui, apenas declaramos uma janela com WindowStyle=Nonee ShowInTaskbar=False. Também adicionamos um manipulador ao evento Loaded, onde modificaremos o estilo da janela estendida. Não podemos fazer esse trabalho no construtor, pois ainda não há nenhum identificador de janela nesse ponto. O próprio manipulador de eventos é muito simples:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    WindowInteropHelper wndHelper = new WindowInteropHelper(this);

    int exStyle = (int)GetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE);

    exStyle |= (int)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
    SetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
}

E as declarações de interoperabilidade do Win32. Removi todos os estilos desnecessários dos enums, apenas para manter pequeno o código de amostra aqui. Além disso, infelizmente, o SetWindowLongPtrponto de entrada não foi encontrado em user32.dll no Windows XP, daí o truque de rotear a chamada através do SetWindowLong.

#region Window styles
[Flags]
public enum ExtendedWindowStyles
{
    // ...
    WS_EX_TOOLWINDOW = 0x00000080,
    // ...
}

public enum GetWindowLongFields
{
    // ...
    GWL_EXSTYLE = (-20),
    // ...
}

[DllImport("user32.dll")]
public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);

public static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
    int error = 0;
    IntPtr result = IntPtr.Zero;
    // Win32 SetWindowLong doesn't clear error on success
    SetLastError(0);

    if (IntPtr.Size == 4)
    {
        // use SetWindowLong
        Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
        error = Marshal.GetLastWin32Error();
        result = new IntPtr(tempResult);
    }
    else
    {
        // use SetWindowLongPtr
        result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
        error = Marshal.GetLastWin32Error();
    }

    if ((result == IntPtr.Zero) && (error != 0))
    {
        throw new System.ComponentModel.Win32Exception(error);
    }

    return result;
}

[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);

private static int IntPtrToInt32(IntPtr intPtr)
{
    return unchecked((int)intPtr.ToInt64());
}

[DllImport("kernel32.dll", EntryPoint = "SetLastError")]
public static extern void SetLastError(int dwErrorCode);
#endregion
Franci Penov
fonte
2
Não verifiquei isso, mas parece que você sabe do que está falando. :) Vou manter isso em mente se precisar fazer de novo, mas como minha outra solução está funcionando bem (e já faz um tempo que fechei o livro sobre esta), não quero mexer e quebrar alguma coisa . Obrigado!
devios1
1
Funciona perfeitamente! Obrigado!
Anthony Brien
Funciona bem para mim. Mas eu odeio ter que importar dll assim: P
J4N
8
@ J4N - Não há nada de errado com um pouco de P / Invoke de vez em quando :-)
Franci Penov
1
Isso não funcionou para mim no WPF. Mas, depois de brincar, descobri que uma solução muito mais fácil era definir ShowInTaskbar = "False" e Visibility = "Hidden" no XAML. Nenhuma pinvoke especial necessária.
Donovan
40

Dentro de sua classe de formulário, adicione isto:

protected override CreateParams CreateParams
{
    get
    {
        var Params = base.CreateParams;
        Params.ExStyle |= 0x80;

        return Params;
    }
}

É tão fácil assim; funciona um encanto!

Danny Beckett
fonte
3
Também é necessário definir ShowInTaskbar como false para que isso funcione.
Nick Spreitzer
20

Eu encontrei uma solução, mas não é bonita. Até agora, esta é a única coisa que tentei que realmente funciona:

Window w = new Window(); // Create helper window
w.Top = -100; // Location of new window is outside of visible part of screen
w.Left = -100;
w.Width = 1; // size of window is enough small to avoid its appearance at the beginning
w.Height = 1;
w.WindowStyle = WindowStyle.ToolWindow; // Set window style as ToolWindow to avoid its icon in AltTab 
w.Show(); // We need to show window before set is as owner to our main window
this.Owner = w; // Okey, this will result to disappear icon for main window.
w.Hide(); // Hide helper window just in case

Encontrei aqui .

Uma solução mais geral e reutilizável seria boa. Suponho que você possa criar uma única janela 'w' e reutilizá-la para todas as janelas do seu aplicativo que precisam ser ocultadas do Alt+ ↹Tab.

Update: Ok, então o que fiz foi mover o código acima, sem o this.Owner = wbit (e mover w.Hide()imediatamente depois w.Show(), o que funciona bem) para o construtor do meu aplicativo, criando uma estática pública Windowchamada OwnerWindow. Sempre que desejo que uma janela exiba esse comportamento, simplesmente a defino this.Owner = App.OwnerWindow. Funciona muito bem e envolve apenas a criação de uma janela extra (e invisível). Você pode até definir this.Owner = nullse deseja que a janela reapareça na caixa de diálogo Alt+ ↹Tab.

Obrigado a Ivan Onuchin nos fóruns do MSDN pela solução.

Update 2: Você também deve definir ShowInTaskBar=falsesobre wa impedi-lo de piscar brevemente na barra de tarefas quando mostrado.

devios1
fonte
Também existe uma solução de interoperabilidade Win32 para esse problema.
Franci Penov
Interessante, estou fazendo essa abordagem, mas evitando a janela oculta (usando a janela principal do aplicativo como o proprietário), e ela não está aparecendo no Alt-Tab ...
Dave,
1
Acho que em configurações de monitor duplo, a segunda tela também pode ter coordenadas negativas.
Thomas Weller
@ThomasW. Você provavelmente está certo. Usar um deslocamento como -100000provavelmente seria melhor.
devios1
Este é realmente um hack ruim para este problema.
Alexandru Dicu
10

Por que tão complexo? Experimente isto:

me.FormBorderStyle = FormBorderStyle.SizableToolWindow
me.ShowInTaskbar = false

Ideia tirada aqui: http://www.csharp411.com/hide-form-from-alttab/

Andrey
fonte
Funciona para mim. Obrigado pela contribuição!
MiBol de
Mas uma ToolWindow não pode ser maximizada ou minimizada. Uma ToolWindow nem sempre é uma opção preferível.
Alexandru Dicu
10

Aqui está o que faz o truque, independentemente do estilo da janela você está tentando esconder de Alt+ ↹Tab.

Coloque o seguinte no construtor do seu formulário:

// Keep this program out of the Alt-Tab menu

ShowInTaskbar = false;

Form form1 = new Form ( );

form1.FormBorderStyle = FormBorderStyle.FixedToolWindow;
form1.ShowInTaskbar = false;

Owner = form1;

Essencialmente, você torna seu formulário filho de uma janela invisível que possui o estilo correto e a configuração ShowInTaskbar para ficar fora da lista Alt-Tab. Você também deve definir a propriedade ShowInTaskbar do seu próprio formulário como false. O melhor de tudo é que simplesmente não importa o estilo do seu formulário principal, e todos os ajustes para realizar a ocultação são apenas algumas linhas no código do construtor.

Matt Hendricks
fonte
Espere ... ESTE é C # ou C ou C ++ ??? Eu sou realmente um n00b na família C ou qualquer outra coisa ...
Sreenikethan I
3

Por que tentar tantos códigos? Basta definir a propriedade FormBorderStylepara FixedToolWindow. Espero que ajude.

Saravanakumar. N
fonte
2

veja: (de http://bytes.com/topic/c-sharp/answers/442047-hide-alt-tab-list#post1683880 )

[DllImport("user32.dll")]
public static extern int SetWindowLong( IntPtr window, int index, int
value);
[DllImport("user32.dll")]
public static extern int GetWindowLong( IntPtr window, int index);


const int GWL_EXSTYLE = -20;
const int WS_EX_TOOLWINDOW = 0x00000080;
const int WS_EX_APPWINDOW = 0x00040000;

private System.Windows.Forms.NotifyIcon notifyIcon1;


// I use two icons depending of the status of the app
normalIcon = new Icon(this.GetType(),"Normal.ico");
alertIcon = new Icon(this.GetType(),"Alert.ico");
notifyIcon1.Icon = normalIcon;

this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.Visible = false;
this.ShowInTaskbar = false;
iconTimer.Start();

//Make it gone frmo the ALT+TAB
int windowStyle = GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, windowStyle | WS_EX_TOOLWINDOW);
Behnam Shomali
fonte
Eu acrescentaria aqui que 'Handle' pode ser adquirido por var handle = new WindowInteropHelper (this) .Handle;
Alexandru Dicu
1

Em XAML, defina ShowInTaskbar = "False":

<Window x:Class="WpfApplication5.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    ShowInTaskbar="False"    
    Title="Window1" Height="300" Width="300">
    <Grid>

    </Grid>
</Window>

Edit: Isso ainda mostra em Alt + Tab, eu acho, mas não na barra de tarefas.

Philipp Schmid
fonte
Sim, esse é o problema: ShowInTaskbar não afeta a caixa de diálogo Alt + Tab, como você pode esperar.
devios1
1

Tentei definir a visibilidade do formulário principal como falsa sempre que ele é alterado automaticamente para verdadeiro:

private void Form1_VisibleChanged(object sender, EventArgs e)
{
    if (this.Visible)
    {
        this.Visible = false;
    }
}

Funciona perfeitamente :)

tiendan
fonte
2
Não só é de longe a solução mais fácil, mas funcionou muito bem para mim.
Daniel McQuiston,
1

se quiser que o formulário não tenha bordas, será necessário adicionar as seguintes instruções ao construtor do formulário:

this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;

E você deve adicionar o seguinte método à sua classe derivada de Form:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        // turn on WS_EX_TOOLWINDOW style bit
        cp.ExStyle |= 0x80;
        return cp;
    }
}

mais detalhes

Hossein Moradinia
fonte
0

Propriedades do Form1:
FormBorderStyle: Sizable
WindowState: Minimized
ShowInTaskbar: False

private void Form1_Load(object sender, EventArgs e)
{
   // Making the window invisible forces it to not show up in the ALT+TAB
   this.Visible = false;
}>
Charlie Salts
fonte
-1

Pessoalmente, pelo que sei, isso não é possível sem enganchar nas janelas de alguma forma, nem tenho certeza de como isso seria feito ou se é possível.

Dependendo de suas necessidades, desenvolver seu contexto de aplicativo como um aplicativo NotifyIcon (bandeja do sistema) permitirá que ele seja executado sem ser exibido em ALT + TAB. NO ENTANTO, se você abrir um formulário, esse formulário ainda seguirá a funcionalidade padrão.

Posso desenterrar o artigo do meu blog sobre a criação de um aplicativo que é SOMENTE um NotifyIcon por padrão, se você quiser.

Vendedores Mitchel
fonte
Já estou bem versado no NotifyIcons, obrigado. O problema é que desejo ocultar as janelas abertas (não interativas ou superiores) de Alt + Tab. Curiosamente, acabei de notar que a barra lateral do Vista não aparece em Alt + Tab, então deve haver ALGUMA maneira de fazer isso.
devios1
Olhando os vários pedaços, sem mudar o tipo de janela (como o redbeard postou), não sei como fazer isso.
Mitchel Sellers de