0

my code below is supposed to display N lines per inch. instead i get a little more than N lines per inch. the distance between lines is somewhat smaller. in addition, changing the screen resolution makes the distance between lines change too. does anyone know how to handle that?


using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MyApp
{
    class MyControl : Control
    {
        private readonly ContainerVisual container = new ContainerVisual();
        private readonly DrawingVisual drawing = new DrawingVisual();

        private void RenderDrawing()
        {
            var s = PresentationSource.FromVisual(this);
            var dpiX = 96 * s.CompositionTarget.TransformToDevice.M11;
            var dpiY = 96 * s.CompositionTarget.TransformToDevice.M22;

            double N = 1;

            using (var c = drawing.RenderOpen())
            {
                var p = new Pen(new SolidColorBrush(Colors.Black), 1);

                for (int i = 0; i < 10; i++)
                {
                    var x = i * dpiX / N;
                    c.DrawLine(p, new Point(x, 0), new Point(x, 100));
                }
            }
        }

        protected override Size ArrangeOverride(Size s)
        {
            RenderDrawing();
            return s;
        }

        protected override Visual GetVisualChild(int index)
        {
            return container;
        }

        protected override Size MeasureOverride(Size s)
        {
            return new Size();
        }

        protected override int VisualChildrenCount
        {
            get { return 1; }
        }

        public MyControl()
        {
            container.Children.Add(drawing);
            AddVisualChild(container);
        }
    }
}
akonsu
  • 28,824
  • 33
  • 119
  • 194
  • I am assuming this is just some sample code, but it appears that you are always going to draw 10 lines. Not N lines per inch. I could be missing something, but I am pretty sure the for loop from 0 - 9 is going to draw 10 lines. Also, you should put the Pen in a using statement (using var p = new Pen...) otherwise you will end up leaking GDI handles. – pstrjds Nov 22 '10 at 19:44
  • yes, it is sample code. yes it draws ten lines. it is supposed to draw these ten lines so that they are 1/N inch apart. – akonsu Nov 22 '10 at 19:46
  • Okay, I would maybe edit the question to explain that, I read it as you want N lines per inch not 10 lines with a distance of 1/N between them. As a matter of fact, reading your question again I am a little confused, you say the gap changes as resolution changes, but that is exactly what you want to happen if you want only 10 lines with a distance between based on the current resolution then the distance changes as resolution changes. – pstrjds Nov 22 '10 at 19:48
  • there seems to be misunderstanding...i want the density of lines to be N lines per inch, but it does not matter how many lines i draw... ten or one hundred. N lines per inch makes the distance between them to be 1/N of an inch. – akonsu Nov 22 '10 at 19:51
  • Okay, well in that case the spacing is going to change as you adjust resolution, but I would recommend maybe computing dpiX / N outside of the loop and checking what this value is. I think you may be running into some rounding issues. Run it under the debugger and check what the value for dpiX / N is. From a rough glance the code seems right for your spacing. – pstrjds Nov 22 '10 at 19:56
  • Well, I take it back you are basically computing a DPI of 96 in your code. I think you want to translate the other direction. If you want the lines spaced out for 96 dpi, compute the location but then transform those coordinates to the control. At least that is what I would do, I like to compute the points array and then run the whole thing through the transformation matrix at once, rather than computing a rounded dpi and using that. – pstrjds Nov 22 '10 at 20:00

2 Answers2

1

this article seems to discuss the same problem: WPF DPI issues there is no solution to this problem other than asking the user to set the correct DPI settings that correspond to the physical DPI of the screen. a workaround that i found that makes life a little easier is to use WPF application level scaling as described here: http://www.odewit.net/ArticleContent.aspx?id=WpfDpiScaling&lang=en&format=html

Community
  • 1
  • 1
akonsu
  • 28,824
  • 33
  • 119
  • 194
  • I figured the issue has to do with resolution differences and rounding. I do a lot of work with developing interfaces for users to be able to redact information from images. Scaling and resolution are some of the most difficult things to deal with (what happens to a box when a user zooms in and out, what happens when they change their resolution, etc). Looks like the perspective class (linked in the article you linked: http://perspective.codeplex.com/ will accomplish what you need. – pstrjds Nov 23 '10 at 13:39
0

Not sure if this would accomplish what you are looking for, but it should help if the issue has to do with rounding. As I said in comments, your code looks mostly correct, I think it is a rounding issue with the dpi computation. Since you are desiring to render based on 96 dpi, compute the coordinates based on 96dpi and then convert the points to your device. I wrote this more for clarity, you could use a single array of points and just remember that i is the start point and i+1 is the end point and then you would only have to make a single call to transform the points.

    private void RenderDrawing()
    {
        var s = PresentationSource.FromVisual(this);
        var dpiX = 96;

        int numberOfLines = 10;
        double N = 1;
        double spacing = dpiX / N;

        var startPoints = new Point[numberOfLines]();
        var endPoints = new Point[numberOfLines]();
        for (int i = 0; i < numberOfLines; i++)
        {
            var x = i * spacing;
            startPoints[i] = new Point(x, 0);
            endPoints[i] = new Point(x, 100);                 
        }
        s.CompositionTarget.TransformToDevice.Transform(startPoints);
        s.CompositionTarget.TransformToDevice.Transform(endPoints);

        using (var c = drawing.RenderOpen())
        {
            using (var p = new Pen(new SolidColorBrush(Colors.Black), 1))
            {
                 for(int i=0; i < numberOfLines; i++)
                 {
                    c.DrawLine(p, startPoints[i], endPoints[i]);
                 }
            }
        }
    }
pstrjds
  • 16,840
  • 6
  • 52
  • 61
  • thanks for your help. it seems you did not escape a less than sign in the second line... – akonsu Nov 22 '10 at 20:23
  • good catch, I had intended to cut/paste the line when I was editing and apparently copy/pasted instead. Should be fixed now. – pstrjds Nov 22 '10 at 20:53