Странный рисунок с DrawingContext в WPF

Я работаю над пользовательским элементом управления WPF, который должен визуализировать тысячи графических примитивов в прокручиваемой области. Основная часть шаблона элемента управления следующая:

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:ItemVisualizer}">
            <Border Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <local:ItemAreaElement Grid.Row="0" Grid.Column="0" x:Name="PART_ItemArea" />
                    <ScrollBar Grid.Row="0" Grid.Column="1" x:Name="PART_ScrollBarVert" Orientation="Vertical" Maximum="100" />
                    <ScrollBar Grid.Row="1" Grid.Column="0" x:Name="PART_ScrollBarHorz" Orientation="Horizontal" Maximum="100" />
                    <Rectangle Grid.Row="1" Grid.Column="1" x:Name="PART_SizeGrip" Focusable="False" Fill="#F0F0F0" />
                </Grid>

            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>

Для повышения производительности все операции рисования выполняются в методе OnRender элемента ItemAreaElement. Для четкого рисования я также использую следующие настройки в коде инициализации:

this.SetValue(RenderOptions.EdgeModeProperty, EdgeMode.Aliased);

Однако у меня есть некоторые странные проблемы с моим рисунком. Чтобы продемонстрировать их, я упростил определение моего ItemAreaElement до следующего:

class ItemAreaElement : FrameworkElement
{
    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        const int ITEM_WIDTH = 60;
        const int ITEM_HEIGHT = 20;

        Pen penRed = new Pen(Brushes.Red, 1);
        Pen penGreen = new Pen(Brushes.Green, 1);

        int y = 0;
        for (int iRow = 0; iRow < 3; iRow++)
        {
            int x = 0;
            for (int iCol = 0; iCol < 2; iCol++)
            {
                Point cornerTopLeft = new Point(x, y);
                dc.DrawLine(penRed, cornerTopLeft, new Point(x + ITEM_WIDTH - 1, y));
                dc.DrawLine(penRed, cornerTopLeft, new Point(x, y + ITEM_HEIGHT - 1));

                Point cornerBottomRight = new Point(x + ITEM_WIDTH - 1, y + ITEM_HEIGHT - 1);
                dc.DrawLine(penGreen, new Point(x + ITEM_WIDTH - 1, y), cornerBottomRight);
                dc.DrawLine(penGreen, new Point(x, y + ITEM_HEIGHT - 1), cornerBottomRight);

                x += ITEM_WIDTH;
            }
            y += ITEM_HEIGHT;
        }
    }
}

Когда я запускаю этот код на своем главном ноутбуке с экраном Ultra-HD с 282ppi (коэффициент масштабирования системы составляет 300%), я получаю такую ​​картинку:

Или, после увеличения paint.net с линиями сетки:

Как видите, левый и верхний края моего ItemAreaElement частично покрыты границей элемента управления. Так ли это? Есть ли настройка, которую я могу использовать, чтобы избежать этого?

Вторая проблема - линии, которые не включают начальную точку (см. Верхний левый угол моих "ячеек"). Это ожидаемое поведение? Если так, как заставить WPF рисовать начальный пиксель?

И третья проблема - это место или точка, не зависящая от устройства, в которой должны встречаться зеленые линии (нижний правый угол моих клеток). Как видите, эта точка зазубрена. Я ожидал увидеть только зеленый квадрат в этом месте. Могу ли я реализовать это с помощью метода DrawingContext.DrawLine? Или мне нужно использовать более сложную геометрию со специальными настройками для многоточечных линий и т. Д.?

Кстати, когда я запускаю этот код на тестовом ПК с "классическим" монитором 96 ppi и масштабным коэффициентом ОС, установленным на 100%, в правом нижнем углу ситуация немного лучше:

Но я даже не вижу горизонтальных красных линий в верхнем ряду или вертикальных красных линий в первом столбце. Я ожидал увидеть их там, но не был покрыт границей контроля. Если вы знаете, как исправить все эти проблемы, подскажите, пожалуйста.

1 ответ

Мне удалось решить все мои проблемы, установив соответствующие рекомендации. Ниже вы найдете улучшенную версию метода OnRender(), представленную выше:

protected override void OnRender(DrawingContext dc)
{
    base.OnRender(dc);

    const int ITEM_WIDTH = 60;
    const int ITEM_HEIGHT = 20;

    const double sizeOfPen = 1;
    double halfSizeOfPen = sizeOfPen / 2.0;

    Pen penRed = new Pen
    {
        Brush = Brushes.Red,
        Thickness = sizeOfPen,
        StartLineCap = PenLineCap.Square,
        EndLineCap = PenLineCap.Square
    };
    Pen penGreen = new Pen
    {
        Brush = Brushes.Green,
        Thickness = sizeOfPen,
        StartLineCap = PenLineCap.Square,
        EndLineCap = PenLineCap.Square
    };

    int y = 0;
    for (int iRow = 0; iRow < 3; iRow++)
    {
        int x = 0;
        for (int iCol = 0; iCol < 2; iCol++)
        {
            GuidelineSet guidelines = new GuidelineSet();
            guidelines.GuidelinesX.Add(x);
            guidelines.GuidelinesX.Add(x + ITEM_WIDTH);
            guidelines.GuidelinesY.Add(y);
            guidelines.GuidelinesY.Add(y + ITEM_HEIGHT);

            dc.PushGuidelineSet(guidelines);

            Point cornerTopLeft = new Point(x + halfSizeOfPen, y + halfSizeOfPen);
            dc.DrawLine(penRed, cornerTopLeft, new Point(x + ITEM_WIDTH - halfSizeOfPen, y + halfSizeOfPen));
            dc.DrawLine(penRed, cornerTopLeft, new Point(x + halfSizeOfPen, y + ITEM_HEIGHT - halfSizeOfPen));

            Point cornerBottomRight = new Point(x + ITEM_WIDTH - halfSizeOfPen, y + ITEM_HEIGHT - halfSizeOfPen);
            dc.DrawLine(penGreen, new Point(x + ITEM_WIDTH - halfSizeOfPen, y + halfSizeOfPen), cornerBottomRight);
            dc.DrawLine(penGreen, new Point(x + halfSizeOfPen, y + ITEM_HEIGHT - halfSizeOfPen), cornerBottomRight);

            dc.Pop();

            x += ITEM_WIDTH;
        }
        y += ITEM_HEIGHT;
    }
}
Другие вопросы по тегам