2013-07-31 196 views
5

我在各種尺寸的考慮UI元素(WPF)的屏幕截圖工作,並複印UI元素我能夠做到這一點使用「RenderTargetBitmap,但它有一個Adorner部分UIElement不來而採取的副本。我應該怎麼做,以實現這一目標。任何參考或代碼段?與裝飾器

回答

5

據我所知,元素沒有自己的裝飾器直接引用。裝飾器就通過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)); 

CroppedBitmapRenderTargetBitmap無論從BitmapSource繼承,所以你應該能夠將其保存以同樣的方式。

+0

好吧,這會給裝飾者,但我需要採取整個屏幕截圖。在這種情況下,我必須拍攝兩個不同控件的快照,並且需要將兩個圖像之一集成在另一個之上,並具有許多複雜性,比如找到過去的裝飾圖像的位置...... – Mohanavel

+0

@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"); 
    } 
} 

如果你不想使用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

+2

+1顯示如何實際渲染和保存位圖。裝飾者的問題在於他們被放置在前景層上,而不是被包含在他們所裝飾的元素的可視化樹中。這意味着它們會在您渲染整個窗口時出現,但不會在渲染單個裝飾元素時出現 - 因此可以通過裁剪完整位圖來捕獲單個元素。 – 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); 
     } 
    } 

如果你想在你的畫布中包含所有的裝飾物,這是有效的。