- 
                Notifications
    You must be signed in to change notification settings 
- Fork 117
Fix WrapPanel stretching behavior for last item #704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix WrapPanel stretching behavior for last item #704
Conversation
Added a conditional check to prevent the last item in the WrapPanel from stretching when the parent measure's width is infinite.
| Thanks Andrew, I think a screenshot would have helped me understand the context too. If I understand correctly, in the case where this is happening, all the items are going to be laid out on one row, as it'll never wrap due to the infinite available width, right? Therefore, yes it makes sense that the last item should just be whatever size it wants vs. trying to fill that void. Is there a case where that wouldn't be true? Even if in the measure pass it's infinite and doesn't stretch, if the arrange pass is fixed, then it should self-correct and still stretch when laying out, right? It could be good to add a test here too just to catch this case so we can ensure we don't regress if we modify this logic in the future. Thanks for taking a look into this! 🦙❤️ | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a WrapPanel stretching behavior issue where the last item would attempt to stretch to infinite width when the parent container provides infinite measure constraints, causing a System.Runtime.InteropServices.COMException.
- Added a conditional check to prevent stretching when parent measure width is infinite
- Resolves crashes in scenarios where WrapPanel is placed in auto-sized containers
See #1961 for details.
| Hi @michael-hawker. Thanks for the feedback. 🙂 
 
 This PR just skips the last item stretching if the given space, width or height depending on the  
 Yes. The UpdateRows() method is also called on arrange. 
 I'm working on this. | 
| @michael-hawker I added a simple test case, and it passes successfully with the fix. Without the fix, the test doesn't technically "fail". It breaks the test host itself. This is the error I'm getting: I already tried handling this at App.xaml but didn't help. public App()
{
    this.InitializeComponent();
    UnhandledException += (sender, e) =>
    {
        // Confimed that the exception (System.Runtime.InteropServices.COMException)
        // is handled.
        e.Handled = true; 
    };
}I don't think that this behavior is related to the  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @AndrewKeepCoding, I see you use the term "infinite width", but * does not mean "infinite". It means "stretch, using equal space" and that can be seen by something like <Grid ColumnDefinitions="1*, 3*">. This means: the second column must be three times the size of the first. In other words: two columns, 25% and 75% width of the total. When you have only one column, and one child element, that means the Grid is stretched horizontally, so the child is actually correctly stretching its last item.
Notes: Auto means the minimum size needed (determined by the children) and notice the pretty rounded corners on the right hand side? Nothing is being clipped.
| Hi @Jay-o-Way! Thanks for the clarification 🙂 By “infinite width,” I’m specifically referring to the availableSize.Width argument passed to the WrapPanel’s MeasureOverride() method. In some layouts, such as when the panel is placed inside a Grid with Auto column width, this value can be double.Infinity. When that happens, the last item tries to stretch across an infinite space, which leads to a System.Runtime.InteropServices.COMException. This PR introduces a guard to skip that stretching behavior when infinite space is detected. Sorry for the confusion. | 
| @AndrewKeepCoding yep, you know a lot more about this than I do. <Grid ColumnDefinitions="*">
    <controls:WrapPanel StretchChild="Last">combined with your first screenshot, the result IS exactly what I would expect it to be: the grid is stretched, and the last item stretches pretty inside the available space. The result you introduce with this PR is that the last element does not stretch, and that would seem like a new bug (for future developers who don't know about this history). Obviously, the runtime exception is something to solve, but I don't think this is the best result. | 
| 
 As long a any control does not have a minimum size, that is to be expected. | 
| 
 Since both  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndrewKeepCoding You need to run XAML Styler too, eh?
FYI @azchohfi for once this merges.
| @michael-hawker Sorry about the XAML Styler. It's fixed now. I also added a remark to the  | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @AndrewKeepCoding appreciate all your help on fixing this issue. Would you mind shoring up the tests a bit? We have the new pattern which should make it a bit easier, pointed to the examples from the template.
If you need a hand though, let us know. Thanks!
| 
 @michael-hawker Sure thing! I'm currently working on an issue on the WinUI Gallery that involves a wide range of changes. Once I wrap that up, I'll shift focus to this. 🙂 | 
| Hi @michael-hawker. I just pushed the additional tests. Please let me know if they cover the scenarios you had in mind. | 
| Not sure if this is the same or a different scenario, so would be good to discuss/confirm if it's the same or a different bug. But if it's the same issue then it's a counter-argument to the behavior maybe setup here. As I was writing up the spec for the WinUI transfer and looking at primitive examples and was a bit baffled at what was happening with StretchChild and my intuitive expectations... Take this code:     <controls:WrapPanel Width="132" StretchChild="Last">
        <Rectangle Fill="Red" Width="45" Height="44"/>
        <Rectangle Fill="Blue" Width="44" Height="44"/>
        <Rectangle Fill="Green" Width="44" Height="44"/>
        <Rectangle Fill="Orange" Height="44"/>
    </controls:WrapPanel>This properly stretches the last item across the bottom:   However, if we make enough room for the green box on the first row (by making the red 44 width [only change])...     <controls:WrapPanel Width="132" StretchChild="Last">
        <Rectangle Fill="Red" Width="44" Height="44"/>
        <Rectangle Fill="Blue" Width="44" Height="44"/>
        <Rectangle Fill="Green" Width="44" Height="44"/>
        <Rectangle Fill="Orange" Height="44"/>
    </controls:WrapPanel>Actual: Then the Orange rect disappears entirely!   Expected: I would expect it to span the entire bottom row, like so:   This is the behavior we should at least make sure works (I can open separate issue if it's not related to this one), and have a test for. FYI @azchohfi as we'll need to port whatever fixes we make here to WinUI as well. I'm going to spec it up as I expect it to work for now. | 
| 
 @michael-hawker I believe this is a different issue. We get this bug because the  I took a quick look and it seems that we can address this by updating the condition like so: void Arrange(UIElement child, bool isLast = false)
{
    ...
    var desiredMeasure = new UvMeasure(Orientation, child.DesiredSize);
--  if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U)
++  if ((desiredMeasure.U + position.U + paddingEnd.U) > parentMeasure.U || position.U >= parentMeasure.U)
    {
        // next row!
        position.U = paddingStart.U;
        position.V += currentRow.Size.V + spacingMeasure.V;
        _rows.Add(currentRow);
        currentRow = new Row(new List<UvRect>(), default);
    }
    ...
}Let me know if you want me to create a new issue for this one. | 
| Ah, thanks @AndrewKeepCoding for the confirmation! Appreciate your insights. I'll create a new issue #743 and we can open a new PR with some added tests and things for that scenario outside of this one then. Let me know on that issue if you're also interested in taking a look. | 
| @michael-hawker Can we merge this first, before I start working on the PR for the new row issue? Just to avoid working with conflicts later.🙂 | 
| Thanks for adding the breakdown and pictures to the original post @AndrewKeepCoding, that was super useful with the simple examples! 🦙❤️ Actually, the only thing I'll suggest is that we add a note about this behavior in the doc somewhere. @AndrewKeepCoding mind updating the WrapPanel.md to call this out (You can use the  (You can also see where I had added a StretchChild section in the new spec/doc draft for WinUI here) @Arlodotexe mind just giving this a once-over on your own? Otherwise, I think we're all set! | 
| It also looks like we cannot use  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <controls:WrapPanel x:Name="sampleWrapPanel"
                            Padding="12"
                            HorizontalSpacing="{Binding HorizontalSpacing, ElementName=ThisSamplePage, Mode=OneWay}"
                            VerticalSpacing="{Binding VerticalSpacing, ElementName=ThisSamplePage, Mode=OneWay}" />
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel> | 



Added a conditional check to prevent the last item in the WrapPanel from stretching when the parent measure's width is infinite.
Fixes
Fixes #703 and #297
PR Type
What kind of change does this PR introduce?
What is the current behavior?
THIS WORKS:
THIS THROWS:
This code tries to stretch its last item to an infinity width causing System.Runtime.InteropServices.COMException.
What is the new behavior?
Doesn't stretch its last item when given an infinity width.
PR Checklist
Please check if your PR fulfills the following requirements:
Other information