I am trying to get the CurrentMonitor property of NativeWindowSettings to change which monitor it creates the window on. I'm trying to display on my secondary monitor, here is what I have:
var nativeWindowSettings = new NativeWindowSettings()
{
Size = new OpenTK.Mathematics.Vector2i(800, 600),
Title = "My Window",
Flags = OpenTK.Windowing.Common.ContextFlags.ForwardCompatible,
CurrentMonitor = new Monitors.GetMonitors()[1].Handle, //problem
};
using (var window = new Game(GameWindowSettings.Default, nativeWindowSettings))
{
window.Run();
}
I've tried creating a new Monitor Handle and assigning it to that, and I've tried creating a new MonitorHandle using the pointer from the second monitor in the array of monitors.
CurrentMonitor = new MonitorHandle(Monitors.GetMonitors()[1].Handle.Pointer),
It depends on your OS. Not all window systems give you the ability to specify which screen a newly-created window will appear on, and not all of them are going to pay attention to that parameter either. Your mileage may vary, as they say. You didn’t specify which OS you’re on, so I’m going to assume for the rest of this that it’s some flavor of Microsoft Windows.
On MS Windows, you don’t get to specify which screen your new window appears on without also specifying exactly where on that screen the window should appear: If you let Windows pick the coordinates, it’s going to put the window on the primary screen every time, exactly as you’ve seen it doing.
So the right way to go about it, at least on MS Windows, is to first find out the virtual coordinates of the screen (the MonitorInfo) that you want to create the window on. You can choose from its ClientArea (the virtual coordinates of the entire screen) or its WorkArea (the virtual coordinates of the usable part of that screen, minus things like the taskbar). In either case, each MonitorInfo’s rectangle will be a unique chunk of virtual screen space that doesn’t overlap that of any of the others.
Once you’ve found the rectangle of the screen you want to create the window on, you just specify a Location in the NativeWindowSettings that’s on that screen, inside the coordinates of its ClientArea or WorkArea: Possibly in the top corner, or, if you want to be a bit more clever, you can do some math and center the window in that virtual rectangle. The only thing you can’t easily do is the OS’s “cascading” behavior, where your window will be given a new position that’s sort-of distinct from the others on the screen but not really centered and not really in the top corner either; that behavior is built-in inside the window system, and there’s no way to directly access it or influence it.
But that’s otherwise it: You don’t specify the monitor: You specify a Location that’s on the monitor, and then the window will appear there. That’s not really an OpenTK thing, either: That’s just how Microsoft Windows does a multi-monitor setup.
Related
I have a winform (c#, let's say 250px by 250px) that needs to stay in one location on the screen regardless of screen resolution i.e 800x600, 1920x1080 etc. The Winform itself contains only one element - a picturebox so what's inside really doesn't matter (no need to worry about fonts, etc.). I just need it to stick in one place on the screen from one monitor to another.
Any ideas? Thanks in advance.
Could you use one of these?
1) WindowsState = Maximized (then you dont worry, it always takes whole screen)
2) StartPosition = CenterScreen (then it always shows as centered), or CenterParent to center within parent form
3) Location = in this case you would have to do some math to get screen size, your form size than based on that center it but I dont see point of using this considering that StartPosition does it already for you.
Hope that helps
Ok, so in order to get the window at a fixed location given in % of the screen size, you need the screen size (e.g. using this answer), compute the desired position, and set it as the window-location.
Since you need to do it at startup, you could do it before you show the window, or maybe best inside a Frame.Loaded event handler.
I need to know the drop shadow dimensions of a window. I tried receiving values via the GetSystemMetrics WinAPI function, but I could not find the parameter to pass over.
Any idea on how I can get this value globally (or for a single window handle)? I know that the width of a drop shadow depends on the window type (dialog/normal/and so on).
Ibwould implement this functionality using C#, but C++ would also be fine.
There is a way using the DWM API to figure out the size of the drop shadow, however, it does not work until the window is visible.
In previous versions of Windows, there was the Client Rect, and the Window Rect. But ever since Vista came out, there has been a third Rect for a window called the Extended Frame Bounds. The third rect is larger than the Client Rect and smaller than the Window Rect, and excludes the area taken up by the drop shadow.
Call DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(RECT)) to read the Extended Frame Bounds rect. This function is from <dwmapi.h>, and is
not available in Windows XP or earlier.
Note that if you call this before the window has been shown, you will get incorrect results.
EDIT:
Note that Extended Frame Bounds are in physical pixel coordinates, and are not affected by the Scaling features of Windows. You may need to convert back to virtual coordinates, or convert the virtual coordinates to physical coordinates to make them match.
As far as I know there is no way of obtaining that information. Programs such as WindowClippings solve it by taking a screenshot and cropping to the shadow dimensions afterwards.
Is there a way to make make a windows form application full screen and black out your secondary monitors? So the main display is on your primary display and all your other monitors are just completely black?
You can use the Screen class which give you informations about the current active screens.
// Form myFrm
Rectangle r = new Rectangle();
foreach (Screen s in Screen.AllScreens)
{
if ( s != Screen.CurrentScreen ) // Blackout only the secondary screens
r = Rectangle.Union(r, s.Bounds);
}
myFrm.Top = r.Top;
myFrm.Left = r.Left;
myFrm.Width = r.Width;
myFrm.Height = r.Height;
myFrm.TopMost = true; // This will bring your window in front of all other windows including the taskbar
I can think of one way, and that would be to find out how many monitors there are on the computer, and their layout relative to each other, then create your primary window at 0,0, maximize it and set it to be TopMost, then do the same for the other displays, placing them at the screen locations corresponding to the top left of each monitor of the computer.
The only thing I can think of that would benefit from this in a WinForms environment is an app designed to give a test; the app would cover the entire desktop (except the taskbar; you'd have to disable the Start menu) and pretty much ensure that the user couldn't look at anything except the testing program. It will give you a minimal performance advantage.
Most of the apps that black out all the monitors except the main display are basically using DirectX to control the screen directly (through the lower-level interface to the graphics card). If you're using WinForms to make your program, you're about 50 levels of abstraction above using DirectX.
One way would be to create a second form in your application. One that, when given a set of dimensions, will open without border or menu and with the background set to black.
When your application runs, enumerate your monitors (count them to see how many) and find their dimensions. Your primary one will start at 0,0.
Then spawn a copy of that second form for each of your monitors (except your primary one) and give that form the dimensions of the monitor. They will then turn each screen black.
You will need to remember to keep a handle to each of the forms and terminate them when you terminate your main form.
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've had a bit of a search around, but couldn't find anything similar to what I was looking for. I'm interested in knowing how to display differing contents on secondary/tertiary monitors or projectors using C#. Basically, what I'm wanting to achieve is to have some form of presenter view (a lá Powerpoint) on one particular screen (the primary display), and the output on a secondary screen or projector. I've never really attempted to develop something with multiple display outputs, so any guidance will probably have to be at a fairly obvious level.
If someone could point me in the right direction as to how to handle this sort of thing in C#, that would be greatly appreciated!
Just to expand on Keven's answer (I +1'd it), The Screen.AllScreens array gives you have an array of Screen objects. The Screen object has a property called IsPrimary, which you can use to determine which is the primary screen, and which is the secondary (duh) and it also has a property called WorkingArea, which is a rectangle that gives you back all the coordinates of the second screen. The cool thing about this is, that even if let's say the secondary screen is configured to be on the left of the primary, the WorkingArea.X will be a negative number, and you can place forms there or whatever.
You can use the System.Windows.Forms.Screen.AllScreens property to access a list of all the monitors Windows knows about. If you're looking to utilize a display that hasn't been configured by the user, it gets more difficult - you'd probably need to initialize and access the display adapter using DirectX.
One of the main classes you will need to interact with is Screen (this is in the WinForms namespace). In general all the screens are treated as a set of working areas that you can use the screen class to get properties for each one.
You can get all the screens like this...
Screen [] screens = Screen.AllScreens;
Here is a short article about doing multi-monitor programming in C#.
Also remember to set the property 'StartPosition' to 'Manual' on your the window you want to display on the secondary screen otherwise it will always display on the primary screen.