Giter VIP home page Giter VIP logo

Comments (44)

lindexi avatar lindexi commented on May 16, 2024 8

Define the undefined behaviors

XAML's Grid is the most widely used Panel and is very versatile. However, there are some undefined behaviors.

I hope we can define undefined behaviors.


Reading Tips: All of the examples described in this article are not common usages for Grid. (Microsoft is a great company. It will never do strange things in the common situation.)

Star Unit on Infinite space

Copy and paste the code below and run to view the result:

<Canvas>
    <Grid Height="100">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" Background="CornflowerBlue" Width="150" />
        <Border Grid.Column="1" Background="Tomato" Width="150" />
        <Border Grid.Column="2" Background="Teal" Width="150" />
    </Grid>
</Canvas>

See Scenario1 in demo.

The 1st column is 100-pixel fixed-width. The 2nd column is *, and the 3rd one is 2*. Then what's the visible width of the 2nd Border and the 3rd Border?

image

Did you predicate the result? Although the 2nd and the 3rd column width proportion is 1:2, the final visible proportion is 1:1.

There are flaws here, because you may suspect that the 3rd column is already twice as much as the 2nd column, but the right side is blank and cannot be seen. So now, we remove the Canvas and use HorizontalAlignment="Right". The new code is shown below:

<Grid HorizontalAlignment="Right">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="2*" />
    </Grid.ColumnDefinitions>
    <Border Grid.Column="0" Background="CornflowerBlue" Width="150" />
    <Border Grid.Column="1" Background="Tomato" Width="150" />
    <Border Grid.Column="2" Background="Teal" Width="150" />
</Grid>

See Scenario2 in demo.

After running, you will find that there is no white space on the far right, that is to say, the 2nd and 3rd columns do not have a 1:2 ratio - they are equal.

So where is the lost space? Let's resize the window to check it.

narrow the window

Even if there is space left on the left, the right side begins to clip the element space! Can we say that the length of a missing * length has gone to the left? Obviously not. However, we can guess that the clipping of the right side of the element begins at the 1:2 ratio.

Star Unit at the Size Just Required

HorizontalAlignment="True" helps us a lot to distinguish whether the right side really occupies space. So we continue the testing on the right-alignment.

Now, we modify the 2nd column Border to span the 2nd and 3rd columns. The 3rd column Border is placed into the 2nd column. (In other words, our 3rd column does not contain any Border.)

<Grid HorizontalAlignment="Right">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="2*" />
    </Grid.ColumnDefinitions>
    <Border Grid.Column="0" Background="CornflowerBlue" Width="150" />
    <Border Grid.Column="1" Grid.ColumnSpan="2" Background="Tomato" Width="150" />
    <Border Grid.Column="1" Background="Teal" Width="150" />
</Grid>

See Scenario3 in demo.

The new behavior did not show much surprise to us because we have seen the behavior last section. The 3rd column disappeared, and the 2nd column still lost the 1:2 ratio.

Narrow the window again.

Narrow


the window


again


to


view


the behavior

Why did the tomato Border suddenly appear when the window was narrowing? Why is there a blank space on the right side of the tomato Border?

If we have realized in the last post section that the right-side space is lost when it is right-aligned, why does the white space appear suddenly in the right-side again?

I tried to slightly increase the width of the second Border. Suddenly, I reproduced the strange behavior that I reproduced just now when resizing the window!

The Proportion of Auto Size

Now, abandon the previous right-aligned test method and no longer use the * width to separate the Grid. We use Auto instead.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Border Width="159" Grid.ColumnSpan="3" HorizontalAlignment="Center" Background="PaleGreen" />
    <Border Width="28" HorizontalAlignment="Left" Background="#7FFF6347" />
    <Border Width="51" Grid.Column="1" HorizontalAlignment="Center" Background="#7FC71585" />
    <Border Width="28" Grid.Column="2" HorizontalAlignment="Right" Background="#7F008080" />
</Grid>

See Scenario4 in demo.

Specifically, we have four Border, placed in three columns of Auto size. The first Border spans three columns, and its size is longer than all the others, reaching 159. The remaining three Border each occupy a column, with two sides of equal length and a slightly longer middle.

How are the columns in the actual layout divided? Here is the column width that the designer shows for us:

Where do 46, 69, 46 come from? Could it be that the proportion of 46:69 is the same as that of 28:51? However, the actual calculation result is not!

What if this is a calculation error?

So let's look at the other two sets of values for the three Border: 50:50:50 and 25:50:25.


β–² 50:50:50


β–² 25:50:25

In 50:50:50, we eventually get the 1:1:1 proportion. But the ratio of column widths in 25:50:25 is far from 1:2:1. That is, in fact, the Grid does not calculate the column widths by the proportion to the size of the element.

The same Element Size but Different Column Width

In the experiment in the previous section, we notice that the same size brought about the same final visible size regardless of the proportion. However, this conclusion still can be subverted.

Now, we will replace 3 columns with 4 columns, and the number of Border will be replaced with 6.

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Border Width="159" Grid.ColumnSpan="3" HorizontalAlignment="Center" Background="PaleGreen" />
    <Border Width="159" Grid.Column="1" Grid.ColumnSpan="3" HorizontalAlignment="Center" Background="PaleGreen" />
    <Border Width="28" HorizontalAlignment="Left" Background="#7FFF6347" />
    <Border Width="51" Grid.Column="1" HorizontalAlignment="Center" Background="#7FC71585" />
    <Border Width="51" Grid.Column="2" HorizontalAlignment="Center" Background="#7FC71585" />
    <Border Width="28" Grid.Column="3" HorizontalAlignment="Right" Background="#7F008080" />
</Grid>

See Scenario5 in demo.

Specifically, the first Border spans the first three columns, and the second Border spans the last three columns, the same length as the long Border of the previous section. The third and sixth Borders are on two sides and are as short as the previous Border. The middle two Borders are as long as the previous Border. Just like the picture that is shown below.

What is the width of the columns laid out at this time?


β–² 32:65:65:39

Wait! Where did the 39 come from? If the equal-size Border in the previous section would get equal-sized column widths, then this will also subvert! In fact, even if the proportion of the column width to the proportion of elements is the same at this time, there are as infinitely as many solutions under this layout. WPF picks only one out of this infinite number of solutions - and it cannot explain itself!

The conclusion of the Grid undefined behavior

In summary, the Grid layout has some unreasonable behaviors under special circumstances. I call them "the undefined behaviors". These undefined behaviors are summarized in the following three points:

  1. Infinite layout space with * unit size
  2. * unit column/row with multiple-span elements
  3. Auto size in all column/row

However, you may think that I use the Grid in incorrect ways. However, as an API that exposes the behaviors, the behavior itself is also a part of the API. It should have clear traceable and documentable behavior instead of being explored and guess and failed by the user.

Microsoft does not have any official documents that disclose these bizarre behaviors, and I have not found such behavior in any third-party references (this post is my own conclusion). I think that Microsoft did not publish this kind of documents because the behaviors are too bizarre to be documented!

I hope we can define this behaviors.

See The undefined behaviors of WPF Grid (the so-called bugs) - walterlv

from microsoft-ui-xaml.

dotMorten avatar dotMorten commented on May 16, 2024 7

IMHO a lot of the feature presented above to improve Grid, it really sounds like what you're looking for is RelativePanel.

I do agree ColumnDefinitions should have a simple syntax, and that could easily be achieved with a string converter. Ie <Grid ColumnDefinitions="1*,200,Auto,2*" />

from microsoft-ui-xaml.

JustinXinLiu avatar JustinXinLiu commented on May 16, 2024 5

Here's some of my initial thoughts. :)

Most useful

  1. One of the pain points of using the current Grid is that, when I want to insert a new row in the middle of it, I have to update all the row numbers below it. So having the ability to name rows and columns not only helps improve readability of the code, it also doesn't require updating any existing code.

  2. I love the idea of Auto-flow Layout. Like this code snippet below from the proposal, there's no need to have any columns or rows defined on the actual controls anymore! This could potentially replace the UniformGrid as well.

    <Grid ColumnDefinitions="240, 1*"
      ColumnSpacing="18"
      AutoFlow="Row"
      AutoRowDefinitions="{MinHeight=80 Height=Auto}">
    
      <TextBlock Text="Id"/>
      <TextBox x:Name="IdField"/>
    
      <TextBlock Text="Name"/>
      <TextBox x:Name="NameField"/>
    
      <TextBlock Text="Address"/>
      <TextBox x:Name="AddressField"/>
    </Grid>
    

Other thoughts

  1. Most of the time I use the Designer to create columns and rows as well as assigning them to each child controls. Having a more succinct syntax is still nice though.

  2. I don't mind having Areas because it's one attribute to write instead of two (i.e. column and row) on a control.

  3. IMHO, this is very different from RelativePanel because it's still a very strict grid-layout (which is a good thing).

Additional questions (just thinking out loud...)

  1. Is there a way to align grid items like how justify-content does in CSS?

  2. Take the following CSS code for example, how can we make the Grid responsive-design friendly?

    grid-template-columns: repeat(auto-fit, minmax(296px, 1fr));

from microsoft-ui-xaml.

dotMorten avatar dotMorten commented on May 16, 2024 3

This proposal is rather big (albeit great), I've created a separate proposal for just a simpler string-conversion based syntax of Row and Column definitions (linked above here) that might be a good simple first-step that is very achievable to do and doesn't require a lot of new API or behavior.

from microsoft-ui-xaml.

YuliKl avatar YuliKl commented on May 16, 2024 2

There's a request on UserVoice to ShowGridLines that ought to be considered for this feature proposal.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024 2

@thomasclaudiushuber I've updated the proposal to follow the current naming precedence with RowDefinitions and ColumnDefinitions (and AreaDefinitions for consistency).

I agree there's ways that tooling can help in making it easier to use Grid (and other layouts) and would hope that any improvements to Grid (UWP and WPF) are complementary to any future improvements in the tooling. @diverdan92 as fyi

from microsoft-ui-xaml.

chrisglein avatar chrisglein commented on May 16, 2024 1

I assume in this that Grid.AutoFill maps to CSS grid's grid-auto-flow? (I stumbled on the name change at first)

from microsoft-ui-xaml.

mrlacey avatar mrlacey commented on May 16, 2024 1

To add clarity to the Auto-fill Layout w/ Autogenerated Rows (or Columns) example, is this equivalent ?

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="240" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition MinHeight="80" Height="Auto" />
        <RowDefinition MinHeight="80" Height="Auto" />
        <RowDefinition MinHeight="80" Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="HorizontalAlignment" Value="Right"/>
        </Style>
    </Grid.Resources>

    <TextBlock Text="Id" Grid.Column="0" Grid.Row="0" />
    <TextBox x:Name="IdField" Grid.Column="1" Grid.Row="0" />

    <TextBlock Text="Name" Grid.Column="0" Grid.Row="1" />
    <TextBox x:Name="NameField" Grid.Column="1" Grid.Row="1" />

    <TextBlock Text="Address" Grid.Column="0" Grid.Row="2" />
    <TextBox x:Name="AddressField" Grid.Column="1" Grid.Row="2" />

</Grid>

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024 1

@chrisglein Yes, the autofill would behave the same as the CSS Grid's auto-flow.

@mrlacey The intention with Row="toprow,bottomrow" was that it be interpreted as a range "from,to" rather than a set of non-contiguous rows/columns. I'll clarify that in the proposal. I considered using a colon as the delimiter like Excel, but went with comma for now.
Re: auto-fill w/ autogenerated rows, your example is exactly it. Thanks!

from microsoft-ui-xaml.

chrisglein avatar chrisglein commented on May 16, 2024 1

@lindexi A clear advantage of WinUI living here on GitHub is that we can A) have the source code for the Grid layout readily available, B) use Issues to track either incorrect behaviors or the lack of documentation for edge case behaviors. It seems like together that can help shed light on undefined behaviors and get them to be defined?

@micahl framed this proposal as a desire to absorb some of the goodness from CSS's grid and make a more expressive Grid for XAML via WinUI. As interesting as the cases you've raised are, it seems like there's enough meat there to raise a separate discussion about these existing edge cases? I don't want to distract from the proposal @micahl laid out. Or at the very least phrase these edge cases in a way that are more relevant to the CSS comparison. For example, you can model that first scenario in CSS and talk about how the behavior is different between that and the XAML equivalent and how we feel about those differences:

<style>
.grid-container {
  display: grid;
  grid-template-columns: 100px 1fr 2fr;
  grid-auto-flow: column;
  height: 100px;
}
.item1 {
  background-color: cornflowerblue;
  width: 150px;
}
.item2 {
  background-color: tomato;
  width: 150px;
}
.item3 {
  background-color: teal;
  width: 150px;
}
</style>

<div class="grid-container">
  <div class="item1"></div>
  <div class="item2"></div>
  <div class="item3"></div>  
</div>

from microsoft-ui-xaml.

thomasclaudiushuber avatar thomasclaudiushuber commented on May 16, 2024 1

@michael-hawker Yes, I linked it already, as you can see above. Thanks for explicitly mentioned it here.

@micahl Thanks for the proposal. Here some thoughts:

  • We shouldn't rename the properties RowDefinitions and ColumnDefinitions to Rows and Columns. Instead I think we should keep the original properties and implement a TypeConverter to get the short hand syntax. That stuff is already discussed here dotnet/wpf#166 and it's the main point of the WPF issue.
  • I like the idea of areas. But I think it's much harder to read than explicit columns and rows on an element. In a large document I look at an element, I see the area. Then I scroll up to the area definition, then I look at its rows and columns and then I look at the rows and columns themselves to finally understand how the element is layed out. While I see the value of such an area and while I like it, I think it also overcomplicates things a bit, as you get more ways to do the same thing
  • Naming RowDefinitions and ColumnDefinitions sounds like an interesting idea. But when it comes to RowSpan or ColumnSpan it gets more complex, as it requires a start and an end row/column instead of a simple number. I guess if the tooling would have support for numbers, we don't need names. For example Visual Studio could suggest this quick tip when I place the cursor behind a RowDefinition:
    image
    Then that could automatically adjust all the numbers accordingly and the problem is solved. We could think about the UX a bit more, it's just an initial idea.

On the other points I agree with @dotMorten. The RelativePanel has a lot of that stuff mentioned. I think the Grid should be improved, but it should also stay straight and simple. From my point of view, we should start with the simpler syntax for RowDefinitions and ColumnDefinitions by implementing a Type Converter and we should improve the tooling.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024 1

Thanks for the suggestions! At this point in the conversation I think we need to start considering what the merge would look like if the proposed options are combined and ask ourselves, "Is the behavior for various combinations still reasonable? Does it create conflicts or duplication?" The tl:dr; answer to those is No and Yes. Let's see if we can whittle things down to make it Yes and No.

@lindexi I believe AreaDefinitions can provide the same functionality as RowSpanDefinitions and ColumnSpanDefinitions. It seems reasonable to set a combination of Grid.Row/RowSpan/Column/ColumnSpan properties on an element in addition to specifying a Grid.Area. But, what would it mean and what takes precedence? I believe having the Row(Span)/Column(Span) values take precedence would make the most sense. They'd behave like overrides on the area. For example:

<Grid Name="Root"
      Rows="5" RowHeight="Auto"
      Columns="5" ColumnWidth="Auto"
      AreaDefinitions="{Name=Foo Row=1..2}, {Name=TheNext Row=1..3}">
    <Rectangle Name="Back" Grid.Column="1" Grid.Area="Foo"/>
</Grid>

Since the definition of the Foo area didn't explicitly set a Column it would have to use a default (i.e. zero). Explicitly setting the Grid.Column="1" in the example will mean that the Rectangle will be on row 1 spanning to row 2 and end up in column 1 (instead of the default column 0).

Taking this exploration a bit further to consider the combinations of other properties... What would happen in the contrived example below?

<Grid Name="Root"
      Rows="3"
      RowHeight="Auto"
      RowDefinitions="{Name=BackRow Row=1}"
      Columns="3"
      ColumnWidth="Auto"
      AutoFlow="Row"
      AutoRowDefinitions="40"
      AutoColumnDefinitions="60"
      AreaDefinitions="{Name=BackArea1 Column=1 Row=1..3},{Name=BackArea2 Column=1 Row=BackRow},{Name=BackArea3 Column=1 Row=BackRow..6">

    <Rectangle x:Name="Rect1" Grid.Column="1" Grid.Row="BackRow"/>

    <Rectangle x:Name="Rect2" Grid.Column="1" Grid.Row="1..2"/>

    <Rectangle x:Name="Rect3" Grid.Area="BackArea1"/>

    <Rectangle x:Name="Rect4" Grid.Area="BackArea2"/>

    <Rectangle x:Name="Rect5" Grid.Area="BackArea3"/>

    <Rectangle x:Name="Rect6" Grid.Column="4" Grid.Area="BackArea1" Grid.Row="4"/>

    <Rectangle x:Name="Rect7" Grid.Column="4" Grid.Area="BackArea3" Grid.Row="4"/>
</Grid>

My initial reaction is that it can become confusing very quickly. I believe it would be something equivalent to the markup below. Some issues raised by this exercise are:

  • How should a RowHeight/ColumnWidth property behave with the AutoRowDefinitions/AutoColumnDefinitions?
    • Having both is confusing and it seems like they're close. The shorter name better aligns with the general goal of a more succinct syntax so... drop AutoRow/ColumnDefinitions in favor of RowHeight and ColumnWidth? IMO, yes.
  • Would named Areas be that useful in practice? Or do they just make things more complicated for someone to mentally parse what's going on in the markup?
    • They definitely increase the potential for edge cases when combined with other properties. And, as was pointed out earlier, areas just give more ways to do the same thing which suggests its duplicate functionality. Grid can be difficult to understand at times. Combining AreaDefinitions with the other properties seems to make that even more challenging and not a 'Must' have.
<Grid Name="Root">
    <Grid.RowDefinitions>
        <!-- These rows come from the Rows="3" RowHeight="Auto" properties. -->
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <!-- These are autogenerated to provide the filler rows until the actual row referenced by BackArea3. -->
        <RowDefinition Height="40"/>
        <RowDefinition Height="40"/>

        <!-- This is created so that the row referenced by Rect6 and 7 exists. 
             It's Auto by default to mimic what the CSS Grid seems to do. Should it be 
             based on the RowHeight property instead?  How does RowHeight behave with 
             AutoRowDefinitions? -->
        <RowDefinition Height="Auto"/>

        <!-- These are autogenerated to provide the filler rows until the next row -->
        <RowDefinition Height="40"/>
        <RowDefinition Height="40"/>

        <!-- This is row is required to satisfy the rowspan that comes from combining 
             Rect7's Grid.Row with the BackArea3 area definition.  The row's height is
             Auto by default to mimic what the CSS Grid seems to do. Same question as 
             before. Should it be based on the RowHeight property instead? -->
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <!-- the first 3 Auto columns come from the Columns="3" ColumnWidth="Auto" properties -->
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <!-- the next 2 columns are autogenerated to create the fifth column (Column="4") that Rect6 and Rect7 assume to exist -->
        <ColumnDefinition Width="60"/>
        <ColumnDefinition Width="60"/>
    </Grid.ColumnDefinitions>

    <Rectangle x:Name="Rect1" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"/>

    <!-- Row=1..2 meant row 1 to row 2.  It becomes row 1 with a span of 2. -->
    <Rectangle x:Name="Rect2" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"/>

    <Rectangle x:Name="Rect3" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"/>

    <Rectangle x:Name="Rect4" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2"/>

    <!-- Row=1..6 meant from row 1 to row 6. It becomes row 1 with a span of 5. -->
    <Rectangle x:Name="Rect5" Grid.Column="1" Grid.Row="1" Grid.RowSpan="5"/>

    <!-- BackArea1 had Row=1..3 which would be row 1 with a span of 2, but the 
         explicit Grid.Row="4" overrides the starting position (only). -->
    <Rectangle x:Name="Rect6" Grid.Column="4" Grid.Row="4" Grid.RowSpan="2"/>

    <!-- BackArea3 had Row=Back..6 (same as 1..6) which would be row 1 with a span of 5,
         but the explicit Grid.Row="4" overrides the starting position (only) -->
    <Rectangle x:Name="Rect7" Grid.Column="4" Grid.Row="4" Grid.RowSpan="5"/>

</Grid>

If you made it through that and it made any sense then hats off to you. ;)

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024 1

Thanks, Justin! Brainstorming...

Aligning grid items
I think the equivalent of justify-content (and the related align-content) in XAML parlance would be like having a ColumnsAlignment (and RowsAlignment) property with values of Left, Center, Right, SpaceAround, SpaceEvenly, SpaceBetween (and similar for rows with Top, Center, Bottom). For example:

<Grid Width="445" Height="100"
      AutoColumnDefinitions="75"
      ColumnsAlignment="SpaceEvenly"
      ShowGridLines="True">
    <Grid.Resources>
        <Style TargetType="Rectangle">
            <Setter Property="Height" Value="60"/>
            <Setter Property="Fill" Value="Blue"/>
        </Style>

    <Rectangle />
    <Rectangle />
    <Rectangle />
    <Rectangle />
    <Rectangle />
</Grid>

would give something like below where the tan-ish color represents how the extra width is being distributed across the auto-generated fixed size columns.

grid-columnsalignment

Responsive design
Well, using an AdaptiveTrigger to change column definitions might be one option. Alternatively, a more literal translation of the CSS repeat() method might look like the below. It probably violates a number of things I haven't thought about and seems like a very Grid-specific thing.

<Grid ColumnDefinitions="{x:Repeat(AutoFit, {MinWidth=296, Width=1*}})"/>

Other ideas?

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024 1

Good scenario. Reading the post it reminded me of the shared sizing capability that WPF supports on its Grid which helps address that exact kind of situation.

Another approach (that honestly warrants its own separate proposal for discussion) is to introduce the concept of attached layout more broadly than repeater controls. Separating the pure layout logic from the container (e.g. Panel) would allow a given layout / sizing to be shared across separate containers.
For example, if the layout logic for Grid was also available as a GridLayout type that could be attached to a container (e.g. LayoutPanel) then it would be possible to do something like the below. Despite not being part of the same container the TextBlocks could be visually aligned:

<Page.Resources>
    <GridLayout x:Key="sharedGridLayout" AutoFlow="Row" AutoColumnDefinitions="48,Auto"/>
</Page.Resources>

<LayoutPanel Layout="{StaticResource sharedGridLayout}">
    <FontIcon/>
    <TextBlock Text="Lorem"/>
    <FontIcon/>
    <TextBlock Text="Lorem ipsum dolor"/>
</LayoutPanel>

<!-- ... -->

<LayoutPanel Layout="{StaticResource sharedGridLayout}">
    <FontIcon/>
    <TextBlock Text="Lorem ipsum ..."/>
    <FontIcon/>
    <TextBlock Text="Lorem"/>
</LayoutPanel>

from microsoft-ui-xaml.

JustinXinLiu avatar JustinXinLiu commented on May 16, 2024 1

Amazing. I totally forgot about IsSharedSizeScope in WPF.

The other approach works even better IMO given that's mostly where we use repeated layouts.

from microsoft-ui-xaml.

JustinXinLiu avatar JustinXinLiu commented on May 16, 2024 1

@bschoepke nice one, but I am not sure if it's a Grid specific thing. How about fluid layout for all panel controls?

P.S. Currently we can use Visual and implicit animations to achieve similar background animation. Like you said, animating its contents will need composition clip.

from microsoft-ui-xaml.

mrlacey avatar mrlacey commented on May 16, 2024

I'm curious about this part of the Defining Named Areas example:

...
<AreaDefinition Name="imageArea" Row="toprow,bottomrow" Column="0"/>
...
<Image Grid.Area="imageArea"/>
...

How would the image be displayed in an area that covers multiple rows? (or equivalent for columns?)

Non-sequential rows or columns in an AreaDefinition has the potential to be very confusing. I'm not sure what I expect to happen here and so predict that multiple people could have multiple differing expectations. I'd rather see an AreaDefinition support Row and RowSpan (Or Column equivalents) rather than multiple non-sequential rows.
If there's a good reason to support non-sequential rows (from CSS or elsewhere) I'm happy to be convinced otherwise.

from microsoft-ui-xaml.

mrlacey avatar mrlacey commented on May 16, 2024

+1 for being able to name rows and columns. I can see this being a great way to improve the readability of code.

from microsoft-ui-xaml.

mrlacey avatar mrlacey commented on May 16, 2024

@mrlacey The intention with Row="toprow,bottomrow" was that it be interpreted as a range "from,to" rather than a set of non-contiguous rows/columns. I'll clarify that in the proposal. I considered using a colon as the delimiter like Excel, but went with comma for now.

A comma is used as a separator elsewhere in this proposal so a colon to indicate a range sounds better.

from microsoft-ui-xaml.

walterlv avatar walterlv commented on May 16, 2024

Agree to fix that undefined grid behaviors. This may not be bugs, but it's behavior is really undescriptable.

from microsoft-ui-xaml.

michael-hawker avatar michael-hawker commented on May 16, 2024

We also added an enhanced UniformGrid to the toolkit; so it'd be interesting to think about the overlap between those scenarios and the ones proposed here.

I think the main thing the UniformGrid adds is that it's also effectively an ItemsControl that can take a collection of items to layout, which seems to be a bit what the 'AutoFill' feature is trying to accomplish as well?

I guess the question is could these new Grid 'extensions' cover the UniformGrid scenarios as well?

from microsoft-ui-xaml.

chrisglein avatar chrisglein commented on May 16, 2024

@michael-hawker It does seem like some of the AutoFill/AutoRows options could eliminate the need to use UniformGrid for many scenarios. I'm not sure if it covers all. So I walked through the scenarios listed in the document you linked and recreated them in CSS grid:

https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/uniformgrid#grid-properties

<style>
.grid-container {
  display: grid;
  grid-template-columns: 150px 150px 150px;
  grid-column-gap: 24px;
  grid-row-gap: 24px;
  grid-auto-rows: 150px;
  grid-auto-flow: row;
}
</style>

<div class="grid-container">
  <div style="background-color: aliceblue">1</div>
  <div style="background-color: cornsilk">2</div>
  <div style="background-color: darksalmon">3</div>  
  <div style="background-color: gainsboro">4</div>  
  <div style="background-color: lightblue">5</div>  
  <div style="background-color: mediumaquamarine">6</div>  
  <div style="background-color: mistyrose">7</div>  
</div>

https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/uniformgrid#sized-children

<style>
.grid-container {
  display: grid;
  grid-template-columns: 150px 150px 150px;
  grid-auto-rows: 150px;
  grid-auto-flow: row;
}
</style>

<div class="grid-container">
  <div style="background-color: aliceblue; grid-column-end: span 2;">1</div>
  <div style="background-color: cornsilk">2</div>
  <div style="background-color: darksalmon">3</div>  
  <div style="background-color: gainsboro; grid-row-end: span 2;">4</div>  
  <div style="background-color: lightblue">5</div>  
  <div style="background-color: mediumaquamarine">6</div>  
  <div style="background-color: mistyrose">7</div>  
</div>

https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/uniformgrid#fixed-child-locations

<style>
.grid-container {
  display: grid;
  grid-template-columns: 150px 150px 150px;
  grid-auto-rows: 150px;
  grid-auto-flow: row;
}
</style>

<div class="grid-container">
  <div style="background-color: aliceblue; grid-column-start: 2; grid-row-start: 2;">1</div>
  <div style="background-color: cornsilk">2</div>
  <div style="background-color: darksalmon">3</div>  
  <div style="background-color: gainsboro;">4</div>  
  <div style="background-color: lightblue">5</div>  
  <div style="background-color: mediumaquamarine">6</div>  
  <div style="background-color: mistyrose">7</div>  
</div>

https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/uniformgrid#override-rows-and-columns
This one I haven't been able to figure out yet. I feel like it should be possible.

https://docs.microsoft.com/en-us/windows/communitytoolkit/controls/uniformgrid#orientation
I don't think this scenario is possible with CSS grid. Unlike CSS flex layout's flex-direction there doesn't seem to be a similar grid-direction. This is something we could include if there was need for it.

Note that for many of the above examples I'm making a hard assumption about 3 columns instead of re-flowing. CSS grid seems pretty adverse to re-flowing the layout in that way, and it doesn't handle constraints in the way I expect. Again this is something that CSS flex layout does better with flex-wrap. We'd want a XAML Grid's auto flow options to be able to handle wrapping.

If you have any other UniformGrid scenarios please share them. It does feel like we can achieve a superset Grid layout.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024

@chrisglein Isn't the Orientation behavior covered by the grid-auto-flow in CSS? An orientation of Horizontal is the same as auto-flow of row and Vertical is column?
The CSS Grid's auto-flow also supports a row-dense and column-dense option. Thinking about whether these enhancements might supplant the need for other, similar panels such as UniformGrid... If there was a row-dense/column-dense option on Grid then I believe it would replace our existing VariableSizedWrapGrid.

from microsoft-ui-xaml.

chrisglein avatar chrisglein commented on May 16, 2024

@chrisglein Isn't the Orientation behavior covered by the grid-auto-flow in CSS? An orientation of Horizontal is the same as auto-flow of row and Vertical is column?

@micahl I was thinking of the comparison to flex-direction=row-reverse/column-reverse. CSS flex gives you the ability to reverse the order where CSS grid does not.

If I'm understanding "dense" correctly it's really about sacrificing element order so that the grid can be more tightly packed. Consider the difference between "row" and "row dense" in this example:

<style>
.grid-container {
  display: grid;
  grid-template-columns: 150px 150px 150px;
  grid-auto-rows: 150px;
  grid-auto-flow: row dense;
}
</style>

<div class="grid-container">
  <div style="background-color: aliceblue">1</div>
  <div style="background-color: cornsilk">2</div>
  <div style="background-color: darksalmon; grid-column: span 2">3</div>  
  <div style="background-color: gainsboro;">4</div>  
  <div style="background-color: lightblue">5</div>  
  <div style="background-color: mediumaquamarine">6</div>  
  <div style="background-color: mistyrose">7</div>  
</div>

The thing that makes that example work is having an item with a span that causes it to not fit. This mode would actually make sense in a wrapping flex layout as well (looking forward for smaller items before wrapping a larger item to the next row), but to my knowledge that doesn't exist.

Which isn't what I thought "dense" was going to be at first glance (although the behavior does make sense to me). I was at first looking for a knob to control whether undesignated cells filled in at the earliest open spot or after the last filled spot. Do you fill in the middle or append at the end? Which is probably a useful option although isn't what "dense" does.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024

Right. The downside to the dense behavior is that it might adversely impact an accessibility experience because of how it might jumble around the ordering.
Another observation... the auto flow behavior might be useful in a forms layout scenario where some items are explicitly assigned and the others flow around it. The non-dense behavior would allow someone to explicitly nudge an item over to an assigned column and intentionally leave some empty space because subsequent items would be placed after it rather than potentially filling in gaps.

Updates to the proposal...

  • @chrisglein Renamed AutoFill to AutoFlow given the CSS precedent
  • @YuliKl Added ShowGridLines to the requirements. Thanks!

@mrlacey I agree that the comma for specifying a span can be confusing given how its used elsewhere. For the sake of discussion some alternatives might be:

  • A colon ':' For example, Column="leftcol:rightcol"
  • A double-dot '..' For example, Column="leftcol..rightcol".
  • Mimic the CSS syntax with the slash '/' For example, Column="leftcol / rightcol"
  • Don't do anything special. Instead have RowSpan and ColumnSpan properties on AreaDefinition that accept a number or name.

From the perspective of readability the colon seems to blend into the text when using names as opposed to numbers which makes it less decipherable. A double-dot '..' fairs better IMO. The slash just makes me think of division. The explicit RowSpan and ColumnSpan is familiar, albeit verbose.

I'm interested to hear others opinions.

from microsoft-ui-xaml.

michael-hawker avatar michael-hawker commented on May 16, 2024

@micahl I like .. for the slice syntax, though it'd be good to track what C# is doing for this as well, as outlined here: dotnet/csharplang#185

ShowGridLines would be great as WPF had it as well, so another gap filled. I'd use it a lot for debugging and in some other projects.

from microsoft-ui-xaml.

michael-hawker avatar michael-hawker commented on May 16, 2024

FYI, to call out, a similar discussion was opened on the WPF Issues: dotnet/wpf#166

from microsoft-ui-xaml.

lindexi avatar lindexi commented on May 16, 2024

@thomasclaudiushuber I like the idea of areas and binding the name. I always modify the Grid's row and column when we change the UI and I need change all the element's row and column in the Grid when I insert the row or the column at the first row or the first column. If I can bind the name that I can add the row or the column without modifying the element.

from microsoft-ui-xaml.

trodent83 avatar trodent83 commented on May 16, 2024

@micahl
I think it would also help a lot if we would have a Kind of mix of grid and uniform grid functionality where we could specify a general theme for the grids, and then redefine them with explicit overwrides.

So something similar to this:

<Grid Rows="5" Columns="6" RowHeight="Auto"> <Grid.RowDefinitions> <RowDefinition Row ="2" Height="*"/> </Grid.RowDefinitions> </Grid>
because in most cases we have basicly UniformGrid which only differ in one or two rows and columns.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024

Thanks, @trodent83. Based on your comment I took a moment to look across a few different apps using Grid to see what common patterns existed for rows/column definitions. Granted this wasn't a very rigorous data collection exercise, but my observations were that:

  1. Auto-sizing was used more than *-sizing
  2. The row (or column) definitions for a Grid were often homogeneous (e.g. all Auto or all *) with a few that differed (similar to your assertion)
  3. Most Grids I saw only had 2-3 row (or column) definitions. At least in my sample I didn't see anything with more than 8.

Assuming what I observed holds more generally and there are usually only a handful of column/row definitions (less than 3) I'd suspect that a common approach would be to just rely on the shortcut syntax for Row/ColumnDefinitions. For example:
<Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="*,*"/>

from microsoft-ui-xaml.

lindexi avatar lindexi commented on May 16, 2024

Add RowSpanDefinitions to Grid. As RowSpanDefinitions name says this property can define the row span.

How to use?

                <Grid Name="Root" RowDefinitions="Auto,Auto,Auto,Auto,Auto"
                      ColumnDefinitions="Auto,Auto,Auto,Auto,Auto"
                      RowSpanDefinitions="{Name=Foo Start=1 End=2},{Name=TheNext Start=1 End=3}">
                          <Rectangle Name="Back" Grid.Column="1" Grid.RowSpan="Foo"/>
              </Grid>

It like the Area by @micahl but we can use it in different column. And we can think of it is an area that has not to define where is it column.

image

We define another set of attributes ColumnSpanDefinitions in pairs.

image

But when we use Grid.RowSpan to bind the RowSpanDefinitions, can we use Grid.Row property?

from microsoft-ui-xaml.

trodent83 avatar trodent83 commented on May 16, 2024

@micahl
It is true that in most cases the Grids only have a few rows and columns, and the simplification of the Definition in these cases would help a lot, but sadly sometimes this is not the case, and in these situations the simplified Definition would not only not help much, but quite the opposite as it would make the code less readable.

And I thing the two aproaches could be merged relatively easy, and so we could have the best of the two worlds.

from microsoft-ui-xaml.

lindexi avatar lindexi commented on May 16, 2024

@micahl How about something like StrokeDashArray?

       StrokeDashArray="1*,2*"

image

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024

@lindexi, thanks for the suggestion. In your experience has the need for that kind of non-uniform distribution of space between items occurred frequently?

from microsoft-ui-xaml.

lindexi avatar lindexi commented on May 16, 2024

@micahl This is my lack of consideration. I find all of my codes and I can not find any app use it.

from microsoft-ui-xaml.

JustinXinLiu avatar JustinXinLiu commented on May 16, 2024

Other ideas?

I came across this post talking about subgrid in CSS and immediately remembered I had to deal with similar cases in the past and the solution was either to use a fixed width, or manually re-calculate it whenever a new item is added in.

I don't think this is a very common scenario but could be something to have a think about. ;)

from microsoft-ui-xaml.

mdtauk avatar mdtauk commented on May 16, 2024

Uncommon scenarios may be uncommon, because they are too difficult to implement with current controls and layout engines.

from microsoft-ui-xaml.

JustinXinLiu avatar JustinXinLiu commented on May 16, 2024

Just thought of something else. ;)

Have we considered making RowDefinitions, ColumnDefinitions and AreaDefinitions as attached properties that are data-binding friendly? Imagine this scenario:

<Style TargetType="MyControl">
    <Setter Property="Grid.RowDefinitions" Value="{Name=toprow Height=Auto}, {Name=body Height=1*}, {Name=bottomrow Height=Auto}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="MyControl">
                <Grid RowDefinitions="{TemplateBinding Grid.RowDefinitions}">
                    ...
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<MyControl Grid.RowDefinitions="{Name=toprow Height=1*}, {Name=body Height=4*}, {Name=bottomrow Height=Auto}" />

Currently, I will have to duplicate and modify the entire style if I want to change the layout of inner elements of a control. If they were bindable, I could simply override RowDefinitions on the control itself.

from microsoft-ui-xaml.

mdtauk avatar mdtauk commented on May 16, 2024

We have gone from XAML providing best in class Grid panels, with CSS and HTML utterly failing. To HTML CSS having a very flexible solution as well as powerful.

When final decisions are made to pursue a new Grid control, I think it should be done as a new control at least until it can reach the maturity of the current Grid control.

So far what @JustinXinLiu has been considering, are tweaks and simpler XAML notation for the Grid. But if this new enhanced Grid was done as a new control, more options could be considered as well as breaking changes as the expectation wont be just simply substituting one for another.

Things like:

  • Gutters / Column and Row gaps, with or without border lines;
  • Per grid cell borders, padding, backgrounds;
  • Row and Column spans/merges;
  • Responsive re-flowing of cells/content;
  • Area and region naming/styling;
  • Dynamic placement of items in grid areas;
  • Insertion of repeated or data-bound rows and columns with Child(X) or some other means of specifying where new rows and columns are placed;

from microsoft-ui-xaml.

bschoepke avatar bschoepke commented on May 16, 2024

@micahl @jevansaks CSS Grid properties are now animatable in Firefox Nightly:
https://twitter.com/vlh/status/1085181353283543041

I know XAML layout currently runs on the UI thread but perhaps this new Grid provides an opportunity to make fast layout transforms like this easier--even if some smoke and mirrors are required, like automatically animated clips.

from microsoft-ui-xaml.

michael-hawker avatar michael-hawker commented on May 16, 2024

Linking issue from the toolkit, closed ours out for now as it's an open item here. For reference, here's the WPF Property for ShowGridLines for back-compat. And the UserVoice item currently has 18 votes for this property.

from microsoft-ui-xaml.

mdtauk avatar mdtauk commented on May 16, 2024

@bschoepke nice one, but I am not sure if it's a Grid specific thing. How about fluid layout for all panel controls?

P.S. Currently we can use Visual and implicit animations to achieve similar background animation. Like you said, animating its contents will need composition clip.

What about making this enhanced Grid a CompositionGrid which moves the layout from UI Thread to the Composition Layer, and becomes flexible in how and where items will be layed out, and allows for flexible reflow as the size changes, whilst keeping it smooth, and enabling future advances like lifting elements out for Mixed Reality 3D presentation.

from microsoft-ui-xaml.

micahl avatar micahl commented on May 16, 2024

Hey folks, I only recently returned from being gone since mid-January and have been catching up on things. I apologize if it seemed like I went dark. If it happens again I'll be sure to mention it beforehand or at least set my GitHub status.

Good discussion and thanks for all the input!

@JustinXinLiu, its interesting to think about lifting the layout out of a control such that it can be more easily styled without requiring you to re-template. For example, if a control's template parts were assigned their slot in the layout by name (i.e. Grid.Area) and if it was possible to set the Grid definitions the control should use then I believe it would make changing a control's internal layout <Style>-able.

@mdtauk, moving all of layout from the UI thread to the composition layer would be pretty gnarly (in ways both good and not so good). Depending on the desired UX there might be ways to achieve it without moving everything off the UI thread.

from microsoft-ui-xaml.

nandin-borjigin avatar nandin-borjigin commented on May 16, 2024

I've started a dicussion in WindowsCommunityToolkit to support dynamic layout switching in Grid, with a working PR. The PR also introduces the concept of AreaDefinition and I borrowed(stole) the idea from CSS grid-template-areas. I personally think the approach is succinct and intuitive while I'm open to any suggestion. It would be great if we can merge these two proposals and finally produce a more powerful Grid.

I used plain string to describe AreaDefinition. The element names are listed in row-major order, separated by white space, and semicolons are used to separate different rows.

An example layout like this

    +----------+----------+----------+
    |          | ElementB | ElementC |
    | ElementA +----------+----------+
    |          |       ElementD      |
    +----------+---------------------+
    |             ElementE           |
    +--------------------------------+

could be expressed simply like below:

ElementA ElementB ElementC;
ElementA ElementD ElementD;
ElementE ElementE ElementE

Complete example

The example defines two possible layouts for a grid and supports switching them dynamically (by setting the GridExtensions.ActiveLayout property. The part that is related to this discussion is the way that I used to describe an AreaDefinition.

<Grid x:Name="RootGrid" GirdExentions.ActiveLayout="Normal">
    <!-- Declaratively define the possible layouts. -->
    <!-- GridExtensions.Layouts is a dictionary of GridLayoutDefinition -->
    <GridExtensions.Layouts>
        <GridLayoutDefinition x:Key="Normal">
            <!-- A GridLayoutDefinition consists of -->
            <!-- row definitions, column definitions and an area definition -->
            <GridLayoutDefinition.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </GridLayoutDefinition.RowDefinitions>
            <GridLayoutDefinition.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </GridLayoutDefinition.ColumnDefinitions>
            <!-- Area definition just simply puts down -->
            <!-- children names in desired order -->
            Number Title Description
        </GridLayoutDefinition>
        <GridLayoutDefinition x:Key="Narrow">
            <GridLayoutDefinition.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </GridLayoutDefinition.RowDefinitions>
            <GridLayoutDefinition.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </GridLayoutDefinition.ColumnDefinitions>
            Number      Title; <!-- semicolon is used to separate differnt rows -->
            Description Description <!-- row/column span is expressed by repeating the elment name -->
        </GridLayoutDefinition>
    </GridExtensionstensions.Layouts>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="NarrowState">
                <VisualState.StateTriggers .../> <!-- Trigger implementation omitted -->
                <VisualState.Setters>
                    <!-- Only ActiveLayout property on the root grid needs to change according to the visual state -->
                    <Setter Target="RootGrid.(GridExtensions.ActiveLayout)" Value="Narrow">
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <TextBlock x:Name="Number">1</TextBlock>
    <TextBlock x:Name="Title">Lorem Ipsum</TextBlock>
    <TextBlock x:Name="Description">Lorem ipsum dolor sit amet...</TextBlock>
</Grid>

from microsoft-ui-xaml.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.