Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OnRender not called after InvalidateVisual()

A custom WPF Control overrides OnRender. The method generates and displays a Path from custom data. The data provider is bound using a Dependency Property. The Dependency Property registers for an event when data changed. This event in turn calls InvalidateVisual().

However, after the InvalidateVisual() the OnRender is not always called.

We use the Prism Framework and the Region functionallity. The Control in question is embedded in such a Region, which is activated and deactivated. However, the Control's property "IsVisible" is true whenever the region is active. But still, when calling InvalidateVisual() the OnRender method is not called...

What could prevent the OnRender method from being called?

like image 216
falstaff Avatar asked Jun 10 '13 10:06

falstaff


2 Answers

I just had this problem, too.

Context

I've got a load of controls based on the DynamicDataDisplay graph components inside a VirtualizingStackPanel (inside a ListBox).

When there are more controls that are visible at once, but not enough for the VirtualizingStackPanel to start re-using them when you scroll then I see this issue with the D3 AxisControl class. For some reason it does a lot of work in it's OnRender method, which it tries to trigger by calling InvalidateVisual when something changes.

In the problem case the problem controls call InvalidateVisual but they never get a call to MeasureOverride, ArrangeOverride or OnRender. Interestingly, most of the controls still work, in one particular problem case I get the last 3 out of a set of 11 failing to work properly. Notably those 3 (and only those 3) receive a call to MeasureOverride immediately before the data binding update that triggers the call to InvalidateVisual.

My Fix

In the end I managed to fix it by adding a call to InvalidateMeasure alongside the call to InvalidateVisual.

It's a horrible solution, but it's not a performance critical part of our application, so I seem to be getting away with it.

like image 144
Swythan Avatar answered Sep 24 '22 04:09

Swythan


If the size of your control is staying the same, you should not be using InvalidateMeasure() or InvalidateVisual() because they trigger an expensive re-layout.

WPF is a retained drawing system. OnRender() might be better called AccumulateDrawingObjects(), because it doesn't actually draw. It accumulates a set of drawing objects which WPF uses to draw your UI whenever it wants. The magic thing is, if you put a DrawingGroup into the DrawingContext during OnRender(), you can actually efficiently update it after OnRender, anytime you like.

See my answer here for more details..

https://stackoverflow.com/a/44426783/519568

like image 36
David Jeske Avatar answered Sep 24 '22 04:09

David Jeske