tags:

views:

1229

answers:

4

Hello, I've been using Rainlendar for some time and I noticed that it has an option to put the window "on desktop". It's like a bottomMost window (as against topmost).

How could I do this on a WPF app?

Thanks

+4  A: 

My answer is in terms of the Win32 API, not specific to WPF (and probably requiring P/Invoke from C#):

Rainlendar has two options:

  • "On Desktop", it becomes a child of the Explorer desktop window ("Program Manager"). You could achieve this with the SetParent API.
  • "On Bottom" is what you describe - its windows stay at the bottom of the Z-order, just in front of the desktop. It's easy enough to put them there to begin with (see SetWindowPos) - the trick is to stop them coming to the front when clicked. I would suggest handling the WM_WINDOWPOSCHANGING message.
Hugh Allen
Thanks Hugh, I have the "on bottom" part working, going to try with SetParant now.Basically "on desktop" is "on bottom" without minimizing right?
Artur Carvalho
I'm not sure what you're getting at with "without minimizing", but if you use MS Spy++ to examine the window tree (with Rainlendar running) you will hopefully see what I mean.
Hugh Allen
+1  A: 

This is what I used so the window is always "on bottom":

   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);
}
Artur Carvalho
A: 

The OnDesktop version that Im using:

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

I was having some trouble finding the Program Manager window, but Kimmo, the creator from Rainlendar gave me a link to the code:

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

If anybody needs more detail just look in library/rainwindow.cpp for the function SetWindowZPos.

Artur Carvalho
+2  A: 

Warning The accepted answer suggests that you call SetParent to create a child of the Desktop. If you do this, you cause the Win32 Window Manager to synchronize the input queue of the Desktop to your child window, this is a bad thing - Raymond Chen explains why. Basically, if your window hangs or blocks (say with a MessageBox) you will lock up your desktop.

Mo Flanagan
1. The "desktop" I refer to is not the root window returned by GetDesktopWindow().2. Even if you call SetParent(myWindow, GetDesktopWindow()) it won't cause a problem - it will just make your window a "top level" window, if it wasn't already.I think you should read that Raymond Chen article again.
Hugh Allen
Hugh, did you actually try this - its easy to reproduce. 1. Call SetParent with the "Program Manager" as the parent. 2. Hang your child window (do a sleep, whatever) 3. Note that the desktop is now hung, you cannot interact with it (shortcuts etc).
Mo Flanagan
OK if you hang a child of Progman you hang Progman, but showing a MessageBox doesn't do it, and it's something you can recover from anyway (kill the process). You confused the issue by linking to an article which describes something different (hanging the whole machine by disabling the root window).
Hugh Allen
(by "showing a MessageBox" I of course mean that the child window calls MessageBox() with itself as the owner, not Progman)
Hugh Allen