【问题标题】:How can I draw a Polyline onto an Image (WPF)如何在图像上绘制折线(WPF)
【发布时间】:2021-03-10 16:02:39
【问题描述】:

我尝试了几种不同的方法,但似乎无法获得有效的组合。

在 C#、Visual Studio 中创建 WPF 应用程序。

System.Windows.Shapes.Polyline 非常适合实时绘制到画布中,但我希望能够以更高分辨率在非可视组件上绘制,我可以然后渲染到图像上。

如果我在 UI 中可见的 Canvas 上创建折线,则效果很好:

// Make rendertarget size of full page
RenderTargetBitmap rtb = new RenderTargetBitmap((int)wPage, (int)hPage, 96, 96, PixelFormats.Default);
// Render the polyline
rtb.Render(lineVirt); 
// Apply to background image
imgBG.Source = rtb;

但是,如果我在 UI 中不可见的 Canvas 上创建折线,则图像不会呈现任何内容。这可能是足够公平的。我的猜测是组件识别出它是不可见的,因此不会费心渲染。

我曾考虑将 Canvas 放在 UI 中的某个位置,隐藏在其他控件之下,但这似乎是一种可怕的 hack。

基本上,我所需要的只是一种干净而快速的方法,可以在图像上绘制一条指定宽度和颜色的多点线。我认为折线可以很好地工作,但似乎只能在可见的容器中工作。

我有什么选择?

【问题讨论】:

    标签: wpf image polyline rendertargetbitmap


    【解决方案1】:

    您根本不需要渲染的 Canvas 或任何其他可见面板。

    只需使用可视层中可用的基本绘图原语。

    下面的DrawGeometry 方法将Geometry 绘制到BitmapSource 上,使用位图的渲染大小,即考虑其DPI 的大小,并返回生成的BitmapSource。

    public static BitmapSource DrawGeometry(
        BitmapSource source, Pen pen, Geometry geometry)
    {
        var visual = new DrawingVisual();
        var rect = new Rect(0, 0, source.Width, source.Height);
    
        using (var dc = visual.RenderOpen())
        {
            dc.DrawImage(source, rect);
            dc.DrawGeometry(null, pen, geometry);
        }
    
        var target = new RenderTargetBitmap(
            (int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
    
        target.Render(visual);
        return target;
    }
    

    为了绘制位图的像素单元并因此忽略其 DPI,修改方法如下:

    var rect = new Rect(0, 0, source.PixelWidth, source.PixelHeight);
    
    using (var dc = visual.RenderOpen())
    {
        dc.DrawRectangle(new ImageBrush(source), null, rect);
        dc.DrawGeometry(null, pen, geometry);
    }
    

    下面的方法使用上面的方法来绘制一条折线为IEnumerable<Point>

    public static BitmapSource DrawPolyline(
        BitmapSource source, Pen pen, IEnumerable<Point> points)
    {
        var geometry = new PathGeometry();
    
        if (points.Count() >= 2)
        {
            var figure = new PathFigure { StartPoint = points.First() };
            figure.Segments.Add(new PolyLineSegment(points.Skip(1), true));
            geometry.Figures.Add(figure);
        }
    
        return DrawGeometry(source, pen, geometry);
    }
    

    它会像这样使用

    var source = new BitmapImage(new Uri(...));
    
    var pen = new Pen
    {
        Brush = Brushes.Blue,
        Thickness = 2,
    };
    
    var points = new List<Point>
    {
        new Point(100, 100),
        new Point(1000, 100),
        new Point(1000, 1000),
        new Point(100, 1000),
        new Point(100, 100),
    };
    
    image.Source = DrawPolyline(source, pen, points);
    

    【讨论】:

    • 太棒了!感谢您提供如此详细的答案。疯狂的是我尝试过这样的事情,但没有奏效。不知道我做错了什么,但这确实有帮助。
    【解决方案2】:

    你的画布需要一个尺寸,所以某人或某事必须Arrange它。这可能已经足以让它渲染,但将任意视觉效果渲染到位图的唯一可靠方法实际上是place them in the visual tree of a window that's displayed and thus laid out by WPF。然后您可以渲染到位图in a deferred task at ContextIdle priority 以确保布局完整。

    【讨论】: