When resizing FluidWrapPanel: InvalidOperationException: Measure Pass
Closed this issue · 7 comments
Hi,
I'm planning to use your excellent FluidWrapPanel as a Grid with a configurable layout of tiles. I wanted to update to the latest version as until now I was on CompositionProToolkit v. 0.7
Now, when I resize my MainWindow, at a certain size I get the following unhandled exception:
System.InvalidOperationException: Measure Pass: Unable to accommodate child in the panel!
at CompositionProToolkit.Controls.FluidWrapPanel.MeasureOverride(Size availableSize)
Messing around with the set child sizes seems to influence the problem, but it does not really help. Also, sometimes when starting up the App, the children don't get distributed but are stacked at the upper left corner. And when resizing the children sometimes are "fitting" into the grid (e.g. two in a row when using childcontrol.Width = Panel.Width / 2 ) and sometimes they don't fit.
Here's a little repository for reproducing the problems:
https://github.com/jasonwurzel/FluidWrapProblem
Thank you for your help,
Michael
Hi Michael,
Thanks for reporting this issue. Let me have a look. I shall get back to you.
Regards,
Ratish
HI @jasonwurzel,
Sorry for the delay in reply.
Setting the ItemWidth and ItemHeight properties of the FluidWrapPanel after setting the children's width and height will fix the crash issue.
So in your MainPageViewModel class put your code this way
public MainPageViewModel()
{
this.WhenAnyValue(model => model.ViewDimensions, model => model.TilingOptions)
.Subscribe(sizeAndTiling =>
{
var size = sizeAndTiling.Item1;
var tiling = sizeAndTiling.Item2;
int rows = tiling == TilingOptions.Option2X2 ? 2 : 3;
int columns = tiling == TilingOptions.Option2X2 ? 2 : 3;
adjustSingleTileViewModelCount(rows, columns);
var width = Math.Floor(size.Width / columns);
var height = Math.Floor(size.Height / rows);
foreach (var child in GridItems)
{
child.Width = width;
child.Height = height;
}
ItemsWidth = width;
ItemsHeight = height;
});
}
I am still looking into the issue why the items are not arranged properly when during the first time.
BTW, I had done a similar implementation using AdaptiveTriggers. Have a look here.
Hi @ratishphilip,
strange, for me it doesn't fix the problem. Fastest way to reproduce (at least for me) is: start the app, press window maximize => same exception as before.
In the mean time, I will look into your AdaptiveTriggers solution, thanks!
Ok, I just compared the demo project you mentioned with mine (actually, that is what I started with).
When updating your "TestFWPanel" to the newest CompositionProToolkit, it has the "initial distribution" problem as well.
But: It does not crash on maximize. The biggest differences between your sample and mine seem to be:
- I'm using the FluidWrapPanel as the ItemsPanel of an ItemsControl
- You're adding a list of "FluidItemsControl"s directly as the panel.ItemsSource, where as I use a ViewModel approach, where I set a RxUI ViewModelViewHost as the DataTemplate that resolves the view after it knows its ViewModel. Maybe this extra indirection makes it so some values are available too late/or the old, wrong values are available for the FluidWrapPanel when it tries to Measure.
I'm experimenting a bit into this direction, maybe I see something interesting...
Upon further investigation, I found out the issue. The ItemsWidth and ItemsHeight values which are being set in the MainPageViewModel are not being propogated to the FluidWrapPanel (maybe an issue with the ReactiveAttribute).
So the default ItemWidth and ItemHeight of the FluidWrapPanel are set to 10 which is a relatively low number (I will change it to 100 in the next release). FluidWrapPanel allows children whose size are multiples of the ItemWidth * ItemHeight and the maximum multiplication factor is 60.
With the ItemWidth and ItemHeight being at a default value of 10, when the window is maximized, the calculated size of the child is more than 700. So the child is 70 times the ItemSize which is not allowed.
That is why an exception is being raised.
So in the MainPage.xaml if the change the code to
<ItemsControl x:Name="GridView" Grid.Row="1" Margin="50">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:FluidWrapPanel ItemHeight="{Binding ItemsHeight, FallbackValue=200}" ItemWidth="{Binding ItemsWidth, FallbackValue=200}" IsComposing="{Binding IsComposing}"
Orientation="{Binding Orientation}" Background="Violet" FluidAnimationDuration="0:0:0.2" DragOpacity="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerTransitions>
<TransitionCollection />
</ItemsControl.ItemContainerTransitions>
<ItemsControl.ItemTemplate>
<DataTemplate>
<reactiveUi:ViewModelViewHost x:Name="ViewModelViewHost" ViewModel="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="45"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
By putting a FallbackValue to the ItemWidth and ItemHeight, the crash is solved. But the newly calculated ItemsWidth and ItemsHeight properties are not propogated to the FluidWrapPanel. Thus it is trying to arrange with the old values.
Another thing if you notice in the above code I have added the following snippet
<ItemsControl.ItemContainerTransitions>
<TransitionCollection />
</ItemsControl.ItemContainerTransitions>
You need to add this to an ItemsControl (and similarly to a GridView) if your ItemsPanel is set to a FluidWrapPanel and you want the "initial distribution" animation to work.
TL;DR - Try using INotifyPropertyChanged instead of the Reactive Attribute and add the above snippet to your code, it should work without any issues.
@jasonwurzel
I have updated the TestFWPanel example in GitHub to fix the initial distribuition issue.
You can find the updated code here.
Closing this issue.