Как показать невидимое окно?

319
28 мая 2017, 18:54

У меня есть окно, которое выполняет какой-то процесс с визуализацией. И мне надо чтобы при запуске приложение оно работало, но не показывалось (то есть чтобы произошло событие Load). При этом у пользователя всегда есть возможность его показывать и скрывать. Проблема в том что у меня не получается решить этот вопрос без начального мерцания из-за подобного кода.

SomeWindow.Show();
SomeWindow.Hide();

Ни в Windows Forms, ни в WPF не вижу решения этой проблемы.

Проблема также в том, что окно показывается в TaskBar, но только когда оно видимое. Поэтому мне не подходят хаки вроде

  1. Установки прозрачности окна.
  2. Установки нулевой широты, высоты и удаления рамок.
  3. Вынос окна за пределы экрана и т.д.

Для чего это надо и почему нельзя всё вынести в какой-нибудь Task? Потому что код выполняемый окном зависит от визуализации. Мне нужно чтобы человек мог скрывать и показывать окно когда и сколько угодно, но чтобы его состояние было таким же как будто его не скрывали.

Answer 1

Просто так вам не получится вызвать событие Loaded: оно вызывается лишь тогда, когда ваше окно реально было показано. Но, возможно, у вас сработает обходной путь, который сработал в моём проекте.

Для начала, мы отделим контент окна в UserControl. Окно пусть будет пустым. Контрол назовём SimpleUserControl для примера.

Теперь мы в начале работы приложения создаём контрол, но не показываем его, таким образом:

public partial class App : Application
{
    protected override async void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var uc = new SimpleUserControl(); // создали контрол
                                          // возможно, надо установить DataContext
        var size = new Size(100, 100);    // фейковый размер
        uc.Measure(size);                 // заставляем пробежать layout engine
        uc.Arrange(new Rect(size));
        uc.UpdateLayout();
        // дождёмся выполнения пустой лямбды на фоновом приоритете,
        // это даст время привязкам разрешиться
        await Dispatcher.CurrentDispatcher.InvokeAsync(
            () => { },
            DispatcherPriority.Background);
        // теперь можно работать с контролом. я просто сделаю паузу для примера
        await Task.Delay(9000);
        // и вставить его в окно:
        new MainWindow() { Content = uc }.Show();
    }
}

При этом Loaded на контроле всё ещё не вызывается (оно вызовется только при показе), но у вас подконтролы будут находиться в «рабочем» состоянии.

Answer 2

Получил ответ на англ. SO. Этот же код работает для WPF если заменить FormWindowState на WindowState. Суть в том, что надо сначала чтобы перед "показом" установить (в коде или дизайнере):

SomeWindow.ShowInTaskbar = false;
SomeWindow.WindowState = FormWindowState.Minimized;

Затем выполнить код

SomeWindow.Show();
SomeWindow.Hide();

И сразу после этого подготавливаем окно для нормального отображения при последующих вызовах SomeWindow.Show:

SomeWindow.ShowInTaskbar = true; // Если надо чтобы форма показывалась на панели быстого запуска.
SomeWindow.WindowState = FormWindowState.Normal; // или FormWindowState.Maximized если надо

Это можно превратить в метод расширения для Windows Forms:

public static class FormHelper
{
    public static void ShowInvisible(this Form form)
    {
        // сохраняем параметры окна
        bool needToShowInTaskbar = form.ShowInTaskbar;
        WindowState initialWindowState = form.WindowState;
        // делаем окно невидимым
        form.ShowInTaskbar = false;
        form.WindowState = FormWindowState.Minimized;
        // показываем и скрываем окно
        form.Show();
        form.Hide();
        // восстанавливаем параметры окна
        form.ShowInTaskbar = needToShowInTaskbar;
        form.WindowState = initialWindowState;
    }
} 

или для WPF:

public static class WindowHelper
{
    public static void ShowInvisible(this Window window)
    {
        // сохраняем параметры окна
        bool needToShowInTaskbar = window.ShowInTaskbar;
        WindowState initialWindowState = window.WindowState;
        // делаем окно невидимым 
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;
        // показываем и скрываем окно
        window.Show();
        window.Hide();
        // восстанавливаем параметры окна
        window.ShowInTaskbar = needToShowInTaskbar;
        window.WindowState = initialWindowState;
    }
}
Answer 3

Как я понял, при создании формы:

  • её не должно быть видно,
  • должно сработать событие Load,
  • её иконка должна появиться на таскбаре,
  • форма не не должна мелькнуть.

По-моему, установка прозрачности в ноль перед показом, а после показа в единицу полностью решает проблему.

var form = new Form();
form.Opacity = 0;
form.Load += Form_Load;
form.Show();
form.WindowState = FormWindowState.Minimized;
form.Opacity = 1;

Событие вызывается, форма не мелькает, на таскбаре отображается.

READ ALSO
Не передаётся аргумент cmd

Не передаётся аргумент cmd

Самый простой способ сделать то, что вам нужно, такой:

317
Рисование фигур на элементе wpf

Рисование фигур на элементе wpf

Всем доброе время суток! Можно ли реализовать отрисовку различных фигур на каком-то элементе управления, для примера этот: <lvc:CartesianChart GridColumn="1"...

346
VS генерация имени поля исходи из типа

VS генерация имени поля исходи из типа

Есть в других IDE такая функция, которая предлагает название переменной исходя из ее типаПример:

318
Мобильная точка доступа WiFi

Мобильная точка доступа WiFi

Какие существуют способы создания мобильной точки доступа via C#?

322