我在各種尺寸的考慮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