Проблемы с положением RenderTargetBitmap

Цель

Сделайте снимок экрана элемента управления (или набора элементов управления) с помощью RenderTargetBitmap.

Источник:

<Grid Height="200" Width="500">
    <!-- Here goes any content, in my case, a Label or a Shape-->
    <Label VerticalAligment="Top" HorizontalAligment="Left" Content="Text">
</Grid>

Ожидаемый результат:

Ожидается

Способ 1

Этот в основном использует UIElement в качестве источника RenderTargetBitmap.

public static ImageSource GetRender(this UIElement source)
{
     double actualHeight = source.RenderSize.Height;
     double actualWidth = source.RenderSize.Width;

     var renderTarget = new RenderTargetBitmap((int)Math.Round(actualWidth), 
         (int)Math.Round(actualHeight), 96, 96, PixelFormats.Pbgra32);

     renderTarget.Render(source);
     return renderTarget;
}

Результат:

Результат метода 1

Способ 2:

Вместо того, чтобы напрямую устанавливать UIElement в качестве источника RenderTargetBitmap, я буду использовать VisualBrush.

//Same RenderTargetBitmap...

DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
    VisualBrush vb = new VisualBrush(target);
    ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);

Результат:

Этот игнорирует положение и размер Grid и Label внутри:

введите здесь описание изображения

Что тут происходит?


person Nicke Manarin    schedule 25.08.2015    source источник
comment
Не могли бы вы показать, как создается сетка с меткой?   -  person Clemens    schedule 25.08.2015


Ответы (2)


Модифицированный метод 2

Мне просто нужно было получить границы потомка Grid и отрисовать только нужную часть.

public static ImageSource GetRender(this UIElement source, double dpi)
{
    Rect bounds = VisualTreeHelper.GetDescendantBounds(source);

    var scale = dpi / 96.0;
    var width = (bounds.Width + bounds.X)*scale;
    var height = (bounds.Height + bounds.Y)*scale;

    RenderTargetBitmap rtb = 
        new RenderTargetBitmap((int)Math.Round(width, MidpointRounding.AwayFromZero), 
        (int)Math.Round(height, MidpointRounding.AwayFromZero), 
        dpi, dpi, PixelFormats.Pbgra32);        

    DrawingVisual dv = new DrawingVisual();
    using (DrawingContext ctx = dv.RenderOpen())
    {
        VisualBrush vb = new VisualBrush(source);
        ctx.DrawRectangle(vb, null, 
            new Rect(new Point(bounds.X, bounds.Y), new Point(width, height)));
    }

    rtb.Render(dv);
    return (ImageSource)rtb.GetAsFrozen();
}

Результат:

Визуализированные Label/Shape:

Визуализировано

Объединено с другим изображением:

Объединено

person Nicke Manarin    schedule 26.08.2015
comment
Вам нужно уменьшить ширину и высоту при рендеринге: new Rect(new Point(bounds.X, bounds.Y), new Point(width / scale, height / scale)));. - person 15ee8f99-57ff-4f92-890c-b56153; 05.02.2019

Это связано с тем, что RenderTargetBitmap визуализирует визуальный объект на основе координат его родительского объекта. Margin сам по себе, Padding или BorderThickness его родителя будут влиять на визуализируемое изображение. Чтобы решить эту проблему, вы можете просто добавить поддельный родительский контейнер: если исходное визуальное логическое дерево похоже на

<Grid>
    <Canvas Margin="20" />
</Grid> 

изменился на

<Grid>
    <Border Margin="20">
        <Canvas />
    </Border>
</Grid> 

Настройки для Alignment\Margin также должны быть перемещены в родительский элемент. Теперь вы получите то, что хотите.

person Shannon    schedule 06.08.2018