Лучший способ скрыть окно от переключателя программ Alt-Tab?
Я был разработчиком.NET уже несколько лет, и до сих пор это одна из тех вещей, которые я не знаю, как делать правильно. Окно с панели задач легко скрыть через свойство в Windows Forms и WPF, но, насколько я могу судить, это не гарантирует (или не обязательно влияет) на его скрытие в диалоге Alt + ↹Tab. Я видел невидимые окна, появляющиеся в Alt + ↹Tab, и мне просто интересно, как лучше всего гарантировать, что окно никогда не появится (видимое или нет) в диалоге Alt + ↹Tab.
Обновление: пожалуйста, смотрите мое опубликованное решение ниже. Мне не разрешено отмечать мои собственные ответы как решение, но пока оно работает единственно.
Обновление 2: теперь есть подходящее решение от Франциска Пенова, которое выглядит довольно хорошо, но я не пробовал его сам. Включает в себя некоторые Win32, но избегает создания неудачных закадровых окон.
16 ответов
Обновить:
Согласно @donovan, в наши дни WPF поддерживает это изначально, путем установкиShowInTaskbar="False"
а также Visibility="Hidden"
в XAML. (Я еще не проверял это, но тем не менее решил увеличить видимость комментария)
Оригинальный ответ:
Есть два способа скрыть окно от переключателя задач в Win32 API:
- добавить
WS_EX_TOOLWINDOW
расширенный стиль окна - это правильный подход. - сделать его дочерним окном другого окна.
К сожалению, WPF не поддерживает такой гибкий контроль стиля окна, как Win32, поэтому окно с WindowStyle=ToolWindow
заканчивается по умолчанию WS_CAPTION
а также WS_SYSMENU
стили, что заставляет его иметь заголовок и кнопку закрытия. С другой стороны, вы можете удалить эти два стиля, установив WindowStyle=None
Однако это не установит WS_EX_TOOLWINDOW
расширенный стиль и окно не будет скрыто от переключателя задач.
Иметь окно WPF с WindowStyle=None
это также скрыто от переключателя задач, один из двух способов:
- перейдите с примером кода выше и сделайте окно дочерним окном небольшого скрытого окна инструмента
- изменить стиль окна, чтобы также включить
WS_EX_TOOLWINDOW
расширенный стиль.
Я лично предпочитаю второй подход. С другой стороны, я делаю некоторые продвинутые вещи, такие как расширение стекла в клиентской области и включение рисования WPF в заголовке в любом случае, так что небольшое взаимодействие не является большой проблемой.
Вот пример кода для подхода решения взаимодействия Win32. Во-первых, часть 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" >
Ничего особенного, мы просто объявляем окно с WindowStyle=None
а также ShowInTaskbar=False
, Мы также добавляем обработчик в событие Loaded, где мы будем изменять расширенный стиль окна. Мы не можем сделать эту работу в конструкторе, так как на данный момент нет дескриптора окна. Сам обработчик событий очень прост:
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);
}
И Win32 декларации взаимодействия. Я удалил все ненужные стили из перечислений, просто чтобы оставить здесь небольшой пример кода. Кроме того, к сожалению SetWindowLongPtr
Точка входа не найдена в user32.dll в Windows XP, поэтому хитрость заключается в маршрутизации вызова через 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
Внутри вашего класса формы добавьте это:
protected override CreateParams CreateParams
{
get
{
var Params = base.CreateParams;
Params.ExStyle |= 0x80;
return Params;
}
}
Это так просто, как это; работает шарм!
Я нашел решение, но это не красиво. Пока это единственное, что я пробовал, это действительно работает:
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
Нашел это здесь.
Было бы неплохо использовать более общее многоразовое решение. Я полагаю, вы могли бы создать одно окно "w" и повторно использовать его для всех окон в вашем приложении, которые должны быть скрыты от Alt+↹Tab.
Обновление: Хорошо, так что я сделал, переместить вышеуказанный код, минус this.Owner = w
немного (и движется w.Hide()
незамедлительно после w.Show()
, который работает нормально) в конструктор моего приложения, создавая общедоступную статическую Window
называется OwnerWindow
, Всякий раз, когда я хочу, чтобы окно демонстрировало это поведение, я просто устанавливаю this.Owner = App.OwnerWindow
, Прекрасно работает, и включает в себя только создание одного дополнительного (и невидимого) окна. Вы даже можете установить this.Owner = null
если вы хотите, чтобы окно снова появилось в диалоге Alt+↹Tab.
Спасибо Ивану Онучину за помощь на форумах MSDN.
Обновление 2: Вы также должны установить ShowInTaskBar=false
на w
чтобы он не мигал на панели задач, когда отображается.
Вот что делает трюк, независимо от стиля окна, которое вы пытаетесь скрыть от Alt+abTab.
Поместите следующее в конструктор вашей формы:
// Keep this program out of the Alt-Tab menu
ShowInTaskbar = false;
Form form1 = new Form ( );
form1.FormBorderStyle = FormBorderStyle.FixedToolWindow;
form1.ShowInTaskbar = false;
Owner = form1;
По сути, вы делаете свою форму дочерней по отношению к невидимому окну с правильным стилем и настройкой ShowInTaskbar, чтобы исключить его из списка Alt-Tab. Необходимо также установить для свойства собственной формы ShowInTaskbar значение false. Лучше всего то, что просто не имеет значения, какой стиль имеет ваша основная форма, и все настройки для скрытия - это всего лишь несколько строк в коде конструктора.
Почему так сложно? Попробуй это:
me.FormBorderStyle = FormBorderStyle.SizableToolWindow
me.ShowInTaskbar = false
Идея взята отсюда: http://www.csharp411.com/hide-form-from-alttab/
Увидеть его:(с http://bytes.com/topic/c-sharp/answers/442047-hide-alt-tab-list)
[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);
Зачем пытаться так много кодов? Просто установите FormBorderStyle
подход к FixedToolWindow
, Надеюсь, поможет.
Я пытался установить видимость главной формы на false, когда она автоматически меняется на true:
private void Form1_VisibleChanged(object sender, EventArgs e)
{
if (this.Visible)
{
this.Visible = false;
}
}
Работает отлично:)
Если вы хотите, чтобы форма была без полей, вам нужно добавить следующие операторы в конструктор формы:
this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;
И вы должны добавить следующий метод в свой производный класс Form:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
// turn on WS_EX_TOOLWINDOW style bit
cp.ExStyle |= 0x80;
return cp;
}
}
Я попробовал это. Меня устраивает
private void Particular_txt_KeyPress(object sender, KeyPressEventArgs e)
{
Form1 frm = new Form1();
frm.Owner = this;
frm.Show();
}
В XAML установите 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>
Изменить: Это все еще показывает это в Alt+Tab, я думаю, но не на панели задач.
Решение для тех, кто хочет, чтобы окно WPF оставалось видимым, будучи скрытым от переключателя Alt+Tab:
Самый простой способ сделать это с помощью WPF, который я нашел (хотя до сих пор тестировался лишь вкратце), заключался в том, чтобы просто установить владельца главного окна в новом окне. По сути, это заставляет окно выглядеть как своего рода модальное диалоговое окно или наложение, но оно не отображается на панели задач или на вкладке Alt-Tab.
Пример:
В коде для окна:
public partial class OverlayWindow : Window
{
public OverlayWindow()
{
Owner = Application.Current.MainWindow;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
И в XAML для окна:
<Window x:Class="WPFSandbox.OverlayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Height="200" Width="300" ShowInTaskbar="False" WindowStyle="None" WindowStartupLocation="CenterOwner">
<!-- Window content (for test) -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Title" HorizontalAlignment="Center" Grid.Row="0"/>
<Label Grid.Row="1" Content="Content" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Content="Close" Grid.Row="2" Click="Button_Click" Width="50" HorizontalAlignment="Right" Margin="5"/>
</Grid>
</Window>
Вышеупомянутое окно затем можно было бы вызвать из другого места просто с помощью стандартного метода ShowDialog() следующим образом (или Show(), если оно не рассматривается как диалог):
private void Button_Click(object sender, RoutedEventArgs e)
{
var result = new OverlayWindow().ShowDialog();
// Do something with result if required
}
В приведенном выше примере в очень простом тестовом приложении это должно выглядеть примерно так:
Не показывать форму. Используйте невидимость.
Подробнее здесь: http://code.msdn.microsoft.com/TheNotifyIconExample
Свойства Form1:
FormBorderStyle: Sizable
WindowState: свернутый
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;
}>
Лично, насколько я знаю, это невозможно без того, чтобы каким-то образом подключиться к окнам, я даже не уверен, как это будет сделано или возможно ли это.
В зависимости от ваших потребностей, разработка контекста вашего приложения как приложения NotifyIcon (системный трей) позволит ему работать без отображения в ALT + TAB. ОДНАКО, если вы откроете форму, эта форма будет по-прежнему соответствовать стандартной функциональности.
Я могу выкопать мою статью в блоге о создании приложения, которое по умолчанию является ТОЛЬКО NotifyIcon, если хотите.