ratishphilip / CompositionProToolkit

Collection of Helper classes and controls (using Win2d) for Windows.UI.Composition
MIT License
235 stars 29 forks source link

When resizing FluidWrapPanel: InvalidOperationException: Measure Pass #15

Closed jasonwurzel closed 6 years ago

jasonwurzel commented 6 years ago

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

ratishphilip commented 6 years ago

Hi Michael,

Thanks for reporting this issue. Let me have a look. I shall get back to you.

Regards, Ratish

ratishphilip commented 6 years ago

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.

jasonwurzel commented 6 years ago

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!

jasonwurzel commented 6 years ago

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:

ratishphilip commented 6 years ago

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.

ratishphilip commented 6 years ago

@jasonwurzel I have updated the TestFWPanel example in GitHub to fix the initial distribuition issue.

You can find the updated code here.

ratishphilip commented 6 years ago

Closing this issue.