cfischy
cfischy

Reputation: 381

Workaround for ScrollView not scrolling inside a StackLayout in Maui

I have a page that presents a list of items in a CollectionView within a ScrollView. I want the user to be able to add a new item to this list by hitting an "add" button or tapping an image at the top of the page. I first tried this using the hierarchy of views shown below. The code below is an abbreviated version of the real thing. I discovered that putting a ScrollView within a VerticalStackLayout breaks the scrolling in Maui! Here is the reported bug.

I tried deleting the VerticalStackLayout that precedes the ScrollView and the scrolling still doesn't work.

<ContentPage.Content>

<VerticalStackLayout>
    <VerticalStackLayout HorizontalOptions="Center">
        <Image Source="add.png">
           <ImageGestureRecongnizers>
               <TapGestureRecognizer... Code to add new item to MyCollection...]/>
           </ImageGestureRecognizers>
        </Image>
    </VerticalStackLayout>
    <ScrollView>
        <CollectionView ItemsSource="{Binding MyCollection}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    [Layout for displaying items in MyCollection...]
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </ScrollView>
<VerticalStackLayout


</ContentPage.Content>

I'd greatly appreciate suggestions on a workaround to allow the viewing of the scrollable list and adding an item to the list by tapping an object (button or image) that's always visible on the page regardless of how the list is scrolled.

Upvotes: 1

Views: 7266

Answers (6)

Marius Rusu
Marius Rusu

Reputation: 125

I found this to be of great help:

  1. The easiest thing to do is to set the VerticalOptions on the ScrollView to "StartAndExpand". This will trigger the legacy behavior for StackLayout and make it act as if it were a single-column Grid. (This is effectively what it's doing in Forms when it special-cases the "ScrollView inside StackLayout" behavior.)
  2. [My prefered solution] Alternatively, replace the outer StackLayout with a Grid. This will constrain the ScrollView vertically and allow for scrolling. "But wait, I want to stack things above the ScrollView." Okay, divide your Grid up into two rows, and put a StackLayout in the first row source: https://github.com/dotnet/maui/issues/12452#issuecomment-1683054644

Upvotes: 1

Ivan
Ivan

Reputation: 1873

ScrollView need to have a definite height. So calculate and set the height of ScrollView. This is the fix.

Upvotes: 0

Dave Foley
Dave Foley

Reputation: 1

I had a similar problem where I wasn't seeing a Vertical scollbar. I found if I set the row height in the grid containing the CollectionView from "Auto" to "*", I would then see a vertical scrollbar.

Upvotes: 0

Actually for the Xaml, this will work...

<?xml version="1.0" encoding="utf-8" ?> 
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="MauiAppTest01.MainPage">

<Grid RowDefinitions="30,*">

        <CollectionView ItemsSource="{Binding MyCollection}"
Grid.Row="1">

            <CollectionView.ItemTemplate>

                <DataTemplate>

                    <VerticalStackLayout  >

                        <Label Text="{Binding Name}" FontSize="Large" />


                    </VerticalStackLayout>


                </DataTemplate>

            </CollectionView.ItemTemplate>

        </CollectionView>


    <Image Source="dotnet_bot.png" HeightRequest="100" WidthRequest="100" 
 Margin="0,0,50,20">

        <Image.GestureRecognizers>
            <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
        </Image.GestureRecognizers>

    </Image>
</Grid>

Adjust your RowDefinition(30) for the image! Sorry if my code is not neat, as I'm on mobile.

Upvotes: 1

Alexandar May - MSFT
Alexandar May - MSFT

Reputation: 10156

If you want to allow the viewing of the scrollable list and add items to the list by tapping an image, you can just wrap them with a Grid.

Here's the code snippet below for your reference:

XAML:

<?xml version="1.0" encoding="utf-8" ?> 
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiAppTest01.MainPage">

    <Grid>

            <CollectionView ItemsSource="{Binding MyCollection}">

                <CollectionView.ItemTemplate>

                    <DataTemplate>

                        <VerticalStackLayout  >

                            <Label Text="{Binding Name}" FontSize="Large" />


                        </VerticalStackLayout>


                    </DataTemplate>

                </CollectionView.ItemTemplate>

            </CollectionView>


        <Image Source="dotnet_bot.png" HeightRequest="100" WidthRequest="100" HorizontalOptions="End" VerticalOptions="End" Margin="0,0,50,20">

            <Image.GestureRecognizers>
                <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
            </Image.GestureRecognizers>

        </Image>
    </Grid>


</ContentPage>

Code-behind:

public partial class MainPage : ContentPage 
{

    public ObservableCollection<MyModel> MyCollection { get; set; }
    
    public MainPage()
    {
            InitializeComponent();

            MyCollection = new ObservableCollection<MyModel>
            {

               new MyModel{ Name="1"},

               new MyModel{ Name="2"},

             };

             BindingContext = this;
     }



    private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
    {

        for (int i = 0; i < 10; i++) {
            MyCollection.Add(new MyModel { Name = i+"" });
        }
        
    }
}

Model:

    public class MyModel 
    {
        public string Name { get; set; }
    }

Upvotes: 0

cfischy
cfischy

Reputation: 381

I ended up creating a "fancy" floating button on the bottom right of the page by:

  • Creating a one-row Grid
  • Putting both the CollectionView and Image in the one row
  • Defining the Image after the CollectionView so that the Image sits on top of the CollectionView

I also got rid of the ScrollView per Jason's suggestion.

<ContentPage.Content>

    <Grid
        <CollectionView ItemsSource="{Binding MyCollection}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    [Layout for displaying items in MyCollection...]
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
        <Image Source="add.png"  HeightRequest="40" HorizontalOptions="End"
            VerticalOptions="End" Margin="0,0,50,20">
           <ImageGestureRecongnizers>
               <TapGestureRecognizer... Code to add new item to MyCollection...]/>
           </ImageGestureRecognizers>
        </Image>
    </Grid>


</ContentPage.Content>

Upvotes: 0

Related Questions