Загрузка окна WPF без его отображения

Я создаю глобальную горячую клавишу, чтобы показать окно с помощью PInvoking RegisterHotKey(). Но для этого мне нужно это окно HWND, который не существует, пока окно не загружено, это означает, что показано в первый раз. Но я не хочу показывать окно до того, как смогу установить горячую клавишу. Есть ли способ создать HWND для этого окна, которое невидимо для пользователя?

11 ответов


если вы нацелены на .NET 4.0, вы можете использовать новый EnsureHandle метод доступен на WindowInteropHelper:

public void InitHwnd()
{
    var helper = new WindowInteropHelper(this);
    helper.EnsureHandle();
}

(спасибо Томасу Левеску за указали на это.)

если вы нацелены на более старую версию .NET Framework, самый простой способ-показать окно, чтобы добраться до HWND, установив несколько свойств, чтобы убедиться, что окно невидимо и не крадет фокус:

var window = new Window() //make sure the window is invisible
{
    Width = 0,
    Height = 0,
    WindowStyle = WindowStyle.None,
    ShowInTaskbar = false,
    ShowActivated = false
};
window.Show();

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


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

  • установить WindowStartupLocation до Manual
  • установить Top и Left Свойства где-то за пределами экрана
  • set ShowInTaskbar в false, чтобы пользователь не понял, что есть новое окно
  • Show и Hide окне

теперь вы должны иметь возможность получить Его hwnd

EDIT: другой вариант, возможно, лучше: set ShowInTaskBar false и WindowState to Minimized, затем покажите его : он не будет виден вообще


вы также можете изменить окно в так называемое окно только для сообщений. Поскольку этот тип окна не поддерживает графические элементы, он никогда не будет показан. В основном это сводится к вызову:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

либо создайте выделенное окно сообщения, которое всегда будет скрыто, либо используйте реальное окно GUI и измените его обратно на обычное окно, когда вы хотите его отобразить. Более полный пример см. В приведенном ниже коде.

    [DllImport("user32.dll")]
    static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);

    private const int HWND_MESSAGE = -3;

    private IntPtr hwnd;
    private IntPtr oldParent;

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;

        if (hwndSource != null)
        {
            hwnd = hwndSource.Handle;
            oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
            Visibility = Visibility.Hidden;
        }
    }

    private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
    {
        SetParent(hwnd, oldParent);
        Show();
        Activate();
    }

для меня решение задания ширина, высота до нуля и стиль до none не сработали, так как он все еще показывал крошечное окно с раздражающей тенью того, что кажется границей вокруг окна 0x0 (протестировано на Windows 7). Поэтому я предлагаю альтернативный вариант.


Я уже отвечал на этот вопрос, но я нашел лучшее решение.

Если вам просто нужно убедиться, что HWND создан, фактически не показывая окно, вы можете сделать это:

    public void InitHwnd()
    {
        var helper = new WindowInteropHelper(this);
        helper.EnsureHandle();
    }

(фактически EnsureHandle метод не был доступен, когда вопрос был опубликован, он был представлен в .NET 4.0)


Я никогда не пытался делать то, что вы делаете, но если вам нужно показать окно, чтобы получить HWND, но не хочу показать it, установите непрозрачность окна в 0. Это также предотвратит любые испытания на попадание. Тогда у вас может быть открытый метод в окне, чтобы изменить непрозрачность на 100, когда вы хотите сделать его видимым.


Я абсолютно ничего не знаю о WPF, но не могли бы вы создать Окно только для сообщений используя другие средства (например, PInvoke) для получения сообщения WM_HOTKEY? Если да, то после получения WM_HOTKEY вы можете запустить окно WPF оттуда.


класс WindowInteropHelper должен позволить вам получить HWND для окна WPF.

MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);

IntPtr hwnd = helper.Handle;

документация MSDN


другой вариант в аналогичном ключе для установки непрозрачности 0, чтобы установить размер 0 и установить положение, чтобы быть вне экрана. Для этого не потребуется AllowsTransparency = True.

также помните, что после того, как вы показали его один раз, затем вы можете скрыть его и получить его hwnd.


сделайте размер окна 0 x 0 px, поместите ShowInTaskBar в false, покажите его, а затем измените его размер при необходимости.


Я создал метод расширения для отображения невидимого окна, далее Show вызовы будут вести себя хорошо.

public static class WindowHelper
{
    public static void ShowInvisible(this Window window)
    {
        // saving original settings
        bool needToShowInTaskbar = window.ShowInTaskbar;
        WindowState initialWindowState = window.WindowState;

        // making window invisible
        window.ShowInTaskbar = false;
        window.WindowState = WindowState.Minimized;

        // showing and hiding window
        window.Show();
        window.Hide();

        // restoring original settings
        window.ShowInTaskbar = needToShowInTaskbar;
        window.WindowState = initialWindowState;
    }
}

Я заметил, что последнее, что происходит, когда окно инициализируется, это изменение WindowState, если оно отличается от нормального. Таким образом, вы можете использовать его:

public void InitializeWindow(Window window) {
    window.Top = Int32.MinValue;
    window.Left = Int32.MinValue;

    window.Width = 0;
    window.Height = 0;

    window.ShowActivated = false;
    window.ShowInTaskbar = false;
    window.Opacity = 0;

    window.StateChanged += OnBackgroundStateChanged;

    window.WindowStyle = WindowStyle.None;
}

public void ShowWindow(Window window) {
    window.Show();
    window.WindowState = WindowState.Maximized;
}

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.Top = 300;
        window.Left = 200;

        window.Width = 760;
        window.Height = 400;

        window.WindowState = WindowState.Normal;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

работает достаточно справедливо для меня. И это не требует работы с какими-либо ручками и вещами, и, что более важно, не требует наличия пользовательского класса для окна. Что отлично подходит для динамически загружаемого XAML. И это также отличный способ, если вы делаете полноэкранных приложений. Вы не даже нужно изменить свое состояние на нормальное или установить правильную ширину и высоту. Просто иди с

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

и вы сделали.

и даже если я ошибаюсь в своем предположении, что изменение состояния-последнее, что делается при загрузке окна, вы все равно можете перейти на любое другое событие, это не имеет значения.