Homam 85

Isnt it possible to create Visual Brush or RenderTargetFrameBitmap from a Frame control I remember it was easy to create snap shots of WebBrowser control in .Net Framework 2 and need the same feature in WPF.

Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

Chris Crosetto - MSFT

Frame uses the same Win32 component to render web content as IE. This means the Frame contains an HwndHost displaying Win32 content that is invisible to VisualBrushes, RenderTargetBitmap, Render transforms, opacity and other cool WPF features.

It sounds like using a WindowsFormsHost (an HwndHost that hosts Forms) might work for you. Try hosting the WebBrowser control in the WindowsFormsHost then use the same technique you mention above for taking a snapshot. - Don't forget to reference WindowsFormsIntegration.dll.





Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

JasonEK

I'm trying to do something similar and still getting invisible WebBrowsers.

We have a WPF application which contains numerous children including some WebBrowser controls. To print what appears on the screen, I'm using RenderTargetBitmap to create an image of the screen which I then can use for preview or printing. However the WebBrowser controls show up empty. The WebBrowser controls are hosted inside WindowsFormsHost and WindowsFormIntegration.dll is referenced.





Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

Kev__msdn

Hi,

I was having similar problems (anything that used a WindowsFormsHost). I found a workaround by sending it to an xps document and then getting its visual. I think this works because it goes via the printing framework which seems to handle this kind of thing much better.

I can post the code if you are still interested.




Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

JasonEK

Kev__msdn wrote:
Hi,

I was having similar problems (anything that used a WindowsFormsHost). I found a workaround by sending it to an xps document and then getting its visual. I think this works because it goes via the printing framework which seems to handle this kind of thing much better.

I can post the code if you are still interested.

Yes I am definitely still interested. Seeing the code would be greatly appreciated.





Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

Kev__msdn

Here you go. Its not the simplest method in the world but it does work!

Code Snippet

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Xps;
using System.Windows.Xps.Packaging;

namespace Examples
{
class CaptureControlExample
{

// Captures any control to an image..even a WindowsFormsHost :)

public static Image Capture(UIElement control)
{
Image image = new Image();
image.Source = MakeImageSource(control);

return image;
}

private static BitmapSource MakeImageSource(UIElement control)
{
// The control we have been passed is quite possibly the child of
// another element and we have no idea of what its left/top properties
// may be. So the first thing we have to do is take a visual copy of it.
// This will ensure we have a relative 0,0 origin.
Rectangle rect = PaintControl(control);

// Print the control using Xps printing. This is the only fool-proof way
// to get both standard WPF controls and WindowsFormsHost wrapped Win32
// controls to render correctly.
Visual capture = PrintToVisual("out.xps", rect);

// We want to render the resulting visual into a RenderTarget
// so that we can create an image from it.
RenderTargetBitmap renderTarget =
RenderVisual(capture, control.RenderSize.Height, control.RenderSize.Width);
#if DEBUG
// Debug image output
System.IO.FileStream stream = new System.IO.FileStream("out.png",
System.IO.FileMode.Create);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderTarget));
encoder.Save(stream);
stream.Close();
#endif
// Walla! The most complicated method of creating an image
// from a control you've ever seen!
return renderTarget;
}

/// <summary>
/// Paints a control onto a rectangle. Gets around problems where
/// the control maybe a child of another element or have a funny
/// offset.
/// </summary>
/// <param name="control"></param>
/// <returns></returns>
private static Rectangle PaintControl(UIElement control)
{
VisualBrush vb = new VisualBrush(control);
vb.TileMode = TileMode.None;
vb.Stretch = Stretch.Uniform;

// Fill a rectangle with the illustration.
Rectangle rect = new Rectangle();
rect.Fill = vb;
rect.Width = (control.RenderSize.Width);
rect.Height = (control.RenderSize.Height);

// Force the rectangle to re-size
Size szRect = new Size(rect.Width, rect.Height);
rect.Measure(szRect);
rect.Arrange(new Rect(szRect));
rect.UpdateLayout();
return rect;
}

/// <summary>
/// Prints any UIElement to an xps document and gets the resulting Visual.
/// This is the only full proof way to copy the contents of a UIElement into
/// a visual. Other methods may work well...but not with WindowsFormsHosts.
/// </summary>
/// <param name="xpsFilePath"></param>
/// <param name="element"></param>
/// <returns></returns>
private static Visual PrintToVisual(string xpsFilePath, UIElement element)
{
// Write the element to an XpsDocument
System.IO.File.Delete(xpsFilePath);
XpsDocument doc = new XpsDocument(xpsFilePath, System.IO.FileAccess.ReadWrite);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(element);

// Get the visual that was 'printed'
Visual capture = doc.GetFixedDocumentSequence().DocumentPaginator.GetPage(0).Visual;
doc.Close();

// Got it.
return capture;
}

/// <summary>
/// Render a Visual to a render target of a fixed size. The visual is
/// scaled uniformly to fit inside the specified size.
/// </summary>
/// <param name="visual"></param>
/// <param name="height"></param>
/// <param name="width"></param>
/// <returns></returns>
private static RenderTargetBitmap RenderVisual(Visual visual,
double height, double width)
{
// Default dpi settings
double dpiX = 96;
double dpiY = 96;

// We can only render UIElements...ContentPrensenter to
// the rescue!
ContentPresenter presenter = new ContentPresenter();
presenter.Content = visual;

// Ensure the final visual is of the known size by creating a viewbox
// and adding the visual as its child.
Viewbox viewbox = new Viewbox();
viewbox.MaxWidth = width;
viewbox.MaxHeight = height;
viewbox.Stretch = Stretch.Uniform;
viewbox.Child = presenter;

// Force the viewbox to re-size otherwise we wont see anything.
Size sFinal = new Size(viewbox.MaxWidth, viewbox.MaxHeight);
viewbox.Measure(sFinal);
viewbox.Arrange(new Rect(sFinal));
viewbox.UpdateLayout();

// Render the final visual to a render target
RenderTargetBitmap renderTarget = new RenderTargetBitmap(
(int)width, (int)height, dpiX, dpiY, PixelFormats.Pbgra32);
renderTarget.Render(viewbox);

// Return the render taget with the visual rendered on it.
return renderTarget;
}
}
}






Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

JasonEK

I'm about to try and get this all working but is there a way to do it with out the temporary file (out.xps)




Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

Kev__msdn

Not that i've found/used. Maybe you could use a MemoryStream If you do manage to find a way, let me know!




Re: Windows Presentation Foundation (WPF) Visual Brush from Frame Control

JasonEK

well it didn't seem to be working at first even though the files created did have the correct stuff. I did get it working only using your PrintToVisual function so still haven't gotten rid of the file part but was able to minimize it some. The visual returned from my function is sent to the print functions. I wish there was some way to get rid of the file stuff though. Gonna change your PrinttoVisual function to use Path.GetTempFileName(); Some of my code already matched yours but I had already gotten it to render bitmaps using the printer's DPI.

thanks for all the help btw

-Jason

Code Snippet

static private Visual CreateImageFromUiElementForPrinting(PrintDialog printDialog, PrintPageSettings pageSettings, UIElement uiElement)

{

if (printDialog == null)

{

throw new ArgumentNullException("printDialog");

}

if (uiElement == null)

{

throw new ArgumentNullException("uiElement");

}

if (pageSettings == null)

{

throw new ArgumentNullException("pageSettings");

}

//Create Bitmap of screen image

int dpiX = printDialog.PrintTicket.PageResolution.X.GetValueOrDefault(96);

int dpiY = printDialog.PrintTicket.PageResolution.Y.GetValueOrDefault(96);

int elementWidth = (int)uiElement.DesiredSize.Width * dpiX / 96;

int elementHeight = (int)uiElement.DesiredSize.Height * dpiY / 96;

RenderTargetBitmap renderTargetBitmap

= new RenderTargetBitmap(elementWidth, elementHeight, dpiX, dpiY, PixelFormats.Pbgra32);

//my old code

//renderTargetBitmap.Render(uiElement);

//////////////

//new code

Visual capture = PrintToVisual("out.xps", uiElement);

renderTargetBitmap.Render(capture);

//////////////

//Set up image to print and return it

Image printImage = new Image();

printImage.Source = renderTargetBitmap;

double innerWidth = printDialog.PrintableAreaWidth - pageSettings.MarginLeft - pageSettings.MarginRight;

double innerHeight = printDialog.PrintableAreaHeight - pageSettings.MarginTop - pageSettings.MarginBottom;

printImage.Measure(new Size(innerWidth, innerHeight));

Size imageSize = printImage.DesiredSize;

Point imagePoint = new Point(((innerWidth - imageSize.Width) / 2) + pageSettings.MarginLeft,

((innerHeight - imageSize.Height) / 2) + pageSettings.MarginTop);

printImage.Arrange(new Rect(imagePoint, imageSize));

return printImage;

}