tags:

views:

188

answers:

2

I need to host WPF control inside IE, therefore I'm trying to implement IHTMLPainter and IElementBehavior interfaces. I'd like to build my custom behavior and use it inside IE, but the problem is how to draw WPF control by just having IntPtr hdc parameter. Probably I can get Drawing.Graphics by the following code:

Graphics.FromHdc(hdc);

But I'm not sure that this is the best way. Please advise

A: 

Rendering behaviors through IHTMLPainter and IElementBehavior are meant to alter or supplement the display of existing elements in a page, not to render content for user controls. If you're looking to use WPF controls in a page, this is not the path to take. Instead, consider creating a blank windowed UserControl with ActiveX support, then do either of the following.

  1. Add your WPF control at runtime as a member of the UserControl.
  2. Perform WPF activities using the window handle (HWND) of the control.

Alternatively, you could just use Silverlight to make user controls. Silverlight has a pretty good subset of WPF display features, and even manually constructed Silverlight content is easier to manage than trying to get .NET Windows + ActiveX Hosting + WPF working.

If I've mistaken your question and you're truly intent on using WPF to perform drawing activities in an element behavior, Graphics.FromHdc() is an acceptable way to get a usable Graphics object. You should attach to the HDC specified in the Draw() callback.

Draw Method (IHTMLPainter) @ MSDN

You could also attach to the window handle (HWND) of the document view (retrieved via IOleWindow), if your WPF activities involve the entire viewport. The window object can be cast to IOleWindow for this purpose (see IHTMLWindow2).

IOleWindow Interface @ MSDN
IHTMLWindow2 Interface @ MSDN

meklarian
+1  A: 

I'm assuming you want to be able to make use of the advanced features of WPF within a MSHTML context. In that case, Graphics.FromHdc(hdc); will not do the trick for you. The resulting Graphics object will have no way to receive WPF content because WPF uses a retained-mode system and its MILCore rendering engine uses Direct3D not GDI+.

I'll give you one sure way to use WPF features inside a IHTMLPainter, plus pointers to another way that would likely be faster if you can get it to work.

Bitmap copying solution

An easy solution is to simply copy the background provided by MSHTML into an ImageBrush, use RenderTargetBitmap to let WPF render to a bitmap, then copy it back to the device.

To do this, construct your WPF content in any Visual subclass with a Background property (eg Grid or Border), then in your IHTMLPainter.Draw() method, just do the following:

  1. Create a System.Drawing.Bitmap corresponding to rcUpdate
  2. BitBlt from the given DC into the System.Drawing.Bitmap
  3. Construct an ImageSoure from the System.Drawing.Bitmap (see recent SO answers for details)
  4. Construct an ImageBrush from the BitmapSource using a viewport/viewbox that will lay it behind the portion of the visual corresponding to rcUpdate
  5. Set your root visual's background to the ImageBrush
  6. Set the RenderTransform on the root visual so that the rcUpdate portion starts at (0,0)
  7. Render the root visual to a RenderTargetBitmap of rcUpdate size
  8. BitBlt the RenderTargetBitmap to the rcUpdate area of the DC

This should work well, be simple to implement, and work for any WPF content including advanced features such as 3D, BitmapEffects, etc. The only disadvantage is that those two bitmap copies might slow things down somewhat.

Note that if you know your WPF Visual is totally opaque you can completely skip steps 1-5 and simply render your Visual to a RenderTargetBitmap and BitBlt it to the device.

Direct3D possibility (partial solution)

Obviously it would be faster to avoid all this bitmap copying during render. This is most likely possible, but I can only give you some ideas to point the way -- it will take a lot of trial and error and probably some undocumented calls to make it work.

Since WPF renders using Direct3D, obviously you would prefer to get a Direct3D surface from MSHTML and paint on it. Doing this requires two things: Getting the surface from MSHTML, and getting MILCore to draw on it.

IHTMLPainter has a flag HTMLPAINTER_3DSURFACE to request a Direct3D surface in its GetPainterInfo call, but I couldn't find any examples of how to use HTMLPAINTER_3DSURFACE. I suspect it could be figured out with a little trial and error.

I did not find any way to get WPF's native component "MILCore" to accept a Direct3D surface to paint on instead of a hWnd. There is no documentation on MILCore, and the only public API for setting up rendering tree, HwndSource, doesn't seem to be able to do the job.

Ray Burns
Thanks for the answer. Btw, do you know the way to put WPF inside IE. I did the same for WinForms, by using IElementBhavior, but this doesn't work for WPF for some reason...
Paul Podlipensky