OnRender not called after InvalidateVisual() - wpf

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?

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.

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

I just had this problem, too.
I had a scrollbar for a control which only figured out during OnRender() how much space is really needed to display all content, which could be bigger than the available display space and therefor needed a scrollbar. It could happen that OnRender() called some methods which ultimately changed the value of the scrollbar which was supposed to start OnRender() with InvalidateVisual().
However, OnRender() did not get called again after InvalidateVisual(). I guess the reason is that InvalidateVisual() sets some flags which tells WPF that the control needs to get drawn again, but once OnRender() finishes, that flag gets reset. Here some pseudo code how I expect it to happen:
//someCode:
control.InvalidateVisual()
//code of InvalidateVisual()
control.RedrawFlag = true;
//WPF some time later:
if (control.RedrawFlag){
control.OnRender()
//OnRender code
//do some stuff
//decide control needs to be redrawn
//however, RedrawFlag is alreday true!
//next line is not changing anything
control.RedrawFlag = true;
//WPF finished executing control.OnRender
control.RedrawFlag = false;
}
I didn't further investigate if WPF really works this way, but it would explain why OnRender() does not get called a second time.
Instead of wasting even more time, I changed how to calculate the total width of the control content can be and put this code outside of OnRender().

Related

How to display render status in a WPF form?

I'm overriding WPF's OnRender to draw complex graphics. This may rarely take a long time. I would like to indicate to the user that the app did not crash, but is "merely" taking a long time to render.
How would I do that? It seems not possible to modify the UI in any way during the OnRender call.
You are talking about placing a "Busy Indicator" on top of your drawing.
Just place a half transparent grid on top with the writing "Loading..."
and you're done.
Bind its visibility to some bool that you update once when entering the complex rendering, and once when finished.
Or put a spinning Ellipse with gradient background if you want to see motion..
Since the OnRender method will execute on the UI thread you cannot do anything else such as displaying an interactive ProgressBar or respond to user input during the execution of your code.
What you can do is to pop up a new window that launches in a separate thread and display this one right before you begin to draw your complex graphics.
Please refer to my answer in the following similar question for more information about this.
Wait screen during rendering UIElement in WPF
Once the OnRender method has returned you then close the temporary "loading" window:
Can I stop WPF from executing UI actions out of the order in my code

LayoutUpdated event has stopped firing. Handlers are in place, any ideas?

I have a control which allows the user to add and manipulate n graphical and text layers. The layers are each usercontrols that are added to the parent control's canvas. I use the parent control's LayoutUpdated event to refresh transform specific variables and other stuff. With no change to the handler method, the LayoutUpdate event stopped firing. I've obviously done something that caused it, but I haven't been tinkering with anything that can logically tie to this new problem. Any ideas are appreciated - thanks.
JUST TO CLARIFY: I'm not asking for a solution - just an anecdote of experience with something similar - That will probably be more helpful than you can imagine. Thanks
The cause, in this case, was a WPF UserControl (kid) defined in xaml at design time inside a canvas without any dependency property values set. The canvas is inside another usercontrol of different type (mom). At runtime, a mom is instantiated and her kid is sized, positioned, and made visible within the canvas of mom based on the runtime size of mom as well as calling arguments that denote whether or not kid is to be visible. This instance of kid is only sized and positioned if it is requested to be visible, if not, it is only hidden (not sized, not positioned).
The presence of this instantiated, but unsized and unpositioned element within the canvas of mom was causing the failure of layoutupdated events to fire. This situation effected not only the instance in question, but all other instances created at runtime in the project afterward. (NOTE - no exception was raised)
The solution appropriate for this circumstance was to explicitly size and place this element within the canvas - even if it is not needed. The layoutupdated events then fire as expected. I would love to provide a more profound and generalized answer, but perhaps wiser people can include their insight as well. Thanks

Access WPF DataGrid after making element visible

Currently I am stuck with a problem that is simple on the first sight. Its about automated GUI testing.
I want to make a row/cell of a WPF DatGrid completely visible by scrolling using ScrollIntoView(row) and then accessing the row/cell directly after. Unfortunately scrolling in ScrollViewer seems to happen asynchronously. This means I need to wait for the scrolling to finish before accessing the row/cell. For this purpose I found the ScrollChanged event I can subscribe.
There is only one detail I can not solve: If the row/cell I want to access is already visible (and no scrolling is necessary) I do not get that event and the algorithm gets stuck. I was not able to find a reliable way to predict if a call to ScrollIntoView(row) actually scrolls.
Any idea how to solve this?
To make sure layout is updated call UIElement.UpdateLayout after you ScrollIntoView and before you want to use item. Quoting MSDN it
Ensures that all visual child elements of this element are properly updated for layout.

Painting in a custom control and the invalidate mechanism

A custom control I am creating needs to draw many "items" in its client space. A call to Invalidate() would trigger a new paint cycle wherein all items would be redrawn.
Now when there are many items and a lot of navigation happens within the control, things need to be optimized; so I need to trigger a paint cycle where only one or two items are drawn. I store references to these items so that the paint method (OnPaint) knows it's a "quicky".
The difficulty is that when OnPaint is executed, it is hard to know if other Invalidate() calls have been made in the meantime. In that case it should do a "normal", complete paint.
I do make use of the clip rectangle. Of course I could check if the clip rectangle in OnPaint has become the whole of the client rectangle, a sign that Invalidate() was called, but this is not 100% safe. I thought of other similar solutions but they seem hacky.
What is the way this problem is usually, or best, solved?
The solution here would be to employ a double buffering approach with the BufferedGraphics class. This way you won't have so much tricky stuff going on in your OnPaint and you'll be able to paint whenever, whatever.
MSDN: Double Buffered Graphics (under "Manually Managing Buffered Graphics")
Here's a useful example:
Custom Drawing Controls in C# – Manual Double Buffering

Silverlight: Is there an event that fires on a FrameworkElement before it is rendered?

In our Silverlight 2 project we have created an attached property to perform on-the-fly translation to text properties of various user controls. To achieve this, we hook the Loaded event of the FrameworkElement when the property is set. When the event fires, we take the existing text property value and perform some simple string substitutions on it, before replacing the property value with the translated text. However, this results in the control being rendered with the untranslated text, then the text is quickly replaced with the translated version.
Is there an alternate event we can hook that would fire before the control is rendered?
I've changed my code so that it now performs the translation as soon as the setter for the attached property is called. There's no need to wait for the FrameworkElement to have finished loading, as I can change the Text property long before the element is rendered.
My initial thoughts on using the Loaded event were to reduce the startup time of the application by only translating the controls that were visible on the screen. As it turns out, I'm duplicating some of the work performed by the runtime, as the runtime won't call the property setter until it needs to anyway.
I'm not totally sure about this, but can you use the LayoutUpdated event. It will fire when the control is resized and such (you could take measures to ensure your code only executes once.)
I know it doesn't seem like the "right" event for this but unfortunately Silverlight kinda leaves you standing there holding it when it comes to events.

Resources