Я работаю над настраиваемым элементом управления WPF, который должен визуализировать тысячи графических примитивов в области с прокруткой. Основной частью шаблона элемента управления состоит в следующем:Странный рисунок с DrawingContext в 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;
}
}
}
Когда я запускаю этот код на мой основной Dev ноутбук, снабженный Ultra-HD экран с 282ppi (коэффициент системы масштаб 300%) , я получаю эту картину:
Или, после масштабирования в Paint.NET с сеткой:
Как вы видите, левый и верхний края элемента ItemAreaElement частично покрыты границей элемента управления. Должно быть так? Есть ли способ, который я могу использовать, чтобы избежать этого?
Вторая проблема - это строки, которые не включают начальную точку (см. Верхний левый угол моих «ячеек»). Это ожидаемое поведение? ЕСЛИ так, как заставить WPF нарисовать начальный пиксель?
И третья проблема - это место или независимая от устройства точка, в которой должны встречаться зеленые линии (нижний правый угол моих ячеек). Как вы можете видеть, этот момент зазубрен. Я ожидал увидеть только зеленую площадь в этом месте. Могу ли я реализовать это с помощью метода DrawingContext.DrawLine? Или мне нужно использовать более сложную геометрию со специальными настройками для многоточечных линий и т. Д.?
BTW, когда я запускаю этот код на тестовом ПК с «классическим» 96-пиксельным монитором и масштабным коэффициентом ОС, установленным на 100%, в нижнем правом углу ситуация немного лучше:
Но я даже не видите горизонтальные красные линии в верхнем ряду или вертикальных красных линий в первом столбце. Я ожидал увидеть их там, но не должен быть охвачен границей контроля. Если вы знаете, как исправить все эти проблемы, скажите мне, пожалуйста.
Имейте в виде, что WPF использует векторную графику, где координаты не являются пикселями, но с плавающей точкой «устройство независимых единиц» (типа ' double'). Поэтому «заполнение» пикселя в позиции «(x, y)» потребует рисовать линию нулевой длины на '(x + 0,5, y + 0,5)' с помощью Pen с 'Thickness', установленным в' 1.0' и ' StartLineCap' и 'EndLineCap' установлены на' Квадрат'. – Clemens
@Clemens, я знаю об этом характере WPF. Это объясняется во многих статьях, таких как [этот] (https://www.wpftutorial.net/DrawOnPhysicalDevicePixels.html) в учебнике WPF (см. Раздел «Выровнять края, а не центральные точки»). Считаете ли вы, что это будет работать для любого разрешения экрана и коэффициента масштабирования системы? – TecMan
Несомненно, вам нужно иметь в виду, где находится начало координат. Если вы, например, нарисуйте горизонтальную линию на 'y = 0' (и управляющие клипсы на ее границу), вы увидите только нижнюю половину строки. – Clemens