2009-09-09 2 views
45

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

ответ

56

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

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

(благодаря Томасу Левескё для pointing this out.)

Если вы ориентируетесь старую версию .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(); 

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

+2

Да, это работает, спасибо. Установка WindowState даже не требуется. Кроме того, я установил содержимое окна в XAML, но это не важно. Другое дело, что WindowStartupLocation = CenterScreen работает неправильно, но это легко исправить. – svick

+0

удалил установщик WindowState ... спасибо, что позволил мне узнать. –

+1

Я бы добавил 'ResizeMode = ResizeMode.NoResize', потому что он удаляет границу окна для изменения размера. – schnaader

0

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

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

IntPtr hwnd = helper.Handle; 

MSDN Documentation

+1

Это то, что я делаю, но таким образом, Окно еще не имеет HWND, поэтому helper.Handle 0, что не то, что мне нужно. – svick

5

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

+0

Это своего рода грязный хак, но он будет работать точно. –

+0

К сожалению, для того, чтобы параметр Opacity был эффективным, AllowsTransparency должен быть установлен в true, а это, в свою очередь, вынуждает WindowStyle к WindowStyle.None, чего я не хочу. Кроме того, AllowsTransparency нельзя изменить после отображения окна, поэтому я не могу установить его обратно. – svick

0

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

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

16

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

  • Установите WindowStartupLocation в Manual
  • установить Top и Left свойства где-то за пределами экран
  • установить ShowInTaskbar ложь, так что пользователь не понимает, есть новое окно
  • Show и Hide й е окно

Теперь вы должны быть в состоянии восстановить HWND

EDIT: еще один вариант, вероятно, лучше: установить ShowInTaskBar ложь и WindowState в Minimized, а затем показать его: он не будет виден на всех

+2

+1 ShowInTaskBar = false и WindowState = Минимизированные работы. –

+2

С помощью вашего другого варианта я вижу окно, свернутое в левом нижнем углу экрана. Но первый выглядит многообещающим. – svick

+0

@svick: какую ОС вы используете? В Windows 7 свернутое окно не видно –

0

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

3

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

+0

+1, вы можете просто использовать окно Winforms в другом потоке, если хотите это сделать. –

15

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

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

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

+1

Это, кажется, единственное 100% решение. –

+0

Спасибо за этот замечательный совет.Это действительно помогло мне, потому что все другие решения здесь вызвали некоторые побочные эффекты, которые были не очень хорошими. Но, к сожалению, у вас тоже. У меня есть MetroWindow (с использованием Fluent Ribbon Suite). Впоследствии окно имеет типичную рамку окна, которая обычно не видна для этих MetroWindows ... Любая идея, как это решить? – SharpShade

+0

Perfect. Это просто нужно: ShowActivated = false; после видимости, потому что без него он мигает. – blez

10

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

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

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

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

+0

красиво сделано Томас – tofutim

+0

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

+0

@PatrickKlug, я тоже не знаю ... Вы можете включить это в свой ответ или просто указать мой ответ, в зависимости от того, что вы считаете лучшим. –

0

Я создал метод расширения для отображения невидимого окна, следующие вызовы 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; 
    } 
} 
Смежные вопросы