I am developing a map control in WPF with C#. I am using a canvas control e.g. 400 x 200 which is assigned a map area of e.g. 2,000m x 1,000m.
The scale of the map would be: canvas_size_in_meters / real_size_in_meters.
I want to find the canvas_size_in_meters.
The canvas.ActualWidth gives the Width in DIU's (Device Independant Units). So, 400 DIU's is 400/96 = 4,17 inches, PROVIDED that the physical resolution of my monitor is 96 dpi.
However, using a ruler, I found that the physical resolution of my monitor is 87 dpi. (There are only few monitors that ACTUALLY have 96 physical dpi)
That DPI difference (10%) translates to a +10% difference in the actual map control width on screen.
How do I measure the size of a WPF control in inches EXACTLY and regardless of screen resolution and DPI setting ?
How do I measure the size of a WPF control in inches EXACTLY and regardless of screen resolution and DPI setting ?
This isn't actually possible, because for it to work, WPF would have to know the resolution (in terms of DPI) of your monitor. Sounds nice in theory, but in practice windows doesn't know this information. This is why windows itself always assumes 96dpi blindly instead of being smarter about it.
Even if there were some way to manually tell it, or if your particular monitor has a custom driver that does pass the correct information to windows, this isn't going to work on anyone else's computer, so windows doesn't pass this information on to any applications.
The best you can do is draw a scale like google maps does. You know that 1 pixel == 1 mile, so you can draw a 50 pixel line on your map, with a label saying "this line equals 50 miles"
There is way to compute current pixel size in mm or inches. As mentioned in the earlier posts, it is not a fixed value and would vary depending on the current resolution and monitor size.
First get the current resolution. Assume it is 1280x1024
Now get the monitor width in mm using GetDeviceCaps function. Its a standard windows library function.
int widthmm = GetDeviceCaps(deviceContext, HORZSIZE);
My monitor width is 362mm
So pixel size = 362/1280 = 0.282 mm
The accuracy of this method depends on the assumption that the display area covers the width of the monitor exactly.
So to answer the original question, the canvas size of 400 x 200 pixels would be
(400 * 0.282/1000) x (200 * 0.282/1000) in meters when shown on my monitor.
Thank you for you prompt reply.
I totally agree, but I didn't want to believe it in the first place. You see, there has to be an approximate calculation of the scale of the map if the map is used to display different layers of map data (scale dependant).
Most applications use a slider control with e.g. 10 discrete map levels to set the "scale".
Having an absolute scale is not crucial for the application, it would be nice to display an indicative scale, like 1:15,000.
An absolute scale would require for an extra variable monitorPhysicalDPI (initially set to 96) that if the uses chooses to change would give slightly better scaling (again it's not crucial). The size of the map control would be:
map.ActualWidth * (96/monitorPhysicalDPI) * inchesPerDIU, inchesPerDIU is 1/96
Again these are cosmetics.. Wouldn't it be nice if Windows knew the ACTUAL control's dimensions? (user would have to give information about the screen dimensions on OS setup, or simply installing the monitor INF file)
Related
I'm trying to draw something in a System.Windows.Media.DrawingVisual but I need to draw thins in millimeter unit. How can I do that?
In WPF, you can't even draw something in pixel units without at least some extra effort. WPF uses "device independent units" where each unit is 1/96th of an inch. Even that is only a theoretical relationship, as it depends on the display device correctly reporting its resolution, which in turn is dependent on the display, its configuration, and what the user has set e.g. in the "large fonts" setting (i.e. in the screen resolution settings, clicking the link that reads "Make text and other items larger or smaller").
All of these affect WPF's interpretation of the available display resolution information, which in turn affect how WPF chooses to render its "device independent" 1/96th of a inch units.
The bottom line is that the link commenter Sheridan offered really is the closest you can come to displaying in millimeters, barring a lot of extra work and help from the user. By scaling your input units, intended as millimeters, by the factor value provided (i.e. 96/ 25.4…in the expression, you can see the 25.4 to convert from millimeters to inches, then the 96 that converts inches to 1/96ths of an inch), you can convert your millimeters into the 96 dpi units that WPF uses natively.
Assuming the display is configured correctly (an optimistic assumption, but it does happen :) ), this will result in reasonably accurate presentation on the screen according to your desired millimeter-based dimensions.
Note that you can accomplish this scaling through the use of a transform on your rendered UI elements. The easiest thing to do would be to set the LayoutTransform property of the outer-most container object where you want the millimeter-based rendering. Then you can just lay out your objects in that container using the millimeters values for their location and size, and WPF will use the transform to present the container and the rendered objects within at the scale you want.
Just recently I switched my Windows to 125% DPI scaling mode and found out that my app does things that I don't want it to do.
One of the problems is scaling of the image I am showing in the app. Let's assume it can be any image and for this example let it be a 600-pixel wide one. I want it to stay just like that. I'm ok with scaling the rest of the interface but this one particular Image control has to be exactly as wide as the Image is, and not scaled up to 125% (to 750 pixels). How can I achieve that? Should I change control size after some DPI-related event fires?
Also, the second question is, why WPF is lying to me? When I get ActualWidth it tells me 600 pixels, but Paintbrush isn't lying - I hope - and it shows 750 pixels when I measure width on the screenshot. Should I be checking this value after any particular event so that I get an actual ActualWidth?
Some code:
imgNav.Source = ImageManager.LoadBitmapImage(uri);
BitmapImage bimp = imgNav.Source;
imgNav.Height = bimp.PixelHeight;
imgNav.Width = bimp.PixelWidth;
...
txtFoo.Text = imgNav.ActualWidth.ToString();
Thank you in advance for any suggestions.
I need to know whether there is any advantages or disadvantages on using 2d graphics in wpf over 2d images or not?
I mean if I'm going to display an ellipse in a wpf window which one is more useful and why? To create an Ellipse object or to create an Image control and then load an pre-designed ellipse image into it?
Does using 2D graphics have any advantages in the sense that they consume less memory or increase performance or anything like that?
Thanks in advance.
An Image is not realy good scalable.
An vector Graphics is.
On a high dpi display your Image with Width = 300 (Units not pixels) and Height = 300 (units) is on a Display with 96 dpi not bigger than a Display with 144dpi. But the Image on the 144 dpi display needs more Pixels for the same size.
1 Unit is 1/96 inch.
So it is better to have a scalable "image" than a fixed one.
Its one of the features of wpf that winform don't have! and the reason why you should not use Pixels as a Size / Width / Height / Position / etc.
Excursus:
In winform it is hard to programm a scalable programm. on high dpi monitors the font, buttons etc. looks very small. there was no option to solve this problem.
so windows programmed an algorithm that creates a bitmap of the programm and scales this up.
So: the progamms width and height is the same as before, but the user sees a much bigger one (the Bitmap). The user input is then recalculated on the real sized application; Everything is working fine - and looking fine.
Hope that helps and is correct.
I am using dual monitor and I want to find the Current(where my application is active) monitor's height and width (note : not resolution). How can i achieve this. My WPF application display screen size options based on current screen size.
If you're feeling adventurous, have a look in the registry keys. Specifically HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY and loop through the displays (or better still just the active display) and use the EDID information to get the screens width and height (and by Pythagoras theorem, the diagonal length) of the monitor
This answer has two parts - please don't judge. The new one I'm quite uncertain but will give you better results. The older one I'm sure works somehow.
New answer
I think you've missed an incredibly important answer in one of the questions you viewed. There was an answer using the Graphics.DpiX and Graphics.DpiY by dividing them to the pixel resolution's x and y values respectively. Read it right here! Don't forget to read through the comments as well.
Because the amount of pixels in an inch differs in different monitors, this dude found out that Graphics.DpiX is the the amount of pixels in the X axis required to make an inch. The same goes for Graphics.DpiY which obviously points the pixels per inch in the y axis. In order to find the inches from the resolution, you divide these two. You can read more on the usage here and here. The second link is in Visual Basic but, imagining that you have previous C# knowledge you'd be able to figure out the syntax.
Be warned - I haven't tried any of the following yet!
Start out by referencing System.Drawing.
Next, theoretically (I haven't tried this out before so please do correct me if you can) you'd need to make a new Graphics class.
Graphics DesktopGraphics = Graphics.FromHdc(GetDC(IntPtr.Zero));
And now, using our new Graphics class that references the monitor in question, we grab the DPI and the screen resolution and divide the screen resolution by the DPI to get how many inches the screen is.
SystemInformation.PrimaryMonitorSize.Width / Graphics.DpiX for width and SystemInformation.PrimaryMonitorSize.Height / Graphics.DpiY for height. You need to assign those two to their respective variables and voila! Width and height of the screen - in inches.
Again if I do do something wrong, please tell me. I'm still a novice programmer so if I do something wrong, don't bash me or flame me - can you please suggest the appropriate code if I do so.
Old answer
The person who wrote; try System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height it's working in my case. was, in fact, telling you the height, in pixels, however. What you need to do is to convert this integer to inches.
One inch is equal to 96 pixels (or 96.0000000000011 to be more exact). So, write the following code;
//Height
Int MonitorHeightinInches = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height / 96;
//Width
Int MonitorWidthinInches = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width / 96;
Now you may reuse the code as much as you want, in your application. This works for me.
You will find it very difficult to get a monitor's physical dimensions. You might be able to get the resolution and DPI and work it out from there, but that won't necessarily give you the correct answer. Imagine a projector - you may know it's resolution and DPI but the physical size of the displayed image will depend on the distance to the screen. It's not just projectors, though. The manufacturing processes per monitor may result in slightly off figures, for example.
I would be very tempted to remove any reference to physical sizes and make a flowing layout that resizes adequately according to the resolution and DPI capabilities of your display.
try
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height
it's working in my case.
I have been using RenderTargetBitmap to draw lines for my application as shown here.
It's working, but I don't quite understand the DPI. If I need the lines to draw under the mouse, I use a DPI of 96. All I need to know is whether this is the same for all devices and screens and, if not, how to find the correct one.
This is the code I use to get system DPI:
// Get system DPI
Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
if (m.M11 > 0 && m.M22 > 0)
{
dpiXFactor = m.M11;
dpiYFactor = m.M22;
}
else
{
// Sometimes this can return a matrix with 0s. Fall back to assuming normal DPI in this case.
dpiXFactor = 1;
dpiYFactor = 1;
}
This will be the factor of normal DPI (96) the system has. The system will have a horizontal DPI of dpiXFactor * 96 and a vertical DPI of dpiYFactor * 96. You can test this on Windows 7 by going start menu -> "dpi" and selecting "Make text and other items larger or smaller". 100% means a factor of 1 and a DPI of 96. 125% means a factor of 1.25 and a DPI of 120. 150% means a factor of 1.5 and a DPI of 144.
The fallback logic is due to a customer crash report which I think could have only been caused by the transform matrix having all zeroes. There might be a more reliable way of getting system DPI (pinvoke, maybe?) but I don't know of it.
DPI is used when rendering non vector images. You dont have to worry about changing DPI's for different monitors or screens tahts determined by the OS and monitor hardware. What you do have to consider is the level of pixelation you want in your image. As a general rule of thumb small images under 100x100 pixel images should be ok with 96 DPI, any thing between 100x100 and 300x300 you should go 150-175 DPI. for anything larger than 300 that you can go 300-400 DPI. After a certain point its just overkill. Most commercial printing is done under 350 DPI unless your doing stochastic printing. If your drawing lines with a stroke of 1-3 you should be ok with 96 DPI, any larger I would suggest 150 DPI.
The DPI you use depends on what will be done with the output. If your drawing will be viewed only on the screen without zooming, you will be ok with 96dpi and you don't really need to worry about it anymore. If you need to zoom in on your drawing, then higher resolution can provide a better user experience. If printing your drawing is an important part of your user experience, then you'd probably be better served going with 300dpi.