Determine what caused LayoutUpdate/ArrangeOverride to occur? - wpf

I have a custom Panel which upon resizing has its LayoutUpdated event and ArrangeOverride called twice.
So initially MeasureOverride and ArrangeOverride do their thing once upon open of the client window. Then, if I maximize the window each are called twice. The Available and Final Sizes respectively are not different between each iteration so I'm not sure what's initiating this.
Is there a way to determine the cause of the Invalidation?

I think that it is called twice because the Height and Width changes. I think that both of those properties affect Measure and thus there is a layout pass for each one.

Is there a way to determine the cause of the Invalidation?
Invalidation usually is caused by a change of a DependencyProperty which among it's FrameworkPropertyMetadataOptions has flags AffectsMeasure/AffectsOverride.
As Pavel already said - it's likely that invalidation fires for both changes in Width and Height.
Anyway, you shouldn't rely on the number of those invalidations.

I would say the easiest way is setting a breakpoint for each method and observing the call stack. You will be able to see what happened before your method was called.

Related

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

MeasureOverride not always called on children's property changes

I wrote a panel similar to the built-in StackPanel. I works almost fine except for a slight problem:
Changing layout properties on children do not always cause the panel's MeasureOverride and ArrangeOverride to be called. They are always called when a child's Visibility property changes, but not when the Width and Height properties change.
I haven't yet managed to reproduce this behavior in a sample small enough to be appropriate for being included in a question on StackOverflow: But since it works fine in the trivial sample I made, I know I must do something avoidable in my actual panel.
So my question: In which circumstances does an element not invalidate its parents measure when changing size-related properties?
I tag this wpf also (I used Silverlight) to have a broader audience - I suspect this will apply to both xaml implementations equally.
I figured what my mistake was and under which condition the panel's MeasureOverride is no longer called on certain changes for size-related properties.
My panel called Measure on children with the exact size the children should have, rather on the size of the panel.
A panel doesn't get it's MeasureOverride method called when children begin to desire more space than was told to them is available in the last Measure call - which makes sense.
Summary: The parameter for the Measure method you call on a child must denote the space the parent panel allots to all children, not just the one Measure is called on.
You must make sure you call the base methods MeasureOverride.
protected override Size MeasureOverride(Size availableSize)
{
// you must call this
var throwaway = base.MeasureOverride(availableSize);
// your code here
return yourNewSize;
}

Constrained Window render error

Basically we have two problems and they may depend so Here is the link to the second question:
We tried the constrained border layout example from the API with the difference of setting autoShow: true which ends in a broken window rendered to the top left of the document.
How can this be fixed and where is the error?
I guess you are facing the problem that the constraining container didn't finished the layout yet so that the window failed to layout itself by using the constrain target. I recommend you to call setVisible(true) on the window by using the afterFirstLayout method. The method is documented as private but based on the usage within the framework itself it should better be promoted as protected template, so you should be save using it.
I guess the afterRender wouldn't be enough cause the layout hadn't be processed yet. And the afterLayout template method would run more then once.
You may give it a try.

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?
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().

Event that fires after element is resized

Here is a problem: after loading some visual elements, I need to change something knowing their new sizes. There is MeasureOverride method, but it is called before changing the size. Is there any method that is called after it?
P.S. I know that I can calculate new sizes having old ones, but new sizes aren't calculated simply. It would be much easier to just use such an event (if it exists).
Are you looking for the FrameworkElement.SizeChanged event?
MSDN's description:
Occurs when either the ActualHeight or the ActualWidth properties change value on a FrameworkElement.
EDIT:
This article has a good description of both the SizeChanged and LayoutUpdated events, including an overview of how the layout loop works.

Resources