How to put a form behind desktop icons? - c#

How would you put a WinForms form behind desktop icons but in front of the wallpaper? To make the desktop the form's parent, I use:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
IntPtr desktopHandle = (IntPtr)FindWindow("Progman", null);
WallForm wallWindow = new WallForm();//WinForms Form
...
private void SwitchParent()
{
wallWindow.Show();
SetParent(wallWindow.Handle, desktopHandle);
//wallWindow.SendToBack();
}
This works, but it puts the form in front of the desktop icons. If I call SendToBack on my form, it disappears, presumably behind the wallpaper. How could I get the form to be between the icons and the desktop background?

I don't believe that this is possible to do. The desktop window is a single window that renders the desktop image and the icons, so there is no way to insert your window between the desktop image and the icons.
Short of writing a shell replacement that handled the background image and desktop icons differently (a major development task with many hurdles) the only other option I can think of is to hook into the desktop's events and intercept WM_ERASEBKGND or similar to do your own drawing. (See this question or this question for more info.)
Unfortunately this won't let you put a WinForm behind the icons, only an image. You'd have to handle a lot of other windows messages to simulate an actual form. It's a major undertaking regardless.

There is a solution to this problem, at least for Windows 8. I postet it in form of an article on CodeProject, so you can read about it here:
http://www.codeproject.com/Articles/856020/Draw-behind-Desktop-Icons-in-Windows
This works for simple drawing, windows forms, wpf, directx, etc. The solution presented in that article is only for Windows 8.

Related

Overlay a WPF window with a native window and enable some user controls being still above it

I have a WPF app and a SDL windows app, both used in fullscreen. I would like to host the SDL window inside the WPF app, but still having the ability to show some WPF user controls above it.
The SDL Windows app is launched by the WPF app and I am able to overlay it using the following method:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
So, my SDL window appears to be "integrated" to my WPF app by overlapping it. But I have a WPF user control I still would like to have on top.
To be more explicit, it's a SDL game with a WPF joystick control on top of the game, something like this :
How can I succeed such a thing?

Snap WinForm to program

Is it possible to do the following with WinForms/C#?
Dynamically detect window size and position of a running program (for example Notepad.exe)?
Snap WinForm to specific position within Notepad.exe?
Minimize and maximize WinForm window with other process (so when Notepad is minimize, so is WinForm window)?
See for example (black shape would be WinForm window):
Essentially I need to create a toolbar for a program, and the toolbar should "snap" to that program in the same place regardless of position or size of window.
First find the handle of the notepad window:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Just pass null for the first parameter and the caption ("Notepad"?) of the window as the second parameter.
An alternative would be to enumerate all windows and select the best match based on the caption:
using System.Runtime.InteropServices;
public delegate bool CallBackPtr(int hwnd, int lParam);
private CallBackPtr callBackPtr;
public class EnumReport
{
[DllImport("user32.dll")]
private static extern int EnumWindows(CallBackPtr callPtr, int lPar);
public static bool Report(int hwnd, int lParam)
{
Console.WriteLine("Window handle is "+hwnd);
return true;
}
}
static void Main()
{
// note in other situations, it is important to keep
// callBackPtr as a member variable so it doesnt GC while you're calling EnumWindows
callBackPtr = new CallBackPtr(EnumReport.Report);
EnumReport.EnumWindows(callBackPtr, 0);
}
Then attach a WndProc to it:
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
In the WndProc respond to the resizing and moving of the window.
I am not sure about setting the toolbar as a child of the notepad window; that might have unexpected effects when Notepad tries to manage it and order its z-depth.
At the same time I doubt this to be a good thing; the user will be able to type 'below' the overlay and lose his cursor/text.
Find Notepad's window (FindWindow).
Create your window without borders.
Set your window as a child of Notepad's window (SetParent).
Your window will be anchored to the top left corner of Notepad's window. Minimizing will be handled automatically, but you'll need to resize your window when Notepad's window is resized (or maximized). You may also want to move Notepad's edit control.
WinForms can be used, but you'll need some interop calls.
I have to warn that this is not a very good idea. Your controls may conflict with controls inside host process's window, host process may rearrange controls the way you don't like, draw over your controls. In general, be ready to fight with numerous issues without a good clean solution, and to accept that there may be glitches when resizing etc.
See also:
Attach form window to another window in C#.

Firing events when window is moved

I am currently developing an iTunes plug-in and I need to have my WPF plug-in to stick beside the iTunes window when dragged, resized, etc. The goal is to have iTunes sticked to my wpf app side by side.
I am looking for a way to track the movement (resizing, moving) of another window (in my case iTunes). The iTunes COM for Windows SDK offers events on maximize and minimize, unfortunately there are no events for resizing and moving the window.
I have tried the win32 setParent function without no succes. I don't think it's the appropriate solution for my problem. I have searched thoroughly through out the web but didn't find nothing.
I think the WINDOWPOS structure is what you're looking for. Other window structures may come in handy.
Some google searching turned up another example, that doesn't use WINDOWPOS:
1, Invoke API FindWindow() to retrieve the window handle (Uuse SPY++
to get the two parameters ClassName & WindowName);
2, Invoke API
GetWindowRect() to retrieve the size & postion of the specified
window.
Code Snippet
[DllImport("user32.dll")]
private static extern IntPtr FindWindow(string className, string windowName);
[DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hwnd, out Rectangle rect);
private void button2_Click(object sender, EventArgs e)
{
string className = "yourClassName";
string windowName = "yourWindowName";
Rectangle rect;
IntPtr hwnd = FindWindow(className, windowName);
GetWindowRect(hwnd, out rect);
}

Making a corner of the desktop activate screensaver

I am trying to write a simple application to activate my screensaver when the mouse in at the top right corner of the screen. I have found an answer to controlling the screensaver from C# however I am having trouble working out how to do a "hot corner" type check for the mouse position. This is the only part I am stuck with, any help would be appreciated.
This Activates the screensaver
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
public static void SetScreenSaverRunning()
{
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}
You could use the System.Windows.Form.Screen class to get the current resolution (take a look at this answer). Then use Cursor.Position.Property to determine where the cursor is currently located (i.e. is it within the boundaries of some predefined rectangle that should activate it).
I have made the exact same thing, only it loads in the top left. What I did was just make the form size 1px by 1px with no border, and just activate the screensaver when the mouse stays over the form for a second. Doing it this way requires that you find all ways to keep the form on top of everything.
Another option would be mouse hooking and just watching for (0,0) mouse position, or for the top right - (0, screen.width)
You could also try ScrHots from Lucian Wischik. It's freeware and does exactly what you need, and also has hot-corners for "never activate the screensaver" capability. All four corners can be programmed to do either function. I've used this one for years, and it works great.
http://www.wischik.com/scr/savers.html (ScrHots3, under the "Utilities" section)
Hope this helps someone.

How to add an extra button to the window's title bar?

I've seen that some apps (maybe not .NET apps) that have an extra button on the left from the minimize button on the form's title bar? How can I achieve this in C#?
UPDATE: Added a solution that will work with Aero enabled for Windows Vista and Windows 7
***Non-Aero Solution***
The non-client area of a window interaction is managed by a series of non-client specfic messages. For example WM_NCPAINT message is sent to the window procedure to paint the non-client area.
I have never done this from .NET, but I suspect you can overide the WndProc and handle the WM_NC* messages to achieve what you want.
Update: Since I never tried this from .NET I got a few minutes and thought I would give it a quick try.
Trying this on Windows 7, I found that I needed to disable the Themes for the Window if I wanted to OS to do the base rendering of the non-client area. So here is a short test. I used GetWindowDC to get the DC of the entire window rather than GetDCEx, that was just because I could interop that from memory and did not have lookup all the flag constants for GetDcEx. And of course the code could do with more error checking.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class CustomBorderForm : Form
{
const int WM_NCPAINT = 0x85;
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);
[DllImport("user32.dll", SetLastError = true)]
public static extern void DisableProcessWindowsGhosting();
[DllImport("UxTheme.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList);
public CustomBorderForm()
{
// This could be called from main.
DisableProcessWindowsGhosting();
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCPAINT:
{
IntPtr hdc = GetWindowDC(m.HWnd);
using (Graphics g = Graphics.FromHdc(hdc))
{
g.FillEllipse(Brushes.Red, new Rectangle((Width-20)/2, 8, 20, 20));
}
ReleaseDC(m.HWnd, hdc);
}
break;
}
}
}
}
Btw. I called DisableProcessWindowsGhosting, this will stop the OS from drawing the non-client area if the application takes too long to respond to windows messages. If you do not do this, then in some situations the border will be renderd but your adornments will not be shown. So that depends on your requirements it that is right for you or not.
***Aero supported solution***
Prompted by the comment from #TheCodeKing, I thought I would take another look at this. It turns out this can be done in a fully documented way while supporting Aero. But it is not for the faint of heart. I will not provide a complete solution here, there are still some kinks to workout, but it does the basics.
This code/solution is based off the Win32 example which can be found at the following location
http://msdn.microsoft.com/en-us/library/bb688195(VS.85).aspx
In principal what you need to do is the following.
Extend the client area of the window to cover the Frame. This is done by handling the WM_NCCALCSIZE message and returning 0. This gives the Non-Client area a size of 0 and therefore the client area now covers the entire window.
Extend the Frame into the client area using DwmExtendFrameIntoClientArea. This gets the OS to render the Frame over the client area.
The above steps will give you a windows with the standard glass frame excluding the system menu (Window Icon) and the title. The minimize, maximize and close buttons will still be drawn and will work. What you will not be able to do is drag or resize the window, this is because the frame is not really there, remember the client area covers the whole window, we have just asked the OS to draw the frame onto the client area.
Now you can draw on the window as normal, even on top of the frame. You can even put controls in the caption area.
Finally, allow the DWM to handle hit-testing for you, by calling DwmDefWindowProc from your WndProc (before you've processed it). It returns a boolean indicating whether the DWM handled the message for you.
Simple Solution:
Step 1: Create a Windows Form (this will be your custom title bar)
-Set Form Border Style to None
-Add whatever controls you would like to this
-I will name this custom form "TitleBarButtons"
Step 2. In the from that you want to use this custom control in add
titleBarBtn = new TitleBarButtons();
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
titleBarBtn.Show();
titleBarBtn.Owner = this;
To your constructor... you can mess with the offsets this just fit in a nice position for my app
Step 3. Add the move event to your main form
private void Form14_Move(object sender, EventArgs e)
{
titleBarBtn.Location = new Point(this.Location.X + 100, this.Location.Y+5);
}
Please let me know if you would like a better explanation of any of the above code.
I think a way to do this would be to handle WM_NCPAINT message (non-client paint) to draw the button, and to handle non-client mouse clicks to know someone clicked on the "button".

Categories