我正在试图捕捉 WPF UI 元素的屏幕截图,使用 "RenderTargetBitmap" 我已经能够在各种尺寸下实现此目标。但是,在获取复制时,带有 "Adorner" 部分的 UIElement
未显示。我应该怎么做才能实现这一点?请提供参考或代码片段。
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
足够大以包含它们所有。RenderTargetBitmap
的裁剪版本:var croppedBitmap = new CroppedBitmap(renderTargetBitmap, new Int32Rect(left, top, width, height));
CroppedBitmap
和 RenderTargetBitmap
都继承自 BitmapSource
,因此您应该能够以相同的方式保存它。
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 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
。 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);
}
}
如果您想在画布中包含所有装饰器,则可以使用此方法。