views:

954

answers:

3

I've been linked in the past to this response to a similar question on converting WPF pixel coordinates to desktop ones, however I'm not sure I understand the maths involved.

Astonish' answer states that "Pixels per WPF Unit = ConstantWPFUnit size * monitor DPI;" and that "The constant WPF unit size is 1/96."

In my case, I've taken the DPI from a graphics object which was created from the bitmap object (as I couldn't find the property the Astonish spoke of) that I created after taking a screenshot of the desktop, so I have:

Graphics g = Graphics.FromImage(bitmap);
float WpfUnit = (1 / 96) * g.DpiX;

Given that the DPI being returned from the graphics object is 96, I am left with

WpfUnit = (1 / 96) * 96 = 1

However, WpfUnit is coming out as 0 for some unknown (to me) reason. The only way I can see to fix this is to say

if(WpfUnit == 0) WpfUnit = 1;

And even that doesn't really fix the issue, as the height value and top values, when multiplied by the WpfUnit as suggested in the linked answer, have nothing done to them aside from being multiplied by 1.

So, in conclusion, I'm still stuck on converting WPF pixels to desktop pixels. Any help on this would be greatly appreciated.

Regards, Andy Hunt

+1  A: 

WpfUnit is coming out as zero because it's doing integer math with the 1/96. Explicitly declare those numbers as floats.

McWafflestix
A: 

Thanks, McWaffleStix. I hadn't thought of that!

Sadly, doesn't seem to solve my problem. For clarity, I now have:

float WpfUnit = (1f / 96f) * g.DpiX;

Which successfully returns 1.

However, I still have the problem of the position and dimensions being wrong, as I'm not modifying them by multiplying them by 1. I have:

float fWidth = WpfUnit * (float)this.ActualWidth; 
float fHeight = WpfUnit * (float)this.ActualHeight; 
float fLeft = WpfUnit * (float)this.Left; 
float fTop = WpfUnit * (float)this.Top;

As was suggested by Astonish. I then convert these in to points and sizes.

System.Drawing.Point pos = new System.Drawing.Point((int)fLeft, (int)fTop);
System.Drawing.Size size = new System.Drawing.Size((int)fWidth, (int)fHeight);

Which I then use to create a rectangle which is used to define what area of the bitmap to crop to

System.Drawing.Rectangle rect = new System.Drawing.Rectangle(pos, size);
System.Drawing.Image croppedImage;
Bitmap clone = bitmap.Clone(rect, bitmap.PixelFormat);
croppedImage = clone;
clone.Dispose();

However, with the dimensions being incorrect, the cropped area is also wrong. For example, if I place the cropped area at (0, 0), the final cropped image will contain a black strip at the top and cut a bit off the bottom.

+1  A: 

How about built-in PointToScreen and PointFromScreen methods? Or am I missing something?

huseyint
I've looked at those two methods but they work with System.Windows.Point instead of the System.Drawing.Point which I need to create a rectangle object :(
You can always use Adapter Pattern to create wrappers from one type to another. See en.wikipedia.org/wiki/Adapter_pattern
huseyint
Thanks, again! I really hadn't thought of manually converting between the two :(
When you don't have any window you cannot use this approach. For more flexible scaling see: http://jerryclin.wordpress.com/2007/11/13/creating-non-rectangular-windows-with-interop/ the code includes the class for scaling.
macias