From GDI to WPF: rendering - wpf

In GDI you just use System.Drawing.Graphics to manually handle the rendering.
In WPF is DrawingContext the way to go?
https://msdn.microsoft.com/en-us/library/system.windows.media.drawingcontext(v=vs.110).aspx

You could indeed override the OnRender method of a UIElement to define your own rendering instructions but note that this is not an immediate mode rendering API like Windows Forms's OnPaint. In fact there is no such API available in WPF.
The drawing operations of the DrawingContext are not used directly when the OnRender method is invoked. They are instead processed by the rendering thread at a later stage.
So depending on what you are trying to do, overriding the OnRender method may not be your best choice after all. You may want to stick to the "WPF way" of creating composite UI elements.

Related

Using SnapsToDevicePixels in OnRender

I have a class derived from FrameworkElement in which I override the OnRender method to draw several lines. I would like the behaviour of SnapsToDevicePixels to work with my primitive control, what is the best way to go about doing this?.
I am aware of GuidelineSets and how to use them, but looking at some examples of elements over at .NET Reference Source they were not used and I saw nothing that takes into account SnapsToDevicePixels in the OnRender overrides. Only a few portions of UIElement does anything with it in the Arrange method, yet the property works on Rectangles.
I feel as though checking the SnapsToDevicePixels property is true and then pushing a GuidelineSet onto the drawingContext would be the naïve approach and I had thought (hoped) there was a better way to incorporate it.
Is this the case? If so, How should I design my OnRender methods to utilise SnapsToDevicePixels?

Strange Rendering Performance

I want to do some rendering in WPF so that I found the hints in MSDN which suggest to use the light-weight DrawingVisual to make a rendering object (e.g. Triangle)
Whenever a property (e.g. color, coordinates) of the rendering is changed, it will render again.
(e.g. RenderOpen(); drawingContext.Draw(.......) ........)
Then I replace the rendering object with UIElement instead of DrawingVisual for the base class with other the same.
And I found that I only render one time no matter how I change the property. Still, I call InvalidateVisual() whenever a property is changed.
Eventually, I found that the rendering performance is much better than DrawingVisual.
Is it the truth?

Layout System in WPF

I want to trigger the Measure/Arrange layout pass of an ItemsControl manually in code behind without user interaction. Is it possible? If yes, How? I tried InvalidateMeasure(), UpdateLayout(), but no use.
Use the InvalidateVisual method to invoke a re-render:
Invalidates the rendering of the element, and forces a complete new
layout pass. OnRender is called after the layout cycle is completed.
However, as Microsoft recommends that this should scarcely be used manually in applications, there is likely another solution to your problem (such as proper use of dependency properties, for instance), if we knew it.

WPF as IHTMLPainter control

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
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:
Create a System.Drawing.Bitmap corresponding to rcUpdate
BitBlt from the given DC into the System.Drawing.Bitmap
Construct an ImageSoure from the System.Drawing.Bitmap (see recent SO answers for details)
Construct an ImageBrush from the BitmapSource using a viewport/viewbox that will lay it behind the portion of the visual corresponding to rcUpdate
Set your root visual's background to the ImageBrush
Set the RenderTransform on the root visual so that the rcUpdate portion starts at (0,0)
Render the root visual to a RenderTargetBitmap of rcUpdate size
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.
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.
Add your WPF control at runtime as a member of the UserControl.
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

performance in C# Application

i use some pictures in my 'MainForm' And My Windows Application was writing by c sharp.
i use this form to start other forms in my project.
And I use some label and panel with Transparent Color.
but when the program started i see many blink in transparent label and panel.
And it is very bad.
How I Can Fix this problem?
Enabling DoubleBuffered as stax suggested above is helpful but it may not be sufficient.
In your form, add the following method override:
protected override void OnPaintBackground(PaintEventArgs e) {}
And, in the OnPaint method, paint the background yourself instead. If you don't do this, drawing the background and painting are separate events, and background painting has higher priority, meaning that it will happen earlier.
Furthermore, if you add child controls (like labels), they receive their own paint background/paint events. You may be able to disable the Label's background. If I do stuff like this, I tend to not use controls but paint the text and the images in one OnPaint.
did you test it on multiple machines.
did you use an updated machine with all the .net service packs needed.
etc

Resources