复制带装饰器的UI元素

5

我正在试图捕捉 WPF UI 元素的屏幕截图,使用 "RenderTargetBitmap" 我已经能够在各种尺寸下实现此目标。但是,在获取复制时,带有 "Adorner" 部分的 UIElement 未显示。我应该怎么做才能实现这一点?请提供参考或代码片段。

3个回答

5
据我所知,元素没有直接引用其修饰器。但是,修饰器确实通过 AdornedElement 引用它们的元素,因此您可以像这样搜索分配给您元素的修饰器:
var layer = AdornerLayer.GetAdornerLayer(element);
var adorners = layer.GetVisualChildren().Cast<Adorner>().Where(a => a.AdornedElement == element);

这里的GetVisualChildren是一个扩展方法,定义如下:

public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject current) {
    return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(current)).Select(i => VisualTreeHelper.GetChild(current, i));
}

装饰器的大小似乎包括被装饰元素的大小(尽管我不确定这是否总是如此),因此如果只有一个装饰器,那就是您的屏幕截图大小。如果有多个装饰器,则需要找到每个边界(左,上,右,下)的最大值来计算屏幕截图区域。

您需要捕获包含被装饰元素和装饰器层(上面代码中的layer)的AdornerDecorator。那将是该层的可视父级:

var container = VisualTreeHelper.GetParent(layer) as Visual;

一旦您拥有容器,您可以使用RenderTargetBitmap将其呈现并裁剪到屏幕截图区域。
对于屏幕截图区域,您需要相对于容器的元素边界。首先,获取非相对边界:
var elementBounds = element.RenderTransform.TransformBounds(new Rect(element.RenderSize));

然后获取相对于容器的边界:
var relativeElementBounds = element.TransformToAncestor(container).TransformBounds(elementBounds);

正如我上面提到的,您需要为元素以及其每个装饰者都执行此操作,并将最大边界组合成一个最终的Rect,该Rect足够大以包含它们所有。
最后,使用CroppedBitmap获取RenderTargetBitmap的裁剪版本:
var croppedBitmap = new CroppedBitmap(renderTargetBitmap, new Int32Rect(left, top, width, height));

CroppedBitmapRenderTargetBitmap 都继承自 BitmapSource,因此您应该能够以相同的方式保存它。


好的,这将给出adorner(装饰器),但我需要截取整个屏幕。在这种情况下,我必须截取两个不同控件的快照,并需要整合两个图像,一个在另一个之上,并且还有很多复杂性,比如找到粘贴adorner(装饰器)图像的位置..... - Mohanavel
@Mohanavel 你需要捕获整个父元素,然后从中选择控件所占据的区域。这比我最初想象的要复杂,因此我已经编辑了我的帖子并提供了更多细节。 - nmclean

4
您可以使用本地的WPF打印命名空间来打印到XPS文件中,这将包括修饰符在结果中(我已经成功测试过)。
using System.Windows.Controls;
private void ExecutePrintCommand(object obj)
{
    PrintDialog printDialog = new PrintDialog();
    if (printDialog.ShowDialog() == true)
    {
        printDialog.PrintVisual(_mainWindow, "Main Window with Adorner");
    }
}

如果您不想使用打印对话框(实际上是打开一个对话框),您可以使用XpsDocumentWriter类在程序中控制这个过程。启用此功能的代码片段如下:
     XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(q);
     xpsdw.Write(viewer.Document);

...这段内容是从这里提取的:以编程方式打印FixedDocument,如果您的要求需要,还有更多有关微调过程的文章。请注意,XPS文件实际上是一个伪装成“xps”文件的“zip”文件,因此您可以通过更改扩展名来解压缩它,查看其内容是否有用。

其次,我使用以下代码测试了保存一个带有TextBox附属物的窗口...

    private void SaveWithAdorner()
    {
        RenderTargetBitmap rtb = RenderVisaulToBitmap(_mainWindow, 500, 300);
        MemoryStream file = new MemoryStream();
        BitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(rtb));
        encoder.Save(file);
        using (FileStream fstream = File.OpenWrite("Myimage.jpg"))
        {
            file.WriteTo(fstream);
            fstream.Flush();
            fstream.Close();
        }
    }

...取得了良好的效果。即,装饰器以其红色边框出现在保存的位图中。这可能与您的代码不同,因为我使用了Png编码器(但保存为“jpg”文件)。

虽然我已经成功测试了这两种方法,但您需要在自己的硬件上进行检查。

最后,作为最后的手段,您可以关闭WPF的硬件渲染模式并将其设置为软件渲染...

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;

这里有一个很好的SO线程,与之相关的是软件渲染模式-WPF


2
+1 是为了展示如何实际渲染和保存位图。adorners 的问题在于它们被放置在前景层而不是包含在所装饰元素的可视树中。这意味着当您渲染整个窗口时,它们将出现,但当您渲染单个装饰元素时,它们将不会出现 - 因此通过裁剪完整的位图可以实现捕获单个元素。 - nmclean

0
在我的情况下,我所需要的只是像这样调用AdornerLayer类:
    public void GetScreenshotWithAdorner(Canvas canvas, string filename)
    {
      AdornerLayer adornerlayer = AdornerLayer.GetAdornerLayer(canvas);

      RenderTargetBitmap rtb = new RenderTargetBitmap(
        (int)canvas.ActualWidth,
        (int)canvas.ActualHeight,
        96, //dip X
        96, //dpi Y
        PixelFormats.Pbgra32);
      rtb.Render(canvas); //renders the canvas screen first...
      rtb.Render(adornerlayer); //... then it renders the adorner layer

      SaveRTBAsPNG(rtb, filename);
    }

    private void SaveRTBAsPNG(RenderTargetBitmap bmp, string filename)
    {
      PngBitmapEncoder pngImage = new PngBitmapEncoder();

      pngImage.Frames.Add(BitmapFrame.Create(bmp));
      using (var filestream = System.IO.File.Create(filename))
      {
        pngImage.Save(filestream);
      }
    }

如果您想在画布中包含所有装饰器,则可以使用此方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接