2013-07-31 4 views
5

Я работаю над тем, чтобы снимать снимок экрана элемента UI (WPF) различного размера, и я могу достичь этого с помощью «RenderTargetBitmap». Но UIElement, который имеет часть Adorner, не приходит, когда вы делаете копию. Что я должен делать, чтобы достичь этого. Любая ссылка или фрагмент кода?Копировать элемент пользовательского интерфейса с adorner

ответ

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

Размер Adorner, кажется, включает размер украшенного элемента (хотя я не уверен, что это всегда так), так что если есть только один adorner, то есть ваш размер экрана. Если есть более одного adorner, вам нужно будет найти максимум для каждой границы (слева, сверху, справа, внизу), чтобы вычислить область скриншота.

Вам необходимо будет снять AdornerDecorator, который содержит как украшаемые элементы, так и AdornerLayer (layer в вышеуказанном коде). Это было бы визуальный родитель слоя:

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, который достаточно велик, чтобы содержать все их.

Наконец, используйте CroppedBitmap, чтобы получить обрезанную версию RenderTargetBitmap:

var croppedBitmap = new CroppedBitmap(renderTargetBitmap, new Int32Rect(left, top, width, height)); 

CroppedBitmap и RenderTargetBitmap как наследовать от BitmapSource, так что вы должны быть в состоянии сохранить его таким же образом.

+0

хорошо, это даст adorner, но мне нужно снять снимок экрана целиком. В этом случае я должен сделать снимок двух разных элементов управления и вам нужно интегрировать два изображения один поверх других с большой сложностью, например, найти местоположение в прошлом изображении adorner ..... – Mohanavel

+0

@Mohanavel Вам нужно захватить весь родительский элемент и затем выберите из него область, в которой занимаются элементы управления. Это оказалось более сложным, чем я впервые представил, поэтому я более подробно отредактировал свой пост. – nmclean

4

Вы можете использовать родной WPF Printing пространство имен для печати в файл XPS, и это будет включать в себя Adorner в результате (я проверил это успешно) ...

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-файл на самом деле является файлом «zip», который маскируется как файл «xps», поэтому вы можете разархивировать его, изменив расширение, чтобы узнать, какое содержимое используется.

Вторично я проверил сохранение окна с Adorner на 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(); 
     } 
    } 

... с хорошими результатами. I.e., adorner появился в сохраненном растровом изображении с красной рамкой. Это может отличаться от вашего кода, потому что я использую Png-кодировщик (но сохраняется в файле «jpg»).

Хотя я успешно протестировал оба подхода, вам необходимо проверить их на своем оборудовании.

И, наконец, в качестве последней отчаянной крайнем случае, вы можете отключить режим аппаратного рендеринга WPF и установить его на программный рендеринг ...

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; 

... для которого есть хороший SO нить здесь: Software rendering mode - 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); 
     } 
    } 

Это работает, если вы хотите включить ВСЕ рекламные ролики в свой холст.