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.
Related
I have a FIFO Real Time chart (pretty much taken from their published Example) of a SciChart graph. As it renders, it starts out completely zoomed in very close and as the line is drawn, it zooms out to accommodate the full size of the line.
<s:SciChartSurface.XAxis>
<s:NumericAxis x:Name="axisX" MinHeight="50" AutoRange="Always" AxisTitle="{Binding Path=XAxisTitle}" DrawMinorGridLines="False" DrawMinorTicks="False" TextFormatting="0.##">
<s:NumericAxis.GrowBy>
<s:DoubleRange Max="0.1" Min="0.1" />
</s:NumericAxis.GrowBy>
</s:NumericAxis>
</s:SciChartSurface.XAxis>
However, what I would like is for it to begin zoomed out by a certain amount already - e.g. the X axis would already be displaying from (for example) 0 - 10 and as the line is drawn it proceeds across the screen, only zooming if the line happens to get bigger than the space provided.
I've tried setting the VisibleRangeLimit, but while this does allow me to define the range of the chart area, the zoom doesn't kick in when the curve gets too big (so it literally goes "off the chart")
How can this be accomplished?
The reason for this is the Fifo Example in sciChart WPF uses XAxis AutoRange set to Always to scale the axis to fit the data. When the example starts, even if the Fifo buffer has a capacity of 10,000 points, it has no data in it, hence the axis is scaled small to accommodate the data.
There are two ways around this:
Is to pre-fill your FIFO DataSeries with X=xValue, Y=double.NaN. Given enough values the chart will think it has to draw all these points so the XAxis will scale accordingly
Is to take control of XAxis.VisibleRange yourself (do not use AutoRange). In this case, you need to set XAxis.VisibleRange to a window size to accommodate N points, and as you update data, update the window.
The FAQ 'How to create a StripChart in SciChart' demonstrates technique (2), how to update the visible-range of the XAxis to achieve scrolling behaviour.
Disclosure, I am the tech lead of the SciChart WPF Team
I would like to know the standard method for resizing my DirectX control. A model is shown in the control, and I want it to be the same size after resize, only more of its environment should be visible.
I managed to do it with resetting the viewport and swapchain buffers, but I could do it another way too (moving the camera), and maybe others I didn't think of. I just don't know which is the "best" way.
Changing the viewport size should only show "more" of the viewing area as a matter of changing the aspect ratio. For instance, if the viewport doubles in width and height, the aspect ratio will be the same, therefore the same image will be shown, just larger.
In contrast, moving the camera will change the amount that is viewed, e.g. If you move the camera back, you will see more. However, this may not be what you are looking for. You are physically moving the camera to another location rather than simply changing the view properties.
What you are probably looking for is to change the camera projection properties, which would likely be done using "PerspectiveFovLH", the input for which is: ("field of view", "aspect ratio", "z near plane", "z far plane"). Widening the field of view (FOV) will allow you to see more of the scene. Changing the aspect ratio will scale that appropriately. Typically the aspect ratio should be the ratio between the screen width and height, and if you want a larger screen to show more overall, scale the FOV by the amount the screen has been re-sized.
We need to:
Measure text accurately.
Render text line by line to a screen graphics context in the presence of translation and scaling transforms applied to the graphics context.
Hit testing: allow text to be selected precisely with the mouse or via a displayed caret.
Print the result if needed, and as accurately as possible, with a printer. Note: this is secondary. Screen rendering and hit testing are primary.
Run on Windows XP and higher operating systems.
within a WinForms application that is also rendering graphics and images to the same graphics context.
There are four technologies that we've encountered. We've tried using the first two, and ran into the issues described, over the course of several months.
GDI+
Purportedly resolution-independent text. However according to this question - and other sources - this technology is to be avoided because of quality issues.
MSDN states that calling Graphics.MeasureString along with StringFormat.GenericTypographic and TextRenderingHint.AntiAlias produces accurate string measurement. However, in our experience, and that of others, this is not the case - we do not get accurate string measurements.
Pros: Fast
Cons: inaccurate string measurement.
Result: unusable because of inaccurate string measurement.
GDI via TextRenderer
This was introduced to overcome the limitations of GDI+. However this introduced limitations of its own:
Very slow
Does not work with graphics transforms
Result: unusuable for these reasons
GDI via p/invoke
Calling GetTextExtentExPoint for text measurement and DrawText / DrawTextEx / ExtTextOut for rendering.
We haven't tried this yet.
DirectWrite
This seems promising, since it interoperates with other technologies including GDI/GDI+, so presumably the rest of our graphics rendering wouldn't change. However it is only available for Windows Vista and more recent Windows versions. This is presently a problem since Windows XP still has a significant installed base.
Question
Which of these technologies can be made to work given the requirements?
Note: There's much misinformation about this topic floating around, so please answer this question only if you have expertise in this area.
Also, please don't suggest WPF - that isn't something we're considering using.
Supposedly the MeasureCharacterRanges function is more accurate than MeasureString.
If you have to target both the screen and a printer, then you need to make some decisions about your approach before deciding which rendering engine to use. Here are some possibilities:
Lay out in screen units and do a best-possible approximation for the printer.
Lay out in printer units and do a best-possible approximation for the screen.
Lay out in a theoretical high-resolution space and do a best-possible approximation for both printer and screen.
In all cases, the best-possible approximation might be done by very careful approximations at each step, which can give the highest fidelity for each device at the cost of accuracy in matching, or you could render to bitmap and scale to the device, which gives lower fidelity but a the best-possible approximation to the other space.
I've done hard-core print preview with GDI. It's hard, but do-able for regular layouts. But if you're handling arbitrary transforms, especially rotations other than +/-90 degrees, it's nigh impossible.
It's very easy to make subtle mistakes and believe that the measurements you get back are wrong.
Font hinting makes font scaling non-linear when the stroke widths are on the same order of magnitude as the dpi, so if you get a width w for some text, then the width if you double the size of the font may not be exactly 2*w.
Font substitution is common in many printer drivers. Even if you choose a TrueType or OpenType font you might not get the same font on the printer as you got on the screen.
Non-square pixels are common in some printers. These can mess up even decent rasterizers when you're doing something that's not axis-aligned.
Kerning can be surprising. Don't assume that width('a') + width('b') == width("ab").
Rounding errors are easy to make. Sometimes you have to know the rounding rules used by the underlying rasterizer.
Measuring in the wrong context is too common. Don't try to measure in a screen context and apply a transform to get to printer units. If you need printer units, measure in the printer context.
I'm of the opinion today that if you need just print preview, then you should lay out in printer units to a bitmap that's sized to the page, and scale the bitmap to the screen based on the ratios of the DPIs. That's the easiest thing to do and it gives good results. But it's not clear if a good hardcopy and print-preview is really what you're after.
I had similar requirements and ran into the same problems with text rendering and scaling. Even my attempts with WPF (.NET 3.5) were a nightmare, for many reasons.
I ended up using GDI+ graphics.DrawString, despite being 'deprecated', with a funny trick to get accurate text measurements.
static public RectangleF MeasureInkBox(Graphics graphics, string text, Font font)
{
var bounds = new RectangleF();
using (var textPath = new GraphicsPath())
{
textPath.AddString(
text,
font.FontFamily,
(int)font.Style,
font.Size,
new PointF(0, 0),
StringFormat.GenericTypographic );
bounds = textPath.GetBounds();
}
return bounds;
}
The speed and quality turned out to be satisfying. Also, graphics.DrawString is the recommended way to print text with winforms.
Measuring individual character positions can be tricky due to kerning, but I guess a slightly more complex version of this method could do the job. For example, for "Hello" it would measure "H", then "He", then Hel" and so on. It should not degrade performances significantly, especially if you do it on he fly when receiving a click on the word.
I do NOT want the system trying to scale my drawing, I want to do it entirely on my own as any attempt to squeeze/stretch the graphics will produce ugly results. The problem is that as the image gets bigger I want to add more detail rather than have it simply scale up.
Right now I'm looking at two sets of stripes. One is black/white, the other is black/white/white. The pen width is set to 1.
When the line is drawn horizontally it's correct. The same logic drawing vertical lines appears to be doing some antialiasing, bleeding the black onto the nearby white. The black/white/white doesn't look as good as the horizontal, the black/white looks more like medium++ gray/medium-- gray.
The same code is generating the coordinates in all cases, the transform logic is simply selecting what offset to apply where as I am only supporting orientations on the cardinals. Since there's no floating point involved I can't be looking at precision issues.
How do I get the system to leave my graphics alone???
(Yeah, I realize this won't work at very high resolution and eventually I'll have to scale up the lines. Over any reasonable on-screen zoom factor this won't matter, for printer use I'll have to play with it and see where I need to scale. The basic problem is that I'm trying to shoehorn things into too few pixels without just making blobs.)
Edit: There is no scaling going on. I'm generating a bitmap the exact size of the target window. All lines are drawn at integer coordinates. The recommendation of setting SmoothingMode to None changes the situation: Now the black/white/white draws as a very clear gray/gray/white and the black/white draws as a solid gray box. Now that this is cleaned up I can see some individual vertical lines that were supposed to be black are actually doing the same thing of drawing as 2-pixel gray bars. It's like all my vertical lines are off by 1/2 pixel--yet every drawing command gets only integers.
Edit again: I've learned more about the problem. The image is being drawn correctly but trashed when displayed to the screen. (Saving it to disk and viewing it on the very same monitor shows it drawn correctly.)
You really should let the system manage it for you. You have described a certain behavior that is specific to the hardware you are using. Given different hardware, the problem may not exist at all, or it may exist horizontally but not vertically, or may only exist at much smaller or much larger resolutions, etc. etc.
The basic problem you described sounds like the vertical lines are being drawn "between" vertical stacks of pixels, which is causing the system to draw an anti-aliased line. The alternative to anti-aliasing the line is to shift it. The problem with that is the lines will "jitter" or "jerk" if the image is moved around, animated, or scaled or transformed in any other way. Generally, jerk is MUCH less desirable than anti-aliasing because it is more distracting.
You should be able to turn off anti-aliasing using the SmoothingMode enum, or you could try to handle positioning yourself. Either way, you are trading anti-aliasing for jittery, jerky rendering during any movement or transformation.
Have a look at System.Drawing.Drawing2d.SmoothingMode. Setting it to 'Default' or 'None' should turn off anti aliasing when doing line drawing. If you're talking about scaling an image without anti aliasing effects, have a look at InterpolationMode. Specifically, you might wish to set it to 'Nearest-Neighbor' which will keep your rectangular blocks perfectly crisp. Note that you will see some odd effects if you scale your image by anything other than whole numbers.
Perhaps you need to align your lines on half-pixel coordinates? A one pixel line drawn at say x = 5 would be drawn on the center of the line, which means it would go from x = 4.5 to x = 5.5. If you want it to go from x = 4 to x = 5 then you'd need to set its coordinate to x = 4.5.
GDI+ has a property: http://msdn.microsoft.com/en-us/library/system.drawing.graphics.pixeloffsetmode.aspx that allows you to control this behavior.
Sounds like you need to change your application to tell the system it is DPI aware so scaling doesn't occur. Here's an article on doing that: http://msdn.microsoft.com/en-us/library/ms701681%28VS.85%29.aspx
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)