Najlepszy sposób na ukrycie okna przed przełącznikiem programów Alt-Tab?

101

Jestem programistą .NET od kilku lat i nadal jest to jedna z tych rzeczy, których nie umiem zrobić poprawnie. Łatwo jest ukryć okno z paska zadań za pomocą właściwości zarówno w Windows Forms, jak i WPF, ale o ile wiem, nie gwarantuje to (ani nawet nie wpływa) na jego ukrycie w Alt+ ↹Taboknie dialogowym. Widziałem niewidoczne okna pojawiające się w Alt+ ↹Tabi zastanawiam się, jaki jest najlepszy sposób na zagwarantowanie, że okno nigdy się nie pojawi (widoczne lub nie) w oknie dialogowym Alt+ ↹Tab.

Aktualizacja: zobacz poniżej moje opublikowane rozwiązanie. Nie wolno mi oznaczać własnych odpowiedzi jako rozwiązania, ale jak dotąd jest to jedyne, które działa.

Aktualizacja 2: Franci Penov ma teraz odpowiednie rozwiązanie, które wygląda całkiem nieźle, ale sam go nie wypróbowałem. Obejmuje niektóre Win32, ale unika kulawego tworzenia okien poza ekranem.

devios1
źródło
13
Aplikacje na pasku
zadań
3
Chcę to zrobić z jednego powodu, ponieważ używam pełnoekranowego, półprzezroczystego czarnego okna, aby zapewnić efekt „ściemniania”, gdy moja aplikacja wyświetla interfejs modalny, podobny do okna dialogowego UAC. Ponieważ nie jest to okno interaktywne, nie ma sensu pokazywać go w oknie dialogowym Alt-Tab.
devios1
8
Sugerowałbym, aby nie przyciemniać całego pulpitu, gdy aplikacja wyświetla własne okno dialogowe. Przyciemnienie pulpitu sugeruje operację na poziomie systemu operacyjnego. Większość ludzi nie miałaby wystarczającej wiedzy, aby zrozumieć, że to nie jest bezpieczny pulpit.
Franci Penov
3
„Łatwo jest ukryć okno z paska zadań za pomocą właściwości”. Ta właściwość to ShowInTaskbar (tylko dla rekordu).
greenoldman
Pytanie dotyczy ukrycia okna przed Alt-Tab, a nie z paska zadań.
Alexandru Dicu

Odpowiedzi:

94

Aktualizacja:

Według @donovan, współczesne WPF obsługuje to natywnie, poprzez ustawienie ShowInTaskbar="False"iVisibility="Hidden" w języku XAML. (Jeszcze tego nie testowałem, ale mimo to postanowiłem zwiększyć widoczność komentarzy)

Oryginalna odpowiedź:

Istnieją dwa sposoby ukrycia okna z przełącznika zadań w Win32 API:

  1. dodać WS_EX_TOOLWINDOWrozszerzony styl okna - to jest właściwe podejście.
  2. aby było to okno potomne innego okna.

Niestety, WPF nie obsługuje tak elastycznej kontroli nad stylem okna jak Win32, więc okno z WindowStyle=ToolWindowkończy się na domyślnych WS_CAPTIONi WS_SYSMENUstylach, co powoduje, że ma podpis i przycisk zamykania. Z drugiej strony możesz usunąć te dwa style, ustawiając WindowStyle=None, jednak nie spowoduje to ustawieniaWS_EX_TOOLWINDOW rozszerzonego stylu, a okno nie będzie ukryte przed przełącznikiem zadań.

Aby mieć okno WPF z WindowStyle=Nonetym, które jest również ukryte przed przełącznikiem zadań, można wybrać jeden z dwóch sposobów:

  • przejdź do powyższego przykładowego kodu i ustaw okno jako okno potomne małego, ukrytego okna narzędzia
  • zmodyfikuj styl okna, aby uwzględnić również WS_EX_TOOLWINDOWstyl rozszerzony.

Osobiście wolę drugie podejście. Z drugiej strony robię zaawansowane rzeczy, takie jak rozszerzenie szyby w obszarze roboczym i i tak włączenie rysowania WPF w podpisie, więc odrobina interopu nie jest dużym problemem.

Oto przykładowy kod rozwiązania międzyoperacyjnego Win32. Najpierw część 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" >

Nic nadzwyczajnego, po prostu deklarujemy okno z WindowStyle=Nonei ShowInTaskbar=False. Dodajemy również procedurę obsługi do zdarzenia Loaded, w którym zmodyfikujemy rozszerzony styl okna. Nie możemy wykonać tej pracy w konstruktorze, ponieważ w tym momencie nie ma jeszcze uchwytu okna. Sam program obsługi zdarzeń jest bardzo prosty:

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

I deklaracje międzyoperacyjne Win32. Usunąłem wszystkie niepotrzebne style z wyliczeń, aby przykładowy kod był mały. Niestety, SetWindowLongPtrpunkt wejścia nie znajduje się w user32.dll w systemie Windows XP, stąd sztuczka z przekierowywaniem połączenia przez ten SetWindowLongplik.

#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
źródło
2
Nie zweryfikowałem tego, ale wygląda na to, że wiesz, o czym mówisz. :) Będę o tym pamiętać, jeśli będę musiał to zrobić ponownie, ale ponieważ moje drugie rozwiązanie działa dobrze (a minęło trochę czasu, odkąd zamknąłem książkę na tym), nie chcę bawić się i łamać coś . Dzięki!
devios1
1
Działa świetnie! Dzięki!
Anthony Brien
U mnie działa dobrze. Ale nienawidzę importować dll w ten sposób: P
J4N
8
@ J4N - Nie ma nic złego w odrobinie P / Invoke od czasu do czasu :-)
Franci Penov
1
To nie zadziałało dla mnie w WPF. Ale po zabawie stwierdziłem, że znacznie łatwiejszym rozwiązaniem było ustawienie ShowInTaskbar = "False" i Visibility = "Hidden" w XAML. Nie jest wymagane specjalne pinvoke.
donovan
40

Wewnątrz klasy formularza dodaj to:

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

        return Params;
    }
}

To takie proste; działa czarująco!

Danny Beckett
źródło
3
Aby to zadziałało, należy również ustawić ShowInTaskbar na false.
Nick Spreitzer
20

Znalazłem rozwiązanie, ale nie jest ładne. Jak dotąd jest to jedyna rzecz, którą wypróbowałem, która faktycznie działa:

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

Znalazłem to tutaj .

Przydałoby się bardziej ogólne rozwiązanie wielokrotnego użytku. Przypuszczam, że możesz utworzyć pojedyncze okno „w” i użyć go ponownie dla wszystkich okien w aplikacji, które muszą być ukryte przed przyciskiem Alt+ ↹Tab.

Aktualizacja: Ok, więc to, co zrobiłem, to przeniesienie powyższego kodu, bez this.Owner = wbitu (i przejście w.Hide()natychmiast po nim w.Show(), co działa dobrze) do konstruktora mojej aplikacji, tworząc publiczny statyczny plik o Windownazwie OwnerWindow. Zawsze, gdy chcę, aby okno wykazywało takie zachowanie, po prostu ustawiam this.Owner = App.OwnerWindow. Działa świetnie i wymaga tylko stworzenia jednego dodatkowego (i niewidocznego) okna. Możesz nawet ustawić, this.Owner = nullczy chcesz, aby okno pojawiło się ponownie w oknie Alt+ ↹Tab.

Podziękowania dla Ivana Onuchina na forach MSDN za rozwiązanie.

Aktualizacja 2: Należy również ustawić ShowInTaskBar=falsena wcelu zapobiec jej krótko miga na pasku zadań, gdy pokazano.

devios1
źródło
Istnieje również rozwiązanie tego problemu we współpracy z Win32.
Franci Penov
Ciekawe, robię to podejście, ale unikam ukrytego okna (używając głównego okna aplikacji jako właściciela) i nie pojawia się ono w Alt-Tab ...
Dave,
1
Myślę, że w konfiguracjach z dwoma monitorami drugi ekran może również mieć ujemne współrzędne.
Thomas Weller,
@ThomasW. Pewnie masz rację. -100000Lepsze byłoby prawdopodobnie użycie takiego przesunięcia .
devios1
To naprawdę zły hack na ten problem.
Alexandru Dicu
10

Dlaczego tak skomplikowane? Spróbuj tego:

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

Pomysł zaczerpnięty stąd: http://www.csharp411.com/hide-form-from-alttab/

Andrey
źródło
Pracuje dla mnie. Dzięki za wkład!
MiBol
Ale ToolWindow nie można zmaksymalizować ani zminimalizować. ToolWindow nie zawsze jest preferowaną opcją.
Alexandru Dicu
10

Oto, na czym polega sztuczka, niezależnie od stylu okna, przed którym próbujesz ukryć Alt+ ↹Tab.

Umieść następujące elementy w konstruktorze swojego formularza:

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

ShowInTaskbar = false;

Form form1 = new Form ( );

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

Owner = form1;

Zasadniczo sprawiasz, że twój formularz jest dzieckiem niewidocznego okna, które ma odpowiedni styl i ustawienie ShowInTaskbar, aby nie było na liście Alt-Tab. Należy również ustawić właściwość ShowInTaskbar własnego formularza na false. A co najważniejsze, po prostu nie ma znaczenia, jaki styl ma twój główny formularz, a wszystkie poprawki w celu ukrycia to tylko kilka wierszy w kodzie konstruktora.

Matt Hendricks
źródło
Czekaj ... czy TO jest C # czy C czy C ++ ??? Naprawdę jestem n00b w rodzinie C czy kimkolwiek ...
Sreenikethan I
3

Po co próbować tyle kodów? Po prostu ustaw właściwość FormBorderStylena FixedToolWindow. Mam nadzieję, że to pomoże.

Saravanakumar. N
źródło
2

zobacz: (z 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
źródło
Dodałbym tutaj, że 'Handle' można uzyskać przez var handle = new WindowInteropHelper (this) .Handle;
Alexandru Dicu
1

W XAML ustaw 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>

Edycja: to nadal pokazuje to w Alt + Tab, ale chyba nie na pasku zadań.

Philipp Schmid
źródło
Tak, to jest problem: ShowInTaskbar nie wpływa na okno dialogowe Alt + Tab, jak można się spodziewać.
devios1
1

Próbowałem ustawić widoczność głównego formularza na fałsz, ilekroć jest ona automatycznie zmieniana na true:

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

Działa idealnie :)

tiendan
źródło
2
Nie tylko jest to zdecydowanie najłatwiejsze rozwiązanie, ale dla mnie zadziałało bardzo przyjemnie.
Daniel McQuiston,
1

jeśli chcesz, aby formularz był bez obramowania, musisz dodać następujące instrukcje do konstruktora formularza:

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

ORAZ musisz dodać następującą metodę do swojej pochodnej klasy Form:

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

więcej szczegółów

Hossein Moradinia
źródło
0

Właściwości Form1:
FormBorderStyle : Sizable
WindowState: Zminimalizowane
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
źródło
-1

Osobiście, o ile wiem, nie jest to możliwe bez podpięcia do okien w jakiś sposób, nie jestem nawet pewien, jak by to zrobić i czy jest to możliwe.

W zależności od potrzeb, rozwijanie kontekstu aplikacji jako aplikacji NotifyIcon (zasobnik systemowy) umożliwi jej działanie bez wyświetlania w ALT + TAB. JEDNAK, jeśli otworzysz formularz, będzie on nadal spełniał standardowe funkcje.

Mogę odkopać mój artykuł na blogu o tworzeniu aplikacji, która domyślnie jest TYLKO NotifyIcon, jeśli chcesz.

Mitchel Sellers
źródło
Jestem już dobrze zorientowany w NotifyIcons, dzięki. Problem polega na tym, że chcę ukryć otwarte (nieinteraktywne lub najwyższe) okna z Alt + Tab. Co ciekawe, właśnie zauważyłem, że pasek boczny Visty nie pojawia się w Alt + Tab, więc musi być JAKIŚ sposób na zrobienie tego.
devios1
Patrząc na różne fragmenty, bez zmiany typu okna (jak napisał rudobrody), nie wiem, jak to zrobić.
Mitchel Sellers