I have a page that contains three regions (each a VerticalStackLayout
with a Label
and a Frame
containing a CollectionView
) with two Buttons
at the bottom. Each of the regions may or may not be displayed depending upon underlying conditions. Whichever of the regions are visible (three, two, or one -- and it can be any two or any one) should expand to fill the available view space. But I've not been able to hit on the correct combination of XAML elements to make this happen.
I've currently got this structure:
<Grid
ColumnDefinitions="*,*"
RowDefinitions="*,60">
<VerticalStackLayout Grid.Row="0" Grid.ColumSpan="2">
<VerticalStackLayout IsVisible="{Binding ConditionA}">
<Label Text="List A" />
<Frame HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
<VerticalStackLayout IsVisible="{Binding ConditionB}">
<Label Test="List B" />
<Frame HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
<VerticalStackLayout IsVisible="{Binding ConditionC}">
<Label Text="List C" />
<Frame HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
</VerticalStackLayout>
<Button Grid.Row="1" Grid.Column="0" />
<Button Grid.Row="1" Grid.Column="1" />
</Grid>
Without the HeightRequest
on the Frame
the CollectionView
expands to fill the entire page and below running off the bottom of the page. The idea of programatically managing the HeightRequest
(i.e., calculating the appropriate height for whichever regions have displayed) just doesn't feel right. It seems like the expansion should be handled by the XAML elements based upon the available space; e.g., HorizontalOptions="CenterAndExpand"
or something.
So, when all three regions are visible, they should display evening across the page, top to bottom:
List A
-----------------------------
| |
| |
| |
| |
| |
| |
-----------------------------
List B
-----------------------------
| |
| |
| |
| |
| |
| |
-----------------------------
List C
-----------------------------
| |
| |
| |
| |
| |
| |
-----------------------------
---------- -----------
| Button | | Button |
---------- -----------
But if only two regions, the height of the regions should be expanded to balance in the view space:
List A
------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
------------------------------
List C
------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
------------------------------
---------- -----------
| Button | | Button |
---------- -----------
Or, if only one region is displayed, it should fill the available view space:
List B
------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
------------------------------
---------- -----------
| Button | | Button |
---------- -----------
UPDATE: Here's what I ended up doing based upon the answer. This went in the code-behind of the XAML.
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
// This is called twice, once with -1, -1 then the next call with
// the actual width and height.
if (Height > 0)
{
AdjustRegionHeight(height);
}
}
private void AdjustRegionHeight(double pageHeight)
{
double frameHeight;
double bottomSpace = 44;
int cnt = 0;
cnt = (StatesFilter.IsVisible) ? 1 : 0;
cnt = (TopicsFilter.IsVisible) ? 1 : 0;
cnt = (ActivitiesFilter.IsVisible) ? 1 : 0;
double fullHeight = pageHeight - ButtonArea.Height.Value;
frameHeight = (fullHeight / cnt) - bottomSpace;
StatesFrame.HeightRequest = (StatesFilter.IsVisible) ? frameHeight : 0;
TopicsFrame.HeightRequest = (TopicsFilter.IsVisible) ? frameHeight : 0;
ActivitiesFrame.HeightRequest = (ActivitiesFilter.IsVisible) ? frameHeight : 0;
}
CodePudding user response:
The idea of programatically managing the HeightRequest (i.e., calculating the appropriate height for whichever regions have displayed) just doesn't feel right. It seems like the expansion should be handled by the XAML elements based upon the available space;
On the contrary, the idea of programmatically managing the HeightRequest is more likely to be realized.
Based on your code, I don't know the code behind {Binding ConditionA}
. So, to realize this requirement: click the button to change the layout:
.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp1.NewPage1"
Title="NewPage1">
<Grid ColumnDefinitions="*,*" RowDefinitions="*,60">
<VerticalStackLayout Grid.Row="0" Grid.ColumnSpan="2">
<VerticalStackLayout x:Name="ListA" IsVisible="true">
<Label Text="List A" />
<Frame x:Name="FrameA" HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
<VerticalStackLayout x:Name="ListB" IsVisible="true">
<Label Text="List B" />
<Frame x:Name="FrameB" HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
<VerticalStackLayout x:Name="ListC" IsVisible="true">
<Label Text="List C" />
<Frame x:Name="FrameC" HeightRequest="150">
<CollectionView />
</Frame>
</VerticalStackLayout>
</VerticalStackLayout>
<Button Grid.Row="1" Grid.Column="0" Clicked="onlyOne"/>
<Button Grid.Row="1" Grid.Column="1" Clicked="Two"/>
</Grid>
</ContentPage>
.xaml.cs:
public partial class NewPage1 : ContentPage
{
public NewPage1()
{
InitializeComponent();
}
private void onlyOne(object sender, EventArgs e)
{
//change the state of IsVisible of VerticalStackLayout
ListB.IsVisible = false;
ListA.IsVisible = false;
ListC.IsVisible = true;
FrameC.HeightRequest = 450;
}
private void Two(object sender, EventArgs e)
{
ListC.IsVisible = false;
ListB.IsVisible = true;
ListA.IsVisible = true;
FrameA.HeightRequest = 225;
FrameB.HeightRequest = 225;
}
}
The first button is that only one region(List C) displays. The second button is that only two regions(List A and B) display.
Back to your situation, ConditionA/B/C change
is equivalent to the above button being clicked. You can add code logic in Button.Clicked to the function of ConditionA/B/C change
.