Is there documentation on the paint cycle in WinForms?
When i am programming in Windows the paint cycle is usually of the form:
sent a WM_PAINT message
{
call BeginPaint(&paintStruct)
//BeginPaint sends WM_NCPAINT and WM_ERASEBKGND
sent a WM_ERASEBKGND message
{
i can:
- allow default processing (Windows will fill the area with the default background color (e.g. white)
- erase and background myself (e.g. a gradient) and prevent default processing
- do nothing (letting whatever was there before me stay there) and prevent default processing
}
perform whatever painting i desire on
paintStruct.hDC (Device Context)
paintStruct.rcPaint (Invalid Rectangle)
that was populated into paintStruct during BeginPaint
call EndPaint()
}
This is all documented on MSDN: Windows Development\Graphics and Multimedia\Windows GDI\Painting and Drawing\About Painting and Drawing
i cannot find any such documentation about WinForms and its paint cycle. i can randomly find methods and events that have the name paint in them:
OnPaint (protected method "Raises the Paint event.")
OnPrint (protected method "Raises the Paint event.")
InvokePaint (protected method "Raises the Paint event for the specified control.")
Paint (public event)
InvokePaintBackground (protected method "Raises the PaintBackground event for the specified control.")
OnPaintBackground (protected method "Paints the background of the control.")
Note: Ignoring the fact that there is no PaintBackground event
Is there documentation describing the design relationship between these entities? Is there documentation on the paint cycle in WinForms?
It isn't substantially different from the native Windows paint cycle, the .NET events are raised by the corresponding Windows messages. Starting from the bottom, the messages are generated by a call to InvalidateRect(), either by the window manager or by the app itself. The .NET version is Control.Invalidate(). Windows keeps track of the update region for the window, deciding whether to deliver a WM_PAINT, WM_NCPAINT and WM_ERASEBKGND message.
The WM_PAINT and WM_ERASEBKGND messages are recognized by Control.WndProc() when the ControlStyles.UserPaint style is turned on. It calls the virtual OnPaint() and OnPaintBackground() methods. A derived control can override these methods to customize the painting as necessary. And must call the base method. Eventually that reaches the Control.OnPaint/Background method, that fires the Paint and PaintBackground events to allow other code to customize the painting.
The only other wrinkle is double-buffering, enabled by the DoubleBuffered property. Winforms creates a bitmap buffer for the control and runs OnPaintBackground() and OnPaint(), passing a Graphics object created from that bitmap. Then blits the bitmap to the screen.
Is this what you are looking for?
MSDN: Custom Control Painting and Rendering
OP Edit: For when Microsoft implements their next round of link breaking, the documentation's location is:
MSDN Library
Development Tools and Languages
Visual Studio 2010
Visual Studio
Creating Windows-Based Applications
Windows Forms
Getting Started with Windows Forms
Windows Forms Controls
Development Custom Windows Forms Controls with the .NET Framework
Custom Control Painting and Rendering
Related
I am trying to create a subclass of VScroll control and intercepting the WM_VSCROLL message to do some stuff. On msdn, and countless articles/forums, WM_VSCROLL is supposed to be 0x115, even in the windows header file that's what it is. However waiting on 0x115 doesn't bring anything. I realized it's coming through as 0x2115 instead, even this article is using it as 0x2115. Any reason/explanation as to why it's coming as 0x2115 instead? Is it because it's a WinForms control?
Thanks
Yes, this is pretty standard in Windows GUI class libraries, Winforms is no exception. The native Windows control, ScrollBar in your case, send notifications to their parent. After all, they were designed to make their parent act on the notification, not themselves. Or in other words, you expect the parent window to scroll.
That however is not very compatible with the notion of a control class whose behavior you can modify by overriding its message handling and generation. Or for that matter the idea of events in .NET, anybody can subscribe to the Scroll event, not just the parent.
That buys a lot of goodness, but something must be done to get the WM_VSCROLL message from the parent back to the control. Which is what the Winforms plumbing takes care of. It sends the message back but alters the message number to indicate that it was a reflected message, not the original. It adds 0x2000. The value of the (fake) WM_REFLECT message in the Winforms source code.
Due to an unsolved issue in my current project [ Weird scrollbar UI in hosted WPF composite control ], I've had to defer to workarounds. One such idea I came up with was to keep the ScrollViewer in question in sync with a Windows Forms ScrollBar (horizontal and vertical). How would I go about doing that?
In essence, I want the WinForms scrollbar(s) to work like the ScrollViewers (in terms of dragging and events suchlike).
I think there's no any other way than send/listen to the lo-level API Windows messaging. You should have a decent knowledge of the Win APIs, and how to manage them. I'd add also that the scrollers are mid-complexity task to manage, but maybe it's me...
Here is the vertical scroll message reference.
I managed to implement it without having to much around with the WinAPI. Here's how I did it:
Registered a ScrollOffsetChanged event handler with the scrollviewer (the composite control that implemented the IScrollInfo interface exposed the event).
Registered a ValueChanged event handler with the windows forms scrollbar.
A couple of signal flags to prevent cyclic calls to the event handler.
The first handler called an UpdateExternalScrollBars() function that calculated the Maximium Scrollbar property using the following expression: ScrollViewer->ExtentHeight/Width - ScrollViewer->ViewportHeight/Width. The Value property was directly set to ScrollViewer->Vertical/HorizontalOffset.
The second fetched the Value property from the windows forms scrollbar and passed it to ScrolViewer->ScrollToVertial/HorizontalOffset(). The signal flags were appropriately set beforehand.
I've just tracked down a problem, where one one of my UserControls mysteriously stopped firing Paint events.
Turns out the BorderStyle had been accidently set to None. When I set it back to FixedSingle, the Paint events started firing again.
This surprised the heck out of me - any idea why this is happening?
edit
The UserControl is based on a 3rd party control (ASE ChartDirector), and we had another problem with it. When it's BackColor was set to Transparent, it would fire Paint events continuously!. The workaround in that case was to set BackColor to ControlLightLight.
(see this question for more details)
Could that also be a clue to the BorderStyle problem?
Here is a very nice blog post discussing in very detail the reason for that.
Update: This article is for WPF however, both WPF and Windows Forms touch HWND, WParam, LParam at some point.
Here is why I think that the paint events might not get processed (Text below is as it appears on the article).
The operating system must respond very quickly to mouse movement.
Windows uses a dedicated Raw Input Thread (RIT), running in the
kernel, to handle the signals from the mouse hardware. The RIT
quickly scans over the HWND hierarchy to see which window the mouse is
over. Because the system must be responsive, no application code is
invoked. For normal windows, the RIT checks the mouse position
against the window's rectangle (or region, if one is set). But for
layered windows, the RIT looks in the bitmap that specifies the
content for the window and checks the effective transparency at that
location (which can be affected by the constant opacity setting, the
color key setting, or the per-pixel alpha channel). If the pixel is
100% transparent, the RIT skips the window and keeps looking. Once a
window has been located, the mouse move flag is set on the thread that
owns the window. This will cause the thread to receive a WM_MOUSEMOVE
message the next time it calls GetMessage() and there are no other
higher-priority messages.
While this is particularly for messages coming from mouse hardware signals, I though that something similar could happen with other events too.
Here is what I would do,
Inside the Form class have the following code execute in order to watch the messages that are processed.
protected override void WndProc(ref Message m)
{
Trace.WriteLine(m.Msg + ":" + m);
base.WndProc(ref m);
}
I searched but could not get more information around.
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
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