2010-07-10 15 views
7

Ho bisogno di accedere agli handle della finestra Win32 di alcune delle mie finestre WPF in modo da poter gestire i messaggi di attivazione di Win32. So che posso usare PresentationSource.FromVisual o WindowInteropHelper per ottenere l'handle della finestra Win32, ma sto incontrando problemi se la finestra WPF non è stata ancora creata.Forzare la creazione di un handle Win32 nativo della finestra WPF

Se utilizzo PresentationSource.FromVisual e la finestra non è stata creata, il valore restituito PresentationSource è nullo. Se utilizzo WindowInteropHelper e la finestra non è stata creata, la proprietà Handle è IntPtr.Zero (null).

Ho provato a chiamare this.Show() e this.Hide() nella finestra prima di provare ad accedere all'handle. Posso quindi ottenere la maniglia, ma la finestra lampeggia momentaneamente sullo schermo (brutto!).

Qualcuno sa di un modo per forzare la creazione di una finestra WPF? In Windows Form questo era facile come accedere alla proprietà Form.Handle.

Modifica: Ho finito per andare con una variante sulla risposta di Chris Taylor. Qui è, nel caso in cui aiuta a qualcun altro:

static void InitializeWindow(Window window) 
{ 
    // Get the current values of the properties we are going to change 
    double oldWidth = window.Width; 
    double oldHeight = window.Height; 
    WindowStyle oldWindowStyle = window.WindowStyle; 
    bool oldShowInTaskbar = window.ShowInTaskbar; 
    bool oldShowActivated = window.ShowActivated; 

    // Change the properties to make the window invisible 
    window.Width = 0; 
    window.Height = 0; 
    window.WindowStyle = WindowStyle.None; 
    window.ShowInTaskbar = false; 
    window.ShowActivated = false; 

    // Make WPF create the window's handle 
    window.Show(); 
    window.Hide(); 

    // Restore the old values 
    window.Width = oldWidth; 
    window.Height = oldHeight; 
    window.WindowStyle = oldWindowStyle; 
    window.ShowInTaskbar = oldShowInTaskbar; 
    window.ShowActivated = oldShowActivated; 
} 

// Use it like this: 
InitializeWindow(myWpfWindow); 
+0

Hai visto questa domanda - http://stackoverflow.com/questions/1556182/finding-the-handle-to-a-wpf-window - potrebbe non essere d'aiuto in quanto non menziona se la finestra esiste già o no. – ChrisF

+0

@ChrisF: Grazie! Sì, l'ho visto. Sfortunatamente, ha il problema con la finestra non ancora creata. –

+0

Ho pensato di parlarne per ottenere una risposta in modo che se il tuo caso fosse diverso non sarebbe stato selezionato come duplicato. – ChrisF

risposta

3

Una possibilità è quella di impostare lo stato della finestra a ridotto al minimo e non mostrare nella barra delle applicazioni prima di mostrare la finestra. Prova qualcosa di simile.

IntPtr hWnd; 
    WindowInteropHelper helper = new WindowInteropHelper(wnd); 

    WindowState prevState = wnd.WindowState; 
    bool prevShowInTaskBar = wnd.ShowInTaskbar; 

    wnd.ShowInTaskbar = false; 
    wnd.WindowState = WindowState.Minimized; 
    wnd.Show(); 
    hWnd = helper.Handle; 
    wnd.Hide(); 

    wnd.ShowInTaskbar = prevShowInTaskBar; 
    wnd.WindowState = prevState; 
+0

Grazie! Ho finito per andare con una variante di questo (vedi la mia modifica sulla domanda). –

+2

[@ La risposta di DanielAlbuschat] (http://stackoverflow.com/a/4826741/24874) è più semplice ed evita lo sfarfallio. –

+0

Sì, la risposta sotto è molto più pulita. –

17

Utilizzare WindowInteropHelper.EnsureHandle, fa esattamente quello che ti serve.

+0

+1 Ottima scoperta! Dovrò provarlo. –

+0

Ho lo stesso problema, e questa è la migliore risposta! – laishiekai

+0

Ancora una volta, questa è una risposta molto migliore di quella accettata! Per favore usa questo invece, tutti! –

2

Stavo cercando una soluzione se l'handle di WindowInteropHelper è NULL. Speriamo che questo post fornisca alcune informazioni aggiuntive su come risolverlo.

Una soluzione è quella di utilizzare:

var window = new Window(); 
var handle = new WindowInteropHelper(window).EnsureHandle() 

questo funziona solo con .NET Framework 4.

Al momento sto utilizzando .NET Framework 3.5 quindi ho bisogno di un'altra soluzione. Poi ho trovato un thread del forum con un metodo di estensione WindowInteropHelper:

#region 

using System; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Interop; 

#endregion 

namespace System.Windows.Interop 
{ 
    /// <summary> 
    /// Provides NetFX 4.0 EnsureHandle method for 
    /// NetFX 3.5 WindowInteropHelper class. 
    /// </summary> 
    public static class WindowInteropHelperExtension 
    { 
     /// <summary> 
     /// Creates the HWND of the window if the HWND has not been created yet. 
     /// </summary> 
     /// <param name = "helper">An instance of WindowInteropHelper class.</param> 
     /// <returns>An IntPtr that represents the HWND.</returns> 
     /// <remarks> 
     /// Use the EnsureHandle method when you want to separate 
     /// window handle (HWND) creation from the 
     /// actual showing of the managed Window. 
     /// </remarks> 
     public static IntPtr EnsureHandle(this WindowInteropHelper helper) 
     { 
      if (helper == null) 
       throw new ArgumentNullException("helper"); 

      if (helper.Handle == IntPtr.Zero) 
      { 
       var window = (Window) typeof (WindowInteropHelper).InvokeMember(
        "_window", 
        BindingFlags.GetField | 
        BindingFlags.Instance | 
        BindingFlags.NonPublic, 
        null, helper, null); 

       typeof (Window).InvokeMember(
        "SafeCreateWindow", 
        BindingFlags.InvokeMethod | 
        BindingFlags.Instance | 
        BindingFlags.NonPublic, 
        null, window, null); 
      } 

      return helper.Handle; 
     } 
    } 
} 

Il WindowInteropHelper.EnsureHandle() non si aspetta una finestra è già stato creato.

Riferimento: Alexander Yudakov - http://social.msdn.microsoft.com/Forums/en-MY/wpf/thread/5f89ac58-d2ef-4ac0-aefb-b2826dbef48a

0

sono rimasto bloccato su questo stesso problema e sono andato con J Pollack's answer (perché sembra più pulito per me), ma bisogno di qualcosa che avrebbe eseguito sia sul runtime .NET 2.0 e 4.0.

Ma quando l'ho fatto ho finito con una brutta MissingMethodException perché SafeCreateWindow non esiste in .NET runtime 4.0 più. Per rendere il codice di lavoro su entrambi i tempi di esecuzione, ho deciso di prendere il MissingMethodException e richiamare l'equivalente nel runtime .NET 4.0, invece in questo modo:

public static IntPtr EnsureHandle(this WindowInteropHelper helper) 
    { 
     if (helper == null) 
      throw new ArgumentNullException("helper"); 

     if (helper.Handle == IntPtr.Zero) 
     { 
      var window = (Window)typeof(WindowInteropHelper).InvokeMember(
       "_window", 
       BindingFlags.GetField | 
       BindingFlags.Instance | 
       BindingFlags.NonPublic, 
       null, helper, null); 

      try 
      { 
       // SafeCreateWindow only exists in the .NET 2.0 runtime. If we try to 
       // invoke this method on the .NET 4.0 runtime it will result in a 
       // MissingMethodException, see below. 
       typeof(Window).InvokeMember(
        "SafeCreateWindow", 
        BindingFlags.InvokeMethod | 
        BindingFlags.Instance | 
        BindingFlags.NonPublic, 
        null, window, null); 
      } 
      catch (MissingMethodException) 
      { 
       // If we ended up here it means we are running on the .NET 4.0 runtime, 
       // where the method we need to call for the handle was renamed/replaced 
       // with CreateSourceWindow. 
       typeof(Window).InvokeMember(
        "CreateSourceWindow", 
        BindingFlags.InvokeMethod | 
        BindingFlags.Instance | 
        BindingFlags.NonPublic, 
        null, window, new object[] { false }); 
      } 
     } 

     return helper.Handle; 
    } 

Questo mi ha permesso di compilare il codice con .NET 3.5, ma eseguito su Runtime .NET 4.0 su sistemi su cui è installata solo la versione runtime più alta (ovvero Windows 8 e versioni successive).

Problemi correlati