我在各种尺寸的考虑UI元素(WPF)的屏幕截图工作,并复印UI元素我能够做到这一点使用“RenderTargetBitmap,但它有一个Adorner
部分UIElement
不来而采取的副本。我应该怎么做,以实现这一目标。任何参考或代码段?与装饰器
与装饰器
回答
据我所知,元素没有自己的装饰器直接引用。装饰器就通过AdornedElement引用它们的元素,因此,您可以搜索装饰器分配给你的元素是这样的:
var layer = AdornerLayer.GetAdornerLayer(element);
var adorners = layer.GetVisualChildren().Cast<Adorner>().Where(a => a.AdornedElement == element);
这里图10是其被定义为扩展方法:
public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject current) {
return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(current)).Select(i => VisualTreeHelper.GetChild(current, i));
}
装饰器的大小似乎包括佐餐元件的尺寸(虽然我不知道这是否是总是如此),因此,如果有只有一个装饰者,那就是你的截图尺寸。如果有多个装饰器,则需要为每个装订(左,上,右,下)找到最大值来计算屏幕截图区域。
您需要捕捉其中包含两个被装饰的元素AdornerDecorator
,并(在上面的代码layer
)的AdornerLayer
。这将是该层的视觉父:
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足够大以容纳所有这些Rect。
最后,使用CroppedBitmap得到RenderTargetBitmap
的裁剪版本:
var croppedBitmap = new CroppedBitmap(renderTargetBitmap, new Int32Rect(left, top, width, height));
CroppedBitmap
和RenderTargetBitmap
无论从BitmapSource
继承,所以你应该能够将其保存以同样的方式。
您可以使用本机打印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");
}
}
如果你不想使用PrintDialog(实际上打开一个对话框)。您可以使用XpsDocumentWriter类以编程方式控制过程。造成这种情况的实现代码片段是...
XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(q);
xpsdw.Write(viewer.Document);
...这是从这里提取:Print FixedDocument programmatically而且还有更多的文章微调的过程中,如果这是你的要求的一部分。请注意,XPS文件实际上是伪装成'xps'文件的'zip'文件,因此您可以通过更改扩展名来查看内容是否有用,从而将其解压缩。
其次,我测试保存窗口上,此代码文本框装饰器...
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线程在这里:Software rendering mode - WPF
+1显示如何实际渲染和保存位图。装饰者的问题在于他们被放置在前景层上,而不是被包含在他们所装饰的元素的可视化树中。这意味着它们会在您渲染整个窗口时出现,但不会在渲染单个装饰元素时出现 - 因此可以通过裁剪完整位图来捕获单个元素。 – nmclean
在我来说,我只需要调用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);
}
}
如果你想在你的画布中包含所有的装饰物,这是有效的。
- 1. 装饰装饰器
- 2. Typescript mixin类与装饰器
- 3. 如何将装饰器与@contextmanager装饰器混合使用?
- 4. WPF装饰者与定制装饰者?
- 5. Python装饰器
- 6. 在Python中装饰装饰器
- 7. 装饰模式装饰器限制
- 8. 在春天自动装饰装饰器
- 9. 问题与装饰
- 10. 奇诡与装饰
- 11. 线程与装饰
- 12. 如何将SQLAlchemy的@hybrid_property装饰器与Werkzeug的cached_property装饰器结合使用?
- 13. Typescript装饰器与类的继承
- 14. 拖放与拖放装饰器
- 15. Singleton模式与装饰器相结合
- 16. 使'isinstance'与装饰器一起工作
- 17. 装饰渲染器
- 18. 切换装饰器
- 19. Django @login_required装饰器
- 20. 创建装饰器
- 21. Python memoization装饰器
- 22. 装饰器是不同于装饰器的功能类吗?
- 23. Python中的装饰器必须实现装饰器模式吗?
- 24. Django:在其他装饰器中重新使用login_required装饰器
- 25. Python装饰器和装饰器模式有什么区别?
- 26. 芹菜@task装饰器后的装饰器
- 27. Sitemesh 2.4.2:如何使用除装饰器之外的装饰器
- 28. 如何在装饰器中重用TypeScript中的装饰器
- 29. 使用装饰器,(rails)无法推断ActiveRecord :: Base的装饰器
- 30. ECMAScript 2016装饰器是否使TypeScript装饰器变得多余?
好吧,这会给装饰者,但我需要采取整个屏幕截图。在这种情况下,我必须拍摄两个不同控件的快照,并且需要将两个图像之一集成在另一个之上,并具有许多复杂性,比如找到过去的装饰图像的位置...... – Mohanavel
@Mohanavel您需要捕获整个父元素然后从中选择控件占用的区域。事实证明,这比我想象的要复杂得多,所以我编辑了更多细节。 – nmclean