I have an application with a textbox, and the width of the textbox on the screen must always be 17,5 centimeters on the screen of the user.
This is what I tried so far:
const double centimeter = 17.5; // the width I need
const double inches = centimeter * 0.393700787; // convert centimeter to inches
float dpi = GetDpiX(); // get the dpi. 96 in my case.
var pixels = dpi*inches; // this should give me the amount of pixels
textbox1.Width = Convert.ToInt32(pixels); // set it. Done.
private float GetDpiX()
{
floar returnValue;
Graphics graphics = CreateGraphics();
returnValue = graphics.DpiX;
graphics.Dispose(); // don’t forget to release the unnecessary resources
return returnValue;
}
But this gives me different sizes with different resolutions.
It gives me 13 cm with 1680 x 1050 and 21,5 cm with 1024 x 768.
What am I doing wrong?
The method graphics.DpiX does not give the real dots per inch of the monitor. It returns the DPI set in Windows Display properties, mostly either 96 or 120 DPI.
It is not possible to read the real DPI of the monitor. Microsoft did research this for Windows Vista/7 but as long as manufactures of monitors do not provide a standard way to read the value from the monitor hardware it will not be possible.
Yes, unfortunately Xenan is right.
To workaround the problem you could allow a sort of by hand calibration, done by the user.
e.g. draw a line of 400 pixel on the screen, ask the user to measure it on the screen and set the result. Now is really simple to calculate the PPI (pixels per inch) that is your calibration.
Width property of the Size structure depend on PageUnit and PageScale settings of the Graphics class. Try playing around with these settings to get your desired effect. Since you most likely need to modify these settings on the Paint event of the control, I suggest you create your own custom TextBox control instead.
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.
Quick question:
I have this 1000 x 1000 bitmap image:
and I use this routine to load it:
private BitmapSource initialBitmap = new BitmapImage(new Uri("C:\\Users\\...\\Desktop\\Original.bmp"));
Why after I load it, and right after I step over the above line, I see it as 800 x 800?
P.S I want it to be 1000 x 1000 and without using any Resize functions. It was working and suddenly it is 800*800 !
The values returned by BitmapSource.Width and BitmapSource.Height are not in pixels, but rather WPF's device-independent units, which are always 96 dpi. E.g.:
Gets the width of the bitmap in device-independent units (1/96th inch per unit).
If you want to know the actual pixel width and height, you need to use the PixelWidth and PixelHeight properties.
Your question isn't very specific, but if what you are actually concerned about is having the bitmap display at the same size in which it was authored, then the easiest solution is to make sure you author it at 96 dpi. Whatever program you're using to author the bitmap likely has a place where you can set the bitmap resolution. Typically this can be set with or without changing the pixel dimensions of the image (i.e. scaling the image larger or smaller); you want to do it without scaling the image, so that the pixel dimensions remain the same but the dpi changes to match what WPF is using.
Note that this still won't guarantee the bitmap displays at a specific pixel size. The display resolution can be and often is different from 96 dpi, in which case WPF will scale images to ensure that the physical dimensions of the image (i.e. the dimensions in inches, millimeters, etc.) are correct according to the information in the bitmap. For example, 960 pixels wide at 96 dpi means 10" wide. On a 120 dpi display, this means displaying the bitmap large enough so that its width uses 1200 display pixels.
If you want or need the bitmap to display at exactly the same number of display pixels regardless of the display resolution, then you'll have to set a transform where you display the image to reverse the effect of the scaling that WPF would otherwise do. This requires knowing the display resolution, of course.
Here are some other related Stack Overflow questions which you might find useful:
RenderTargetBitmap renders image of a wrong size
WPF for LCD screen Full HD
Screen Resolution Problem In WPF?
This is by design. Note the MSDN documentation for BitmapSource.Width/Height:
Gets the width of the bitmap in device-independent units (1/96th inch
per unit). (Overrides ImageSource.Width.)
Instead you should use the PixelWidth / PixelHeight property:
Gets the width of the bitmap in pixels.
A rather confusing choice or terms, imo, but there you go..
We are using Measurestring() to calculate size based on the length of text. For different screen resolution, MeasureString() gives different size.
Graphics g;
Size size = g.MeasureString(GetItemText(this.Items[n]), this.Font).ToSize();
width=size.width;
For Screen resolution 125%, size.width=76 and
For Screen resolution 100% and 150%, size.width=61.
How can i get same width in this code, please suggest me some ideas to measure size using measurestring().
Waiting for suggestions........
It's because the 125% behaves differently by default. For example, in Windows 7, if you change the DPI setting, because of the Windows XP style mode, the applications will aware of the current DPI setting. However, if you set 150%, this checkbox is not set by default, so the applications will work in DPI unaware mode, which means, that the MeasureString will return the same result as in case of 96 DPI, and the resizing will be performed automatically by Windows.
Normally you can ignore the result, because the sizes will be upscaled in your application anyway. If you still want to obtain the actual DPI value of Windows, see my answer here: https://stackoverflow.com/a/33412669/5114784
And then you can upscale your drawing like this (but as I said, normally this is not needed):
// See GetSystemDpi in the referenced post
float zoomFactor = (float)GetSystemDpi() / 96f;
size.Width = (int)(size.Width * zoomFactor);
size.Height = (int)(size.Height * zoomFactor);
I updated a chart from essentially being 72dpi, to 300dpi. This is because I am using itextsharp to add an image to my pdf and the quality was poor. So I increased the size of the image by 3X and the image does look better, but here is the problem.
DPI has increased, but detail has become very hard to see.
Original Chart Image
Refactored Chart Image
Code
This is how I resized my chart.
private static System.Drawing.Bitmap GetChartBitmap()
{
System.Drawing.Rectangle targetBounds = new System.Drawing.Rectangle(0, 0, chart_runs.Width, chart_runs.Height);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(targetBounds.Width, targetBounds.Height);
bitmap.SetResolution(1000, 1000);
chart_runs.DrawToBitmap(bitmap, targetBounds);
bitmap.Save(#"C:\Temp\OriginalChartImage.bmp");
System.Drawing.Bitmap bitmap3 = new System.Drawing.Bitmap(1650, 990);
bitmap3.SetResolution(300, 300);
chart_runs.DrawToBitmap(bitmap3, new System.Drawing.Rectangle(0, 0, 1650, 990));
bitmap3.Save(#"C:\Temp\RefactoredChartImage.png");
//This stuff below is for my code elsewhere. Using bitmap3 to be added to pdf.
//chart_runs.DrawToBitmap(bitmap, targetBounds);
string path = System.IO.Path.GetTempPath();
bitmap1.Save(path + #"\Image.png");
return bitmap1;
}
I have looked at the Microsoft msdn examples and haven't found anything that addresses my problem. Namely, how can I either increase the size of my labels so people can read them again. OR, is there a way for me to increase the DPI and keep the same label x and label y scale that was used in the first picture? That is, have a larger image and 300DPI, but scale 0 to 300 by 20's and not 5's like my refactored picture?
Attempts to fix
Scaling the axis? See here. I don't think this is working right. Not much success here.
Been trying to find a way in Chart class to see if there is a way to specify strict scales. (20 on y scale vs 15 seconds on x scale).
Most online resources are pleased just to increase the scale of the picture and walk away. And things like this here.
I would greatly appreciate any help and assistance.
Couple different questions, with a couple different answers. The easiest would be to change the font size of your axis labels to be bigger. This can be done via
chart1.ChartAreas[0].AxisX.LabelStyle.Font = new Font...;
Without doing that, your labels won't be readable no matter what else you do, and that's just because you changed the DPI (that's exactly what changing the DPI does).
If you want the labels to be displayed every 20 units on the y axis and every 15 on the x, you can use the Interval and IntervalType properties of the axis. The IntervalType is used when you have DateTime objects being displayed:
chart1.ChartAreas[0].AxisX.Interval = 15;
chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Seconds;
chart1.ChartAreas[0].AxisY.Interval = 20;
Your first link about scaling the axis is essentially zooming in or out, which is why you haven't had success.
I want to display an image at 'true size' in my application. For that I need to know the pixel size of the display.
I know windows display resolution is nominally 96dpi, but for my purposes I want a better guess. I understand this information may not always be available or accurate (e.g. older CRT displays), but I imagine with the prevelance of LCD displays that this should be possible!
Is there a way to get the pixel size of my display?
Is there a way to determine if the pixel size is accurate?
.NET API's preferred (I couldn't find them), but Win32 is OK too, I'm happy to P/Invoke.
For the display size you'll want Screen.PrimaryScreen.Bounds.Size (or Screen.GetBounds(myform)).
If you want the DPI, use the DpiX and DpiY properties of Graphics:
PointF dpi = PointF.Empty;
using(Graphics g = this.CreateGraphics()){
dpi.X = g.DpiX;
dpi.Y = g.DpiY;
}
Oh, wait! You wanted actual, hold a ruler up to the monitor and measure, size?! No. Not possible using any OS services. The OS doesn't know the actual dimensions of the monitor, or how the user has it calibrated. Some of this information is theoretically detectable, but it's not deterministic enough for the OS to use it reliably, so it doesn't.
As a work around, you can try a couple of things.
You can try to query the display string of the installed monitor device (I'm not sure how to do that) and see if you can parse out a sensible size out of that. For example, the monitor might be a "ValueBin E17p", and you might deduce that it's a 17" monitor from that. Of course, this display string is likely to be "Plug and Play Monitor". This scheme is pretty sketchy at best.
You could ask the user what size monitor they have. Maybe they'll know.
Once you know (or think you know) the monitor's diagonal size, you need to find its physical aspect ratio. Again, a couple of things:
Assume the current pixel aspect ratio matches the monitor's physical aspect ratio. This assumes that (A) the user has chosen a resolution that is ideal for their monitor, and that (B) the monitor has square pixels. I don't know of a current consumer-oriented computer monitor that doesn't have square pixels, but older ones did and newer ones might.
Ask the user. Maybe they'll know.
Once you know (or think you know) what the monitor's diagonal size and physical aspect ratio are, then you you can calculate it's physical width and height. A2 + B2 = C2, so a few calculations will give it to you good:
If you found out that it's a 17" monitor, and its current resolution is 1280 x 1024:
12802 + 10242 = 2686976
Sqrt(2686976) = 1639.1998047828092637409837247032
17" * 1280 / 1639.2 = 13.274768179599804782820888238165"
17" * 1024 / 1639.2 = 10.619814543679843826256710590532"
This puts the physical width at 13.27" and the physical height at 10.62". This makes the pixels 13.27" / 1280 = 10.62" / 1024 = 0.01037" or about 0.263 mm.
Of course, all of this is invalid if the user doesn't have a suitable resolution, the monitor has wacky non-square pixels, or it's an older analog monitor and the controls aren't adjusted properly for the display to fill the entire physical screen. Or worse, it could be a projector.
In the end, you may be best off performing a calibration step where you have the user actually hold a ruler up to the screen, and measure the size of something for you. You could:
Have the user click the mouse on any two points an inch (or a centimeter) apart.
Draw a box on the screen and have the user press the up and down arrows to adjust its height, and the left and right arrows to adjust its width, until the box is exactly one inch (or centimeter) square according to their ruler.
Draw a box on the screen and have the user tell you how many inches/centimeters it is in each dimension.
No matter what you do, don't expect your results to be 100% accurate. There are way too many factors at play for you (or the user) to get this exactly correct, every time.
Be aware that 96 dpi is usually pretty close to accurate. Modern pixels on non-projected screens all tend to be about 0.25 mm, give or take, so you usually end up with about 100 physical pixels per inch, give or take, if the monitor is set to its native resolution. (Of course, this is a huge generalization and does not apply to all monitors. Eee PCs, for example, have pixels about 0.19 mm in size, if I remember the specs correctly.)
sorry, you've got to P/Invoke for this information.
Here's the link that I utilized for it a while ago:
http://www.davidthielen.info/programming/2007/05/get_screen_dpi_.html
You can check by just manually calculating from your screen size
cos(45)*LCD_SCREEN_DIAGONAL_IN_INCHES/sqrt(HORZ_RES^2 + VERT_RES^2)
That would give you the pixel width in inches
GetDeviceCaps can be P/Invoke'd to get some figures, but I've never known the figures to be that trustworthy...
You may obtain the physical dimensions of the display using the EDID information stored in the registry. You can obtain the appropriate monitor's registry key using the EnumDisplayDevices windows API call.
Physical Dimensions to the Screen object:
TL;DR
WPF's True Size = Pixels * DPI Magnification
DPI Magnification:
Matrix dpiMagnification
= PresentationSource.FromVisual(MyUserControl).CompositionTarget.TransformToDevice;
double magnificationX = dpiMagnification.M11;
double magnificationY = dpiMagnification.M22;
Discussion
I had trouble solving this question still in 2020. Back when this question was asked/answered in 2009, .NET C# probably meant Windows Forms. But WPF is the de facto standard of the day...
By asking about "true size" you have probably already figured out that the operating system does some calculation with actual pixels (say 1366x768, which I understand is usual laptop resolutions) and the DPI (hard to find) in order to give a control's true size. And you are trying to make an app that scales to different monitors.
This DPI actual number seems to be hidden, but it has been normalized (converted to a percentage). Assume 100% = 96 DPI, just because the actual number does not matter anymore. People can easily increase the system-wide text size by going to Desktop on Windows 10 > right click > Display settings > section Scale and layout > change the percentage to magnify text and other elements.
You can find the pixels another way, and multiple/divide the pixel by the DPI percentage in order to get true size. For instance, I want to drag a UserControl around a canvas element of a WPF window with the mouse. The user control's pixel count and the mouse xy-coordinates were off by the normalized DPI. In order to keep the mouse moving at the same rate as the user control, I use:
double newXCoord = System.Windows.Forms.Cursor.Position.X;
double newYCoord = System.Windows.Forms.Cursor.Position.Y;
double deltaX = newXCoord - oldXCoord;
double deltaY = newYCoord - oldYCoord;
double magnificationX = 1;
double magnificationY = 1;
Matrix dpiMagnification
= PresentationSource.FromVisual(visual).CompositionTarget.TransformToDevice;
if (magnificationMatrix != null)
{
magnificationX = dpiMagnification.M11;
magnificationY = dpiMagnification.M22;
}
PixelsFromLeft += deltaX / m_magnificationX;
PixelsFromTop += deltaY / m_magnificationY;