I understand why you tried WM_PAINT to fix your problem. However WM_PAINT really has nothing to do with WPF rendering at all.
Basically, here is how WPF does its rendering when a Window (or HwndSource, ElementHost, ...) is first shown on the screen:
- A Window object has its Visibility set to "Visible"
- A Win32 window (hWnd) is allocated and shown, but its WM_PAINT is ignored
- A Direct3D Surface is allocated to cover the Win32 window's area
- A Dispatcher callback is scheduled at DispatcherPriority.Render
- When the Dispatcher callback fires, the window's visual tree is measured and arranged
- During the arrange pass every control generates drawing instructions to draw itself and stores these in a DrawingContext
- At the end of the arrange pass WPF reads the DrawingContext data and uses Direct3D to update the screen
From then on, any time a property change is made that affects the display, relevant portions of steps 4-7 are repeated in an extremely efficient incremental fashion, updating only what needs to be updated.
The key thing to note here is that any time there is rendering work to be done, a Dispatcher callback is scheduled to do it. This means that your UI will continue to render as long as:
- The Dispatcher is processing its queue, and
- You aren't continuously adding dispatcher operations at or above Render priority
In addition, processing of input events happens at Input priority, so you need to at least allow the Dispatcher to execute down to Input priority if you want mouse over to work on buttons.
Because of the way WPF works, sending WM_PAINT doesn't do anything at all. What you want to do is to give the Dispatcher a chance to process its queue. Generally this is done by calling Dispatcher.Invoke from the UI thread and passing a priority lower than the events you want to flush, for example:
Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, new Action(() => {}));
Notice that the action does nothing at all. But the call to Dispatcher.Invoke cannot return until the empty action executes, and the empty action cannot execute until all higher priority actions execute. So the above code has the effect of forcing the rendering to happen synchronously, similar to what would happen when doing SendMessage(WM_PAINT, ...) in Win32.
One way do diagnose the problem is to set a DispatcherTimer to periodically write to the console. By running this timer at various priority levels you can determine what is holding up the Dispatcher queue. If nothing is running at any level it is probably something external locking up the UI thread, in which case breaking the process and checking the stack may help. A profiling tool is even better for this. In each case, determine whether the process is executing code or waiting for a lock. If it is executing code, figure out why that code never returns to give the Dispatcher a chance to run. If it is waiting for a lock, figure out who is holding that lock.