2008-12-13 18 views
10

He estado usando Rainlendar durante algún tiempo y he notado que tiene una opción para poner la ventana "en el escritorio". Es como una ventana de bottomMost (en contraposición a la superior).Ventana "en el escritorio"

¿Cómo podría hacer esto en una aplicación WPF?

Gracias

Respuesta

14

Mi respuesta es, en términos de la API de Win32, no es específica de WPF (y que probablemente requiera P/Invoke desde C#):

Rainlendar tiene dos opciones:

  • " En el escritorio ", se convierte en un elemento secundario de la ventana del escritorio de Explorer (" Administrador de programas "). Puede lograr esto con la API SetParent.
  • "En la parte inferior" es lo que describes: sus ventanas permanecen en la parte inferior del orden Z, justo en frente del escritorio. Es bastante fácil ponerlos allí para empezar (ver SetWindowPos) - el truco es evitar que pasen al frente cuando se hace clic. Sugeriría manejar el mensaje WM_WINDOWPOSCHANGING.
+0

Gracias Hugh, tengo el "en la parte inferior" parte de trabajo, va a tratar con SetParant ahora. Básicamente "en el escritorio" está "en la parte inferior" sin minimizar ¿verdad? –

+0

No estoy seguro de a qué te refieres con "sin minimizar", pero si usas MS Spy ++ para examinar el árbol de la ventana (con Rainlendar ejecutándose) es de esperar que veas lo que quiero decir. –

7

Esto es lo que he usado así que la ventana es siempre "en la parte inferior":

using System; 
    using System.Runtime.InteropServices; 
    using System.Windows; 
    using System.Windows.Interop; 

...

[DllImport("user32.dll")] 
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, 
    int Y, int cx, int cy, uint uFlags); 

const UInt32 SWP_NOSIZE = 0x0001; 
const UInt32 SWP_NOMOVE = 0x0002; 
const UInt32 SWP_NOACTIVATE = 0x0010; 

static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 

public static void SetBottom(Window window) 
{ 
    IntPtr hWnd = new WindowInteropHelper(window).Handle; 
    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 
} 
+0

Esto no funciona en Windows 10 –

3

La versión OnDesktop que estoy usando:

[DllImport("user32.dll")] 
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); 
[DllImport("user32.dll", SetLastError = true)] 
static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

public static void SetOnDesktop(Window window) 
{ 
    IntPtr hWnd = new WindowInteropHelper(window).Handle;   
    IntPtr hWndProgMan = FindWindow("Progman", "Program Manager"); 
    SetParent(hWnd, hWndProgMan); 
} 

Estaba teniendo problemas para encontrar la ventana Administrador de programas, pero Kimmo, la c reator de Rainlendar me dio un enlace al código:

http://www.ipi.fi/~rainy/legacy.html

Si alguien necesita más detalle con tan sólo mirar en la biblioteca/rainwindow.cpp para los SetWindowZPos de función.

6

Advertencia La respuesta aceptada sugiere que llame a SetParent para crear un elemento secundario del escritorio. Si hace esto, hace que Win32 Window Manager sincronice la cola de entrada del Escritorio con su ventana secundaria, esto es Lo malo - Raymond Chen explains why. Básicamente, si su ventana se bloquea o bloquea (por ejemplo, con un MessageBox) se bloqueará en tu escritorio

+0

1. El "escritorio" al que me refiero no es la ventana raíz devuelta por GetDesktopWindow(). 2. Incluso si llama a SetParent (myWindow, GetDesktopWindow()), no causará ningún problema, solo hará que su ventana sea una ventana de "nivel superior", si no lo fuera aún. Creo que deberías leer ese artículo de Raymond Chen nuevamente. –

+0

Hugh, ¿realmente has intentado esto? Es fácil de reproducir. 1. Llame a SetParent con el "Administrador de programas" como padre. 2. Cuelgue la ventana de su hijo (duerma, lo que sea) 3. Tenga en cuenta que el escritorio ahora está colgado, no puede interactuar con él (atajos, etc.). –

+0

Está bien si cuelgas a un hijo de Progman cuelgas Progman, pero mostrar un MessageBox no lo hace, y es algo de lo que puedes recuperar de todos modos (matar el proceso). Confundiste el problema al vincularlo a un artículo que describe algo diferente (colgar toda la máquina al deshabilitar la ventana raíz). –

3

Yo estaba tratando de hacer lo mismo ... he usado muchas ideas alrededor, pero pude y evitar el parpadeo.

que lograron anular WndProc, utilizó uno SetWindowPos antes de ponerla en el fondo, y otro para evitar que se haga el foco ...

const UInt32 SWP_NOSIZE = 0x0001; 
    const UInt32 SWP_NOMOVE = 0x0002; 
    const UInt32 SWP_NOACTIVATE = 0x0010; 
    const UInt32 SWP_NOZORDER = 0x0004; 
    const int WM_ACTIVATEAPP = 0x001C; 
    const int WM_ACTIVATE = 0x0006; 
    const int WM_SETFOCUS = 0x0007; 
    static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 
    const int WM_WINDOWPOSCHANGING = 0x0046; 

    [DllImport("user32.dll")] 
    static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, 
     int Y, int cx, int cy, uint uFlags); 
    [DllImport("user32.dll")] 
    static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, 
     IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); 
    [DllImport("user32.dll")] 
    static extern IntPtr BeginDeferWindowPos(int nNumWindows); 
    [DllImport("user32.dll")] 
    static extern bool EndDeferWindowPos(IntPtr hWinPosInfo); 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     IntPtr hWnd = new WindowInteropHelper(this).Handle; 
     SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 

     IntPtr windowHandle = (new WindowInteropHelper(this)).Handle; 
     HwndSource src = HwndSource.FromHwnd(windowHandle); 
     src.AddHook(new HwndSourceHook(WndProc)); 
    } 

    private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     if (msg == WM_SETFOCUS) 
     { 
      IntPtr hWnd = new WindowInteropHelper(this).Handle; 
      SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 
      handled = true; 
     } 
     return IntPtr.Zero; 
    } 

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
    { 
     IntPtr windowHandle = (new WindowInteropHelper(this)).Handle; 
     HwndSource src = HwndSource.FromHwnd(windowHandle); 
     src.RemoveHook(new HwndSourceHook(this.WndProc)); 
    } 
0

La versión de propiedad adjunta de respuesta de @ HrejWaltz:

actualización (12/28/2016)

public class WindowSinker 
{ 
    #region Properties 

    const UInt32 SWP_NOSIZE = 0x0001; 
    const UInt32 SWP_NOMOVE = 0x0002; 
    const UInt32 SWP_NOACTIVATE = 0x0010; 
    const UInt32 SWP_NOZORDER = 0x0004; 
    const int WM_ACTIVATEAPP = 0x001C; 
    const int WM_ACTIVATE = 0x0006; 
    const int WM_SETFOCUS = 0x0007; 
    const int WM_WINDOWPOSCHANGING = 0x0046; 

    static readonly IntPtr HWND_BOTTOM = new IntPtr(1); 

    Window Window = null; 

    #endregion 

    #region WindowSinker 

    public WindowSinker(Window Window) 
    { 
     this.Window = Window; 
    } 

    #endregion 

    #region Methods 

    [DllImport("user32.dll")] 
    static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); 

    [DllImport("user32.dll")] 
    static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); 

    [DllImport("user32.dll")] 
    static extern IntPtr BeginDeferWindowPos(int nNumWindows); 

    [DllImport("user32.dll")] 
    static extern bool EndDeferWindowPos(IntPtr hWinPosInfo); 

    void OnClosing(object sender, System.ComponentModel.CancelEventArgs e) 
    { 
     var Handle = (new WindowInteropHelper(Window)).Handle; 

     var Source = HwndSource.FromHwnd(Handle); 
     Source.RemoveHook(new HwndSourceHook(WndProc)); 
    } 

    void OnLoaded(object sender, RoutedEventArgs e) 
    { 
     var Hwnd = new WindowInteropHelper(Window).Handle; 
     SetWindowPos(Hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 

     var Handle = (new WindowInteropHelper(Window)).Handle; 

     var Source = HwndSource.FromHwnd(Handle); 
     Source.AddHook(new HwndSourceHook(WndProc)); 
    } 

    IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     if (msg == WM_SETFOCUS) 
     { 
      hWnd = new WindowInteropHelper(Window).Handle; 
      SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); 
      handled = true; 
     } 
     return IntPtr.Zero; 
    } 

    public void Sink() 
    { 
     Window.Loaded += OnLoaded; 
     Window.Closing += OnClosing; 
    } 

    public void Unsink() 
    { 
     Window.Loaded -= OnLoaded; 
     Window.Closing -= OnClosing; 
    } 

    #endregion 
} 

public static class WindowExtensions 
{ 
    #region Always On Bottom 

    public static readonly DependencyProperty SinkerProperty = DependencyProperty.RegisterAttached("Sinker", typeof(WindowSinker), typeof(WindowExtensions), new UIPropertyMetadata(null)); 
    public static WindowSinker GetSinker(DependencyObject obj) 
    { 
     return (WindowSinker)obj.GetValue(SinkerProperty); 
    } 
    public static void SetSinker(DependencyObject obj, WindowSinker value) 
    { 
     obj.SetValue(SinkerProperty, value); 
    } 

    public static readonly DependencyProperty AlwaysOnBottomProperty = DependencyProperty.RegisterAttached("AlwaysOnBottom", typeof(bool), typeof(WindowExtensions), new UIPropertyMetadata(false, OnAlwaysOnBottomChanged)); 
    public static bool GetAlwaysOnBottom(DependencyObject obj) 
    { 
     return (bool)obj.GetValue(AlwaysOnBottomProperty); 
    } 
    public static void SetAlwaysOnBottom(DependencyObject obj, bool value) 
    { 
     obj.SetValue(AlwaysOnBottomProperty, value); 
    } 
    static void OnAlwaysOnBottomChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     var Window = sender as Window; 
     if (Window != null) 
     { 
      if ((bool)e.NewValue) 
      { 
       var Sinker = new WindowSinker(Window); 
       Sinker.Sink(); 
       SetSinker(Window, Sinker); 
      } 
      else 
      { 
       var Sinker = GetSinker(Window); 
       Sinker.Unsink(); 
       SetSinker(Window, null); 
      } 
     } 
    } 

    #endregion 
} 
Cuestiones relacionadas