Domanda

creo un tasto di scelta rapida globale per mostrare una finestra PInvoking RegisterHotKey(). Ma per fare questo ho bisogno HWND di quella finestra, che non esiste finché non viene caricata la finestra, che significa mostrato per la prima volta. Ma io non voglio mostrare la finestra prima di poter impostare il tasto di scelta rapida. Esiste un modo per creare un HWND per quella finestra che è invisibile per l'utente?

È stato utile?

Soluzione

Se si prendono di mira .NET 4.0 si può fare uso del nuovo metodo EnsureHandle disponibili sul WindowInteropHelper:

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

(grazie a Thomas Levesque per puntualizzazione. )

Se si prendono di mira una versione precedente di .NET Framework, il modo più semplice è quello di mostrare la finestra per raggiungere la HWND durante l'impostazione alcune proprietà per assicurarsi che la finestra è invisibile e non rubare messa a fuoco:

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

Una volta che si desidera visualizzare la finestra attuale è quindi possibile impostare il contenuto, le dimensioni e cambiare lo stile di nuovo a una finestra normale.

Altri suggerimenti

È inoltre possibile modificare la finestra in una cosiddetta finestra di messaggio-solo. Poiché questo tipo di finestra non supporta gli elementi grafici non sarà mai mostrato. In sostanza si tratta di chiamare:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

In entrambi creare una finestra messaggio dedicato che sarà sempre nascosto, o utilizzare la finestra vera e propria GUI e cambiare di nuovo a una finestra normale quando si desidera visualizzarlo. Vedere il codice qui sotto per un esempio più completo.

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

Per me la soluzione di impostare la larghezza, l'altezza a zero e lo stile a nessuno, non ha funzionato, in quanto mostrava ancora una piccola finestra, con un'ombra di fastidioso di quello che sembra essere il bordo intorno una finestra 0x0 (testati su Windows 7). Perciò sto fornendo questa opzione alternativa.

Questo è un trucco sporco, ma dovrebbe funzionare, e non ha gli aspetti negativi di cambiare l'opacità:

  • impostare il WindowStartupLocation a Manual
  • impostare le proprietà Top e Left a qualche parte di fuori dello schermo
  • set ShowInTaskbar su false in modo che l'utente non si rende conto c'è una nuova finestra
  • Show e Hide la finestra

Si dovrebbe ora essere in grado di recuperare il HWND

EDIT: un'altra opzione, probabilmente meglio: impostare ShowInTaskBar su false e WindowState a Minimized, poi mostrarlo: non sarà visibile a tutti

avevo già pubblicato in risposta a questa domanda, ma ho appena trovato una soluzione migliore.

Se avete solo bisogno di assicurarsi che l'HWND è creato, senza in realtà che mostra la finestra, si può fare questo:

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

(in realtà il metodo EnsureHandle non era disponibile quando la questione è stata pubblicata, è stato introdotto in .NET 4.0)

Non ho mai provato a fare quello che stai facendo, ma se avete bisogno di mostrare la finestra per ottenere il HWND, ma non si vuole Mostra è, impostare l'opacità della finestra a 0 . Questo sarà anche evitare di rilevamento del contatto si verifichi. Poi si potrebbe avere un metodo pubblico sulla finestra per cambiare l'opacità al 100 quando si vuole renderlo visibile.

so assolutamente nulla di WPF, ma potreste creare un messaggio unica finestra con altri mezzi (PInvoke per esempio) per ricevere il messaggio WM_HOTKEY? Se sì, allora una volta che si riceve il WM_HOTKEY, si potrebbe avviare la finestra WPF da lì.

La classe WindowInteropHelper dovrebbe permettere di ottenere il HWND per la finestra WPF.

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

IntPtr hwnd = helper.Handle;

MSDN documentazione

Un'altra opzione in modo simile a impostare l'opacità a 0, è quello di impostare la dimensione di 0 e impostare la posizione di essere fuori dallo schermo. Questo non richiederà l'AllowsTransparency = true.

Ricorda inoltre che una volta che avete dimostrato una volta è possibile nasconderlo e ancora ottenere il hwnd.

Fare la dimensione della finestra 0 x 0 px, messo ShowInTaskBar false, mostrarlo, quindi ridimensionare quando necessario.

Ho creato metodo di estensione per mostrare finestra invisibile, chiamate Show prossimi si comporteranno OK.

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

Ho notato che l'ultima cosa che si verifica quando la finestra viene inizializzato, è il cambiamento di WindowState, se diversa dal normale. Così, si può effettivamente fare uso di esso:

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

Questo funziona abbastanza giusto per me. E non richiede di lavorare con qualsiasi maniglie e roba, e, soprattutto, non richiede di avere una classe personalizzata per una finestra. Che è grande per XAML caricato dinamicamente. Ed è anche un ottimo modo se si stanno facendo un'applicazione a schermo intero. Non c'è nemmeno bisogno di cambiare il suo stato di nuovo al normale o impostare la larghezza e l'altezza corretta. Basta andare con il

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

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

E il gioco è fatto.

E anche se mi sbaglio nel mio presupposto che il cambiamento di stato è l'ultima cosa fatta quando viene caricata la finestra, è ancora possibile modificare in qualsiasi altro caso, non ha molta importanza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top