1

我正在开发一个自定义 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>

为了获得更好的性能,所有绘图操作都在 ItemAreaElement 的 OnRender 方法中执行。为了获得清晰的绘图,我还在初始化代码中使用了以下设置:

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;
        }
    }
}

当我在配备 282ppi 超高清屏幕(系统比例因子为 300%)的主开发笔记本电脑上启动此代码时,我得到了这张图片:

在此处输入图像描述

或者,在使用网格线放大paint.net 之后:

在此处输入图像描述

如您所见,我的 ItemAreaElement 的左边缘和上边缘被控件的边框部分覆盖。一定要这样吗?有没有我可以用来避免这种情况的设置?

第二个问题是不包括起点的线(请参阅我的“单元格”的左上角)。这是预期的行为吗?如果是这样,如何强制 WPF 绘制起始像素?

第三个问题是绿线应相交的位置或与设备无关的点(我的单元格的右下角)。如您所见,这一点是锯齿状的。我预计在那个地方只会看到一个绿色的广场。我可以在 DrawingContext.DrawLine 方法的帮助下实现吗?或者我是否需要使用更复杂的几何图形以及多点线的特殊设置等?

顺便说一句,当我在具有“经典”96 ppi 显示器且操作系统的比例因子设置为 100% 的测试电脑上启动此代码时,右下角的情况要好一些:

在此处输入图像描述

但我什至看不到顶行的水平红线或第一列的垂直红线。我希望在那里看到它们,但不会被控件的边界覆盖。如果您知道如何解决所有这些问题,请告诉我。

4

1 回答 1

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;
    }
}
于 2016-12-08T12:26:06.250 回答