I have 2 monitors, one of them is running at 100% scale, other is 175%.
I start a window on monitor1, 100%, then move it slowly to monitor2, 175%:
I can validate DPI change with form's DpiChanged event:
DPI changed: was: 96, now: 168
MessageBox2_Resize: System.EventArgs
MessageBox2_Resize: System.EventArgs
However, when I moving the form back from 175 to 100:
DPI changed: was: 168, now: 96
DPI changed: was: 96, now: 168
MessageBox2_Resize: System.EventArgs
MessageBox2_Resize: System.EventArgs
DPI changed: was: 168, now: 96
DPI changed: was: 96, now: 168
MessageBox2_Resize: System.EventArgs
DPI changed: was: 168, now: 96
MessageBox2_Resize: System.EventArgs
And this messes up everything.
Important note: Form is set to be "AutoSized" by its contents. If I disable autosizing, it works perfectly.
I have used .NET Framework 4.8 for this, and PerMonitorV2 for dpiAwareness.
Related
This may sound like a duplicate question, but I promise it is not. I have already looked at the answers provided in other questions, such as the one at the following link:
How can I get the DPI in WPF?
The problem is, they are not returning the correct DPI.
I know for a fact that my monitor (Dell U3415W) has a DPI of 109 ppi. The resolution is 3440x1440.
In WPF, I have attempted the following methods to get the DPI of my screen:
//Method 1
var dpi_scale = VisualTreeHelper.GetDpi(this);
double dpiX = dpi_scale.PixelsPerInchX;
double dpiY = dpi_scale.PixelsPerInchY;
//Method 2
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
double dpiX = g.DpiX;
double dpiY = g.DpiY;
}
//Method 3
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null)
{
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
All three of the above methods return 192 as my DPI (The scale factor being returned in methods #1 and #3 is 2).
I am writing code in which I need to reliably display the distances (in physical units such as centimeters) between some objects on the screen, and this code will not just be running on my screen so I can't just hardcode "109" into it.
On a related note, I am mystified by what seems to be an instance of WPF using actual pixels instead of device-independent pixels.
I have the following XAML declaring a window with a simple grid and a rectangle inside of the grid:
<Window x:Class="MyTestWindow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="768"
Width="1024">
<Grid x:Name="MainObjectLocationGrid">
<Rectangle x:Name="MainObjectLocationRectangle" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</Window>
In the code-behind, I have some code that sets some properties on that rectangle:
MainObjectLocationRectangle.Width = 189.5625;
MainObjectLocationRectangle.Height = 146.4;
MainObjectLocationRectangle.Fill = new SolidColorBrush(Colors.White);
MainObjectLocationRectangle.Stroke = new SolidColorBrush(Colors.Transparent);
MainObjectLocationRectangle.StrokeThickness = 0;
MainObjectLocationRectangle.Margin = new Thickness(0, 0, 0, 0);
When my rectangle appears on the screen, the rectangle is 4.4cm x 3.4cm in size. WPF says that 1 device-independent pixel is 1/96th of an inch, so I would assume that 96 dip is 1 inch (which is 2.54 cm). Therefore 189.5625 dip should be 1.9746 inches (or 5.01 cm). Yet it seems that it isn't using dip in this instance. If we insert my monitor's actual resolution (109 dpi) into the equation, we get the actual size of the rectangle being displayed:
189.5625 pixels / 109 dpi = 1.7391 inches (or 4.4 cm)
Yet in the WPF documentation it states that the Width and Height properties use device-independent pixels:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.width?view=netframework-4.7.2#System_Windows_FrameworkElement_Width
So, to conclude:
(1) Why are all the accepted methods for querying the DPI not returning the correct DPI to me?
(2) Why does it seem like when I set the size of the rectangle, it is interpreting that size in units of pixels rather than device-independent pixels?
Thanks for any help!!!
While Windows knows the resolution of the monitor, it doesn't appear to actually know the physical size of the monitor. And thus it has no means of determining the actual physical DPI. Windows simply assumes all screens are 96 dpi. If the user selects a scale factor in display settings, Windows adjusts the system dpi based upon that.
It is best for your app to have a calibration page, where it displays a line on the screen, and asks the user to measure it with a ruler and input the measurement. That would give you enough information to figure out the actual dpi of the screen. And technically, you'd need to do this both horizontally and vertically as the DPIs in each direction could, in theory, be different. (But I think in practice, they are almost always the same.)
This is old question but you can get the physical specifications of a monitor from Windows.Devices.Display.DisplayMonitor.
Prepare to use WinRT, then call Windows.Devices.Enumeration.DeviceInformation.FindAllAsync method.
using Windows.Devices.Display;
using Windows.Devices.Enumeration;
public async Task CheckMonitors()
{
const string deviceInstanceIdKey = "System.Devices.DeviceInstanceId";
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(DisplayMonitor.GetDeviceSelector(), new[] { deviceInstanceIdKey });
foreach (DeviceInformation? device in devices)
{
DisplayMonitor? monitor = await DisplayMonitor.FromInterfaceIdAsync(device.Id);
if (monitor is null)
continue;
Debug.WriteLine($"DeviceInstanceId: {device.Properties[deviceInstanceIdKey]}");
Debug.WriteLine($"DisplayName: {monitor.DisplayName}");
Debug.WriteLine($"PhysicalSizeInInches: {monitor.PhysicalSizeInInches}");
Debug.WriteLine($"RawDpiX: {monitor.RawDpiX}");
Debug.WriteLine($"RawDpiY: {monitor.RawDpiY}");
Debug.WriteLine($"NativeResolutionInRawPixels: {monitor.NativeResolutionInRawPixels.Width},{monitor.NativeResolutionInRawPixels.Height}");
}
}
In the case of Dell U2415, this code produces the following:
DisplayName: DELL U2415
PhysicalSizeInInches: 20.393702,12.755906
RawDpiX: 94.14671
RawDpiY: 94.074066
NativeResolutionInRawPixels: 1920,1200
Make a calculation as you wish.
I am trying to get Screen width and Height of my System on SizeChanged Event.
It works fine in all cases excluding:
Change Screen Size to 1024 X 765 then Run my Application its works fine.
Change Screen Size Again to Another Resolution while my application running
got Width and height of my Previous Resolution not Current.
I have used screen.primaryscreen.bounds.width on Window_SizeChanged Event.
You can monitor the SystemEvents for changes.
using Microsoft.Win32;
Add this to your constructor or the like:
SystemEvents.DisplaySettingsChanged +=
new EventHandler(SystemEvents_DisplaySettingsChanged);
Here's the event handler. I have the data bound to 2 TextBoxes so I could see the values change as I messed with the resolution, including telling it to revert.
void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
data.ScreenHeight = Screen.PrimaryScreen.Bounds.Height;
data.ScreenWidth = Screen.PrimaryScreen.Bounds.Width;
}
In your case, just change the assignment to point to where you want to store it.
I created some graphics to be printed assuming that the PrintDocument class uses the standard 96 dpi (an inch of the drawing requires 96 pixels on the printout), but when I print those graphics on paper they appear smaller thus not having the appropiate dimensions.
So what is the default DPI value of the PrintDocument class?
Thanks in advance!
This answer is by Hans Passant:
In your PrintPage event handler, the e.Graphics.PageUnit property
is set to GraphicsUnit.Display. Which makes 100 "pixels" equal to
one inch on paper. Regardless of the printer DPI. Close to the default
DPI for the screen, not quite.
I have built a WPF application which has a window that can be docked to the edges of the desktop. I use the SHAppBarMessage WIN32 API to perform the docking functions.
Under Windows 8.1, I've noticed that when docked, the window is only reserving half the width it needs. My code gets the PresentationSource object from the window being docked and then gets the TransformToDevice Matrix object:
PresentationSource source = PresentationSource.FromVisual( window );
Matrix TransformToDevice = source.CompositionTarget.TransformToDevice;
I then use the Matrix to transform the window's ActualHeight and ActualWidth to device coordinates before computing the size of the region on the desktop to reserve for the window:
Size windowSize = (Size) source.CompositionTarget.TransformToDevice.Transform( new Vector( appbarWindow.ActualWidth, appbarWindow.ActualHeight ) );
All of this code works as long as the display is in 100% DPI mode, but problems rise when you change the DPI setting. In higher DPI settings ( that is, DPI > 100% ), the area being reserved for the window on the desktop is narrower than the window.
In my debugging, I placed a breakpoint on the line that computes the window size above. I've found that in the 100% DPI setting, the transform Matrix is the Identity matrix. But after changing the DPI setting, the Matrix is unchanged!
I thought that this transform matrix would change if the DPI settings changed? If they don't, how do I get a transform that takes the DPI settings into effect? Is there a method that I am supposed to call when the DPI settings change? And if so, how do I get notification of the DPI Setting change? I have code that is called when the SystemEvents.DisplaySettingsChanged event is raised, and it is getting called when the DPI settings change. But the transforms are always the Identity Matrix. Shouldn't they change? How do I get the correct transform matrix?
I've found out that the short answer to my question is "no." Windows 8.1 added a new kind of DPI awareness called per-monitor DPI awareness. The WPF code is not per-monitor DPI aware. It turns out you have to handle per-monitor DPI awareness differently from system DPI awareness, which WPF has built-in.
I am drawing a rectangle in millimeter on a panel using the following code in c# by entering the width and length in mm at runtime. However the resultant rectangle drawn varies in size in different monitors. I want the rectangle to appear same size irrespective of the running the app in any monitor. Can any1 help me?. currently the width for 10mm measures 12mm and length for 10mm shows 11mm using a scale. I tested the app on different monitors, there again it shows different length. Is their anyway that I can show it to be of same width and length?
void panel1_Paint(object sender, PaintEventArgs e)
{
SolidBrush ygBrush = new SolidBrush(Color.YellowGreen);
g = panel1.CreateGraphics();
g.PageUnit = GraphicsUnit.Millimeter;
int w = Int32.Parse(textBox1.Text.ToString());
int h = Int32.Parse(textBox2.Text.ToString());
rct = new Rectangle(94, 27, w, h);
g.FillRectangle(ygBrush, rct);
}
Most displays aren't properly configured so that the computer knows the DPI (dots-per-inch). Physical units like millimeters only work if the computer knows how many pixels are in a millimeter, both horizontally and vertically.
Essentially, the method you're using is correct - but you're very unlikely in the real world to come across properly-configured machines.
If you needed to solve this problem in your software you could perhaps include a "configuration" option where the user would be responsible for setting the scale of the application.