2013-07-31 26 views
5

Pracuję nad zrobieniem zrzutu ekranu elementu interfejsu użytkownika (WPF) w różnych rozmiarach i jestem w stanie to zrobić za pomocą "RenderTargetBitmap." Ale UIElement, który ma część Adorner, nie przyjdzie podczas kopiowania. Co powinienem zrobić, aby to osiągnąć Jakieś odniesienie lub fragment kodu?Kopiowanie elementu interfejsu użytkownika z adornerem

Odpowiedz

5

Z tego co wiem, elementy nie mają bezpośrednich odniesień do ich adoratorów, ale adoratorzy odnoszą się do ich elementu poprzez AdornedElement, więc można szukać adorners przypisany do Twojego elementu w ten sposób:

var layer = AdornerLayer.GetAdornerLayer(element); 
var adorners = layer.GetVisualChildren().Cast<Adorner>().Where(a => a.AdornedElement == element); 

Tutaj GetVisualChildren stanowi metodę rozszerzenia, które określa się jako:

public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject current) { 
    return Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(current)).Select(i => VisualTreeHelper.GetChild(current, i)); 
} 

Wielkość adorner wydaje się obejmować rozmiar elementu zdobi (choć nie jestem pewien, czy to zawsze tak jest), więc jeśli nie ma tylko jeden adorner, czyli twój rozmiar ekranu. Jeśli jest więcej niż jeden adorner, musisz znaleźć maksymalną wartość dla każdej granicy (lewy, górny, prawy, dolny), aby obliczyć region zrzutu ekranu.

Będziesz musiał przechwycić AdornerDecorator, który zawiera zarówno elementy ozdabiane, jak i AdornerLayer (layer w powyższym kodzie). To byłby wizualny rodzic warstwy:

var container = VisualTreeHelper.GetParent(layer) as Visual; 

Gdy masz pojemnik, można uczynić go RenderTargetBitmap i przyciąć go do obszaru ekranu.

Dla regionu zrzutu ekranu wymagane są ograniczenia elementów względem kontenera. Po pierwsze, uzyskać non-względne granice:

var elementBounds = element.RenderTransform.TransformBounds(new Rect(element.RenderSize)); 

Następnie dostać te granice względem pojemnika:

var relativeElementBounds = element.TransformToAncestor(container).TransformBounds(elementBounds); 

Jak już wspomniano wyżej, trzeba będzie to zrobić dla elementu, jak każdy z jego adorners i połączyć maksymalne granice w jeden końcowy Rect, który jest wystarczająco duży, aby pomieścić wszystkie z nich.

Wreszcie, należy CroppedBitmap dostać przyciętej wersji RenderTargetBitmap:

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

CroppedBitmap i RenderTargetBitmap zarówno dziedziczyć BitmapSource, więc powinieneś być w stanie go uratować w ten sam sposób.

+0

cóż, da to adorner, ale muszę zrobić zrzut ekranu całości. W tym przypadku muszę wykonać migawkę z dwóch różnych kontrolek i trzeba zintegrować dwa obrazy jeden na górze z dużą ilością złożoności, jak znalezienie lokalizacji do przeszłości obrazu adornera ..... – Mohanavel

+0

@Mohanavel Musisz uchwycić cały element nadrzędny a następnie wybierz z niego region, w którym znajdują się kontrolki. Okazuje się, że jest to bardziej skomplikowane, niż początkowo sądziłem, więc zredagowałem mój post bardziej szczegółowo. – nmclean

4

Można używać rodzimych WPF Printing przestrzeń nazw, aby wydrukować do pliku XPS i będzie obejmować adorner w wyniku (ja testowałem to z powodzeniem) ...

using System.Windows.Controls; 
private void ExecutePrintCommand(object obj) 
{ 
    PrintDialog printDialog = new PrintDialog(); 
    if (printDialog.ShowDialog() == true) 
    { 
     printDialog.PrintVisual(_mainWindow, "Main Window with Adorner"); 
    } 
} 

Jeśli nie chcą użyj PrintDialog (który faktycznie otwiera okno dialogowe). Możesz użyć klasy XpsDocumentWriter, aby programowo sterować procesem. Umożliwianie fragment tego jest ...

 XpsDocumentWriter xpsdw = PrintQueue.CreateXpsDocumentWriter(q); 
    xpsdw.Write(viewer.Document); 

... która została pozyskana z tutaj: Print FixedDocument programmatically I istnieje więcej artykułów o dopracowanie procesu, jeśli jest to część wymagań.Zauważ, że plik XPS jest w rzeczywistości plikiem "zip" podszywającym się pod plik "xps", więc możesz go rozpakować, zmieniając rozszerzenie, aby sprawdzić, czy zawartość jest w jakikolwiek sposób użyta.

wtórnie Testowałem zapisywania okno z adorner na TextBox z tym kodem ...

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

... z dobrymi wynikami. Tj. Adorner pojawił się w zapisanej mapie bitowej z czerwoną obwódką. Może to różnić się od twojego kodu, ponieważ używam enkodera Png (ale zapisałem go do pliku "jpg").

Mimo że pomyślnie przetestowałem oba podejścia, musisz sprawdzić je na swoim sprzęcie.

I wreszcie, jako ośrodek ostatniej szansy, można wyłączyć tryb renderowania sprzętu WPF i ustawić go do renderowania programowego ...

RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; 

..., dla których nie jest to miłe SO wątku tutaj: Software rendering mode - WPF

+2

+1 za wyświetlenie sposobu renderowania i zapisywania bitmapy. Problem z adoratorami polega na tym, że są umieszczani na warstwie pierwszego planu, a nie w wizualnym drzewie elementu, który zdobią. Oznacza to, że pojawią się, gdy wyrenderujesz całe okno, ale nie podczas renderowania pojedynczego ozdobnego elementu - tak więc przechwytywanie pojedynczego elementu można uzyskać poprzez przycięcie całej bitmapy. – nmclean

0

W moim przypadku wszystko, co potrzebne było nazwać AdornerLayer klasa tak:

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

Działa to, jeśli chcesz dołączyć WSZYSTKICH adoratorów do swojego płótna.

Powiązane problemy