WPF: Multiple screens - c#

I'm writing a screensaver in WPF. I have the screensaver working, however, it only displays on my main monitor. Is there a way to "black out" or draw graphics to additional monitors when the user has multiple displays? I've done some searching around, but haven't found anything relevant.
UPDATE
From ananthonline's answer below, I was able to accomplish the "black out" effect on non-primary displays using the following window:
<Window x:Class="ScreenSaver.BlackOut"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Cursor="None" WindowStyle="None" ResizeMode="NoResize" Background="Black">
</Window>
and initializing one for each screen in App.xaml.cs using the following process:
foreach (Screen s in Screen.AllScreens)
{
if (s != Screen.PrimaryScreen)
{
BlackOut blackOut = new BlackOut();
blackOut.Top = s.WorkingArea.Top;
blackOut.Left = s.WorkingArea.Left;
blackOut.Width = s.WorkingArea.Width;
blackOut.Height = s.WorkingArea.Height;
blackOut.Show();
}
}
Note an import to System.Windows.Forms is required to access the Screen class.

You should be able to use the System.Drawing.Screen.* classes to set up multiple windows on each screen. Mind that you don't set each window to be maximized, but a properly sized, border less window.
Also - you might want to remember that the total bounds of the multi monitor setup may not always be a rectangle (if you plan to "union" all the bounds to create a window spanning all monitors).

Related

Programmatically Maximize Borderless Window

I currently have a way to do exactly what im asking, but the results are very in-efficient for some users.
Some users reported it making it as if it minimized (no window showing ever but still in taskbar), some reported for example in an ultrawide 21:9 it would only maximize from the left of a 1080p 16:9 width, yet I have a 32:9 super-ultrawise and have had no issues.
My current flow:
Get the Screen Dimensions excluding the taskbar on-load:
MaximizedBounds = Screen.FromHandle(mForm.Handle).WorkingArea; (mForm = Application.OpenForms[0], for support from any thread and such)
From another thread/function/class run:
Form1.mForm.Invoke(new MethodInvoker(() => Form1.mForm.WindowState = FormWindowState.Maximized));
This should result in a maximized window with the proper resolution, but it doesn't :/
What I would prefer:
NOT require to get the screen dimensions on-load, so use something from official Microsoft Code, either DllImport's, .NET Framework Code, or msapi to get the PROPER MaximizedBounds for Borderless Forms. (formborderstyle.none)
I set MaximizedBounds because if I don't, the application will fullscreen the entire screen, not "maximize" like traditional apps but would end up doing more of a Video Player style fullscreen.
Using my 32:9 screen and my 4k 16:9 laptop's screen on Extend Display mode, I managed to re-create the issue
Re-production Steps:
Open the application, leave it on the screen it first started in
Maximize the application (will work fine)
Unmaximize and move it to the other screen
Click maximize, your result should be like above.
This means, the on-load Maximize Bounds only gets the active bounds once which is expected, but due to me executing the Form Style change on a different class and different thread, I cant actually edit the MaximizedBounds property on it everytime I want to maximize, due to property not being public.
Thanks to HansPassant!
Found out exactly whats needed.
Problem:
When moving application to other monitor, it would use the same bounds. Causing it to not properly Maximize
When updating bounds via WndProc(), it would still try and maximize on Monitor 1 but outside its actual bounds, making it look like the application was hidden >:(
Updating the Bounds (put this in Form1):
protected override void WndProc(ref Message m) {
if(m.Msg == 0x02E0) {
Rectangle b = Screen.FromControl(mForm).WorkingArea; //where mForm is Application.OpenForms[0], this is specific to my applications use case.
b.X = 0; //The bounds will try to base it on Monitor 1 for example, 1920x1080, to another 1920x1080 monitor on right, will set X to 1080, making it show up on the monitor on right, outside its bounds.
b.Y = 0; //same as b.X
MaximizedBounds = b; //Apply it to MaximizedBounds
m.Result = (IntPtr)0; //Tell WndProc it did stuff
}
base.WndProc(ref m); //If any other messages were called, do the defaults this method does. Required even if you edited a msg.
}
Updating DPI Awareness for the WndProc message to fire (put this in app.manifest, make it if it doesnt exist):
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness> //Windows 10
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> //Old-Compatibility
</windowsSettings>
</application>
Now it no longer shows up out of bounds, and is finally updating the MaximizeBounds when changing monitors!
Thanks #HansPassant for the tips!

Splashscreen Rect coordinates returning Screen dimensions on Win 10 Mobile

I am trying to create an extended SplashScreen on my UWP app for which the SplashScreen image coordinates are needed. I am using the following code in App.xaml.cs:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
if (e.SplashScreen != null)
var SplashLocation = e.SplashScreen.ImageLocation;
//Rest of initialization...
}
However, if I inspect SplashLocation which is a Rect, SplashLocation.Height and SplashLocation.Width return 1280 and 768 which is the dimension of my phone's screen. All other properties are 0.
Is this a known bug in Win 10 Mobile [build 10536]? It works fine on desktop Windows 10.
Actually the issue is that the code given on the MSDN docs is wrong. Have a look at their samples on Github instead. You can find it here:
Splash Screen Sample
Notice the differences between the MSDN docs and the sample:
ExtendedSplash DOES NOT extend page
They use a ScaleFactor for phone
The root element of the XAML is Grid and not page.
I followed their sample (after hours of wondering) and it all worked well. I hope they update their docs to reflect the correct thing.
As a workaround, here's what I did:
Create an Image in XAML inside a Grid (and NOT inside a Canvas as suggested by the msdn Docs)
<Image Name="ExtSplash"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="Assets/Splash/SplashScreen.png"/>
Now, the trick is to make it work on both Mobile (which doesn't give correct Rect coordinates and Desktop (which works as expected)
So, in the loaded event of Page in code behind, I used this code:
if (splash.Top < splash.Left)
ExtSplash.Margin = new Thickness(splash.Top);
else
ExtSplash.Margin = new Thickness(splash.Left);
where Splash = e.SplashScreen.ImageLocation from the OnLaunched event in App.xaml.cs.
This works because, Splash.Top and Splash.Left both return 0 in case of Mobile. So the app just displays the splashscreen fully extended to the width. In case of Desktop, the Image may have some Left or Top Coordinates as it's reported correctly on desktop, where I set them as Margin for the centrally aligned uniform stretched image.
NOTE 1: There may be cases where a slight variation may occur between splashscreen and the ExtendedSplash image. But in my testing it worked fine. It will continue to work even when in a later build MS fixes the issue, so you can take your time to implement the standard method then.
NOTE 2: I had some issues with this logic if scale-400 splashscreen image was provided. For some reason WM10 picks up the highest resolution available. So I just supplied 100, 125, 150 and 200 scale images (skipped the 400)
I ended up ditching the SplashScreen.ImageLocation approach. The Microsoft tutorial does not work on mobile at the time of writing, and the sample is hackish. Instead I used a simple layout like this for the extended splash screen:
<Grid>
<Image x:Name="splashScreenImage" Source="Assets/SplashScreen.png" MaxWidth="620" MaxHeight="300"/>
<!-- plus some other control -->
</Grid>
The magic is to use MaxWidth/MaxHeight.
On mobile the the image will be slightly different positioned vertically (because of status bar and navigation bar I guess), but otherwise correct including size. On desktop the image seems to be exactly positioned and sized as the initial splash.

Full screen c# windows form covering all monitor

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.

Find size of a Canvas

How to find out size of an Canvas that created in xaml file?
for example I create an Canvas by
<Page
x:Class="MyApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Canvas x:Name="canvas" Background="White">
</Canvas>
</Page>
and full screen is white or whatever color I specified for that canvas
than in my class I tried but with no luck
double h = canvas.Height; // NaN
h = canvas.ActualHeight; // 0
so how do I found out the actual size of that canvas? or the size is 0 but than how to make the canvas full screen size?
I am new C# and metro developer and so confused how everything works compare to iOS.
Where in your code are you checking the size of the Canvas? I'm assuming you're doing it in the page constructor or somewhere else that is running before the UI layout has run. In this case, all auto sized elements (NaN height or width) still have their default size. If you check the size after layout has completed, like in a Loaded event handler in your page, then you should see the true rendered size.
If your Canvas happens to only contain one Image, you can use:
var Width = ((Image) (MyCanvas.Children[0])).Width;
var Height = ((Image) (MyCanvas.Children[0])).Height;
Of course, you can switch out the two Image casts for another element, if your Canvas contains one of some other item.
Alternatively just give the wrapped Image (or other element) a name, and reference it as MyImage.Width.

C# Winforms-WPF interop

Currently I have a Winforms app which relies on transpareny effects. However this is proving to be an absolute pain in the preverial behind! Winforms as Im learning doesn't deal with transparency particularly well.
I was wondering whether or not this would be any easier using WPF components for the transparency bit and winforms for the rest (note althought Id like to move the whole app over to WPF this just isn't feasable!). I know next to nothing about WPF, hence the reason Im here! What I was considereing was :
1) Host a WPF component within a Winforms User Control e.g. Example of WPF Control:
<UserControl x:Class="WindowsFormsApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<Rectangle Name="rectangle1" Stroke="White" Fill="Black" RadiusX="10" RadiusY="10" Opacity="0.7" />
<Rectangle Margin="57,101,43,99" Name="dialog" Stroke="Gray" Fill="White" RadiusX="10" RadiusY="10" />
</Grid>
</UserControl>
2) Host a Winforms user control (content) within the white rectangle (dialog) of the WPF control.
3) allow the content (Winforms user control) to call code on the parent of the WPF-Control.
First things first...
Is this a reasonable thing to do or am I barking up the wrong tree?
Can this be achieved in an easier fashion?
Can anyone help me here? (Sample code would be gratefully received!)
Finally ... are there any online resources that can help me a) learn WPF and b) become more self-sufficient?
It certainly is possible, and I think you are right that it would be the easiest way to work with transparency.
I haven't tried it myself, but according to this article on CodeProject, it should be quite simple. You should use the ElementHost control to host your WPF content.
Hosting WPF in a WinForms control is a supported scenario, a feature built into the framework. So there should be no problems in doing so. There is also a WPF component for going the other way, hosting WinForms in a WPF app.
Here is the solution I used to solve the problem at Hand. This solution relies on the overlaying Control to render its Parent as a bitmap image. This then gets painted as the background of the overlaying control.
public class OverlayingControl : UserControl
{
/// <summary>
/// Overrides the c# standard Paint Background to allow the custom background to be drawn
/// within the OnPaint function
/// </summary>
///
/// <param name="e">Arguements used within this function</param>
protected override void OnPaintBackground( PaintEventArgs e )
{
//Do Nothing
}
protected override void OnPaint( PaintEventArgs e )
{
// Render the Parents image to a Bitmap. NB: bitmap dimensions and Parent Bounds can be changed to achieve the desitred effect
Bitmap background = new Bitmap( Width, Height, PixelFormat.Format64bppArgb );
Parent.DrawToBitmap( background, Parent.Bounds );
// Paint background image
g.DrawImage( background, 0, 0, new RectangleF( Location, Size ), GraphicsUnit.Pixel );
// Perform any alpha-blending here by drawing any desired overlay e.g.
// g.FillRectangle( new SolidBrush( semiTransparentColor ), Bounds);
}
}
This is performed purely within the WinForms domain, however I believe it could be possible to pass this Bitmap image to a WPF control to render as required. Currently there is no provision for updating the Bitmap when the parent changes, However, it should be trivial to create a custom method that clears the bitmap and re-draws the Overlayng control. Not an elegant solution I realise... but it appears to work well enough.

Categories

Resources