I want to make a program to display information while I'm in another full screen application.
Something like: show what song is playing(from Winamp) while in Starcraft II.
How would I got about doing that?
I would start by creating a new window with the topmost flag set. I think that will do what you want. You might have to manually move that window to the top of the topmost z order with a call to SetWindowPos. (SetWindowPos is a win32 call, but you can pInvoke it from C# if needed.)
Related
My C# project starts some other processes, which create windows of their own. Displaying all those windows is messy - is there a way to clip/mask these windows (which my project does not own) so only a small portion of them is displayed and able to be interacted with?
E.g. something one'd pass a wHnd together with a Rectangle.
Is this a thing that exists?
Very, very many thanks to #Michael Gunter, MDI indeed allows to make such constructions!
For anyone who stumbles into this question years from now, here's what works:
IntPtr finalhWnd; // the window to clip's handle
Form mdiP = new MDIParent(); // MDIParent is custom, just a simple form without a border
mdiP.Show();
SetParent(finalhWnd, mdiP.Handle); // set the target form as a child of our own
SetWindowPos(finalhWnd, (IntPtr)0, -30, -50, 100, 100, 0x0200); // clip a 100x100 block, starting at (30,50)
Application.Run();
Here's an example of using it to clip the borders off of a Chrome instance:
I want to draw directly on the desktop in C#. From searching a bit, I ended up using a Graphics object from the Desktop HDC (null). Then, I painted normally using this Graphics object.
The problem is that my shapes get lost when any part of the screen is redrawn. I tried a While loop, but it actually ends up drawing as fast as the application can, which is not the update rate of the desktop.
Normally, I would need to put my drawing code in a "OnPaint" event, but such thing does not exist for the desktop.
How would I do it?
Example code: https://stackoverflow.com/questions/1536141/how-to-draw-directly-on-the-windows-desktop-c
I posted two solutions for a similar requirement here
Basically you have two options.
1- Get a graphics object for the desktop and start drawing to it. The problem is if you need to start clearing what you have previously drawn etc.
Point pt = Cursor.Position; // Get the mouse cursor in screen coordinates
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
g.DrawEllipse(Pens.Black, pt.X - 10, pt.Y - 10, 20, 20);
}
2- The second option that I provide in the link above is to create a transparent top-most window and do all your drawing in that window. This basically provides a transparent overlay for the desktop which you can draw on. One possible downside to this, as I mention in the original answer, is that other windows which are also top-most and are created after your app starts will obscure your top most window. This can be solved if it is a problem though.
For option 2, making the form transparent is as simple as using a transparency key, this allows mouse clicks etc. to fall through to the underlying desktop.
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window.
subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
To get an OnPaint for the desktop you would need to subclass the desktop window and use your drawing logic when it receives a WM_PAINT/WM_ERASEBKGND message.
As the thread you linked to says, you can only intercept messages sent to a window of an external process using a hook (SetWindowsHookEx from a DLL).
As mentioned a transparent window is another way to do it, or (depending on the update frequency) copying, drawing and setting a temporary wallpaper (as bginfo does).
This is difficult to do correctly.
It will be far easier, and more reliable, to make your own borderless form instead.
I want to draw directly on the desktop in C#. From searching a bit, I ended up using a Graphics object from the Desktop HDC (null). Then, I painted normally using this Graphics object.
The problem is that my shapes get lost when any part of the screen is redrawn. I tried a While loop, but it actually ends up drawing as fast as the application can, which is not the update rate of the desktop.
Normally, I would need to put my drawing code in a "OnPaint" event, but such thing does not exist for the desktop.
How would I do it?
Example code: https://stackoverflow.com/questions/1536141/how-to-draw-directly-on-the-windows-desktop-c
I posted two solutions for a similar requirement here
Basically you have two options.
1- Get a graphics object for the desktop and start drawing to it. The problem is if you need to start clearing what you have previously drawn etc.
Point pt = Cursor.Position; // Get the mouse cursor in screen coordinates
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
g.DrawEllipse(Pens.Black, pt.X - 10, pt.Y - 10, 20, 20);
}
2- The second option that I provide in the link above is to create a transparent top-most window and do all your drawing in that window. This basically provides a transparent overlay for the desktop which you can draw on. One possible downside to this, as I mention in the original answer, is that other windows which are also top-most and are created after your app starts will obscure your top most window. This can be solved if it is a problem though.
For option 2, making the form transparent is as simple as using a transparency key, this allows mouse clicks etc. to fall through to the underlying desktop.
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window.
subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
To get an OnPaint for the desktop you would need to subclass the desktop window and use your drawing logic when it receives a WM_PAINT/WM_ERASEBKGND message.
As the thread you linked to says, you can only intercept messages sent to a window of an external process using a hook (SetWindowsHookEx from a DLL).
As mentioned a transparent window is another way to do it, or (depending on the update frequency) copying, drawing and setting a temporary wallpaper (as bginfo does).
This is difficult to do correctly.
It will be far easier, and more reliable, to make your own borderless form instead.
I have a C# Windows application that I want to ensure will show up on a second monitor if the user moves it to one. I need to save the main form's size, location and window state - which I've already handled - but I also need to know which screen it was on when the user closed the application.
I'm using the Screen class to determine the size of the current screen but I can't find anything on how to determine which screen the application was running on.
Edit: Thanks for the responses, everyone! I wanted to determine which monitor the window was on so I could do proper bounds checking in case the user accidentally put the window outside the viewing area or changed the screen size such that the form wouldn't be completely visible anymore.
You can get an array of Screens that you have using this code.
Screen[] screens = Screen.AllScreens;
You can also figure out which screen you are on, by running this code (this is the windows form you are on)
Screen screen = Screen.FromControl(this); //this is the Form class
in short check out the Screen class and static helper methods, they might help you.
MSDN Link, doesn't have much..I suggest messing around in the code by yourself.
If you remember the window's location and size, that will be enough. When you set the position to the previously used position, if it happened to be on the second monitor it will go back there.
For example, if you have 2 monitors, both sized 1280x1024 and you set your window's left position to be 2000px, it will appear on the second monitor (assuming the second monitor is to the right of the first.) :)
If you are worried about the second monitor not being there when the application is started the next time, you can use this method to determine if your window intersects any of the screens:
private bool isWindowVisible(Rectangle rect)
{
foreach (Screen screen in Screen.AllScreens)
{
if (screen.Bounds.IntersectsWith(rect))
return true;
}
return false;
}
Just pass in your window's desired location and it will tell you if it will be visible on one of the screens. Enjoy!
You can get the current Screen with
var s = Screen.FromControl(this);
where this is the Form (or any control on the Form). As about how to remember that is a little tricky, but I would go for the index in the Screen.AllScreens array, or maybe s.DeviceName. In either case, check before using the settings on startup, to prevent using a monitor that was disconnected.
The location of the form will tell you which screen the form is on. I don't really understand why you'd need to know what screen it is on, because if you restore it using the location you saved it should just restore to the same location (maybe you can expand as to why).
Otherwise you can do something like this:
Screen[] scr = Screen.AllScreens;
scr[i].Bounds.IntersectsWith(form.Bounds);
Each screen has a Bounds property which returns a Rectangle. You can use the IntersectsWith() function to determine if the form is within the screen.
Also, they basically provide a function that does this as well on the Screen class
Screen screen = Screen.FromControl(form);
You can use the 'Screen' object:
System.Windows.Forms.Screen
Start playing with something like this:
Screen[] screens = Screen.AllScreens;
for (int i = 0; i < screens.Length ; i++)
{
Debug.Print(screens[i].Bounds.ToString());
Debug.Print(screens[i].DeviceName);
Debug.Print(screens[i].WorkingArea.ToString());
}
It may get you what you need
So I have two forms, mainform and extraform.
extraform is set always moved to the right of mainform when mainform initializes
Sometimes mainform takes up both monitors and extraform is pushed off the screen never to be seen again. I would like to prevent this if possible. How can I do so? It must support dual monitors, that may or may not have distance between them (i.e. screen 1 is 20px to the left of screen 2).
How can I do this?
You can use the Screen class to work out where your window is relative to the desktop. The Screen class has a FromRectangle method, so you can figure out which screen you should be positioning your Form on (by passing your form's Bounds property in).
Each Screen object has a Bounds property, which you can use to compare to the location and size of your window, and adjust them accordingly.
It depends what you want should happen when extraform is pushed beyond the bounds of the screen(s).
However, to find out whether or not it's being pushed off, it's quite simple using the System.Windows.Forms.Screens class. Then you can do bounds checking like so:
foreach (var screen in Screen.AllScreens)
{
if(screen.Bounds.Contains(this.Bounds))
{
Console.WriteLine("Device "+screen.DeviceName+" contains form!");
}
}
Code assumes being in a form. Note that this code only prints that a screen contains the form if the form is completely contained on the screen. But this should be rather simple to fix, depending on your needs.
Perhaps the DesktopLocation property in your Forms can give you a clue about what's happening with what's happening with them