Xamarin.Forms - How to overlay an ActivityIndicator in the middle of a StackLayout programmatically

60,032

Solution 1

If you have problems with RelativeLayout, you can also use AbsoluteLayout which works in a similar context. Sample code below:

var overlay = new AbsoluteLayout();
var content = new StackLayout();
var loadingIndicator = new ActivityIndicator();
AbsoluteLayout.SetLayoutFlags(content, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(content, new Rectangle(0f, 0f, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
AbsoluteLayout.SetLayoutFlags(loadingIndicator, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(loadingIndicator, new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
overlay.Children.Add(content);
overlay.Children.Add(loadingIndicator);

If you would like a full working example, I have made one available @ https://github.com/teamtam/xamarin-forms-timesheet

Solution 2

There's an acceptable answer but I wrote an extensions for all contentpage and think it could be nice.

public static void AddProgressDisplay (this ContentPage page)
{
    var content = page.Content;

    var grid = new Grid();
    grid.Children.Add(content);
    var gridProgress = new Grid { BackgroundColor = Color.FromHex("#64FFE0B2"), Padding = new Thickness(50) };
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
    gridProgress.SetBinding(VisualElement.IsVisibleProperty, "IsWorking");
    var activity = new ActivityIndicator
    {
        IsEnabled = true,
        IsVisible = true,
        HorizontalOptions = LayoutOptions.FillAndExpand,
        IsRunning = true
    };
    gridProgress.Children.Add(activity, 0, 1);
    grid.Children.Add(gridProgress);
    page.Content = grid;
}

Remark: IsWorking must be member of ViewModel (implemented INotifyPropertyChanged) property.

Call extension page ctor last statement.

this.AddProgressDisplay();

Solution 3

You need to use absolute layout. Follow the following hierarchy of the layout:

<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
    <StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">

        <!--< Your control design goes here >-->

    </StackLayout>

    <StackLayout IsVisible="{Binding IsBusy}" Padding="12"
             AbsoluteLayout.LayoutFlags="PositionProportional"
             AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">

        <ActivityIndicator IsRunning="{Binding IsBusy}" Color ="#80000000"/>

        <Label Text="Loading..." HorizontalOptions="Center" TextColor="White"/>

    </StackLayout>

</AbsoluteLayout>

With this the activity indicator will be overlaid in the middle of a stack layout.

Solution 4

Yes, you should wrap your StackLayout inside Relative Layout. I have already gone through the example which you have given and acheived my requirement by formatting my XAML as below code

XAML Code

<RelativeLayout ...> 
    <StackLayout ...>
        <ActivityIndicator  IsRunning="false"
                            Color="Maroon"
                            BackgroundColor="Black"
                            VerticalOptions="CenterAndExpand"
                            HorizontalOptions="CenterAndExpand"
                            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
                                Property=Height,
                                Factor=0.33}"
                            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
                                Property=Height,
                                Factor=0.28}" />
    </StackLayout>
</RelativeLayout> 

Solution 5

Here is a little more costumizable version of Nuri YILMAZ version.

public static void AddProgressDisplay(this ContentPage page, string isVisibleProperty = "IsBusy", string bgColor = "#1a1a1ab2")
{
    var content = page.Content;

    var grid = new Grid();
    grid.Children.Add(content);
    var gridProgress = new Grid { BackgroundColor = Color.FromHex(bgColor), Padding = new Thickness(50) };
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
    gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
    gridProgress.SetBinding(VisualElement.IsVisibleProperty, isVisibleProperty);
    var activity = new ActivityIndicator
    {
        IsEnabled = true,
        IsVisible = true,
        HorizontalOptions = LayoutOptions.FillAndExpand,
        IsRunning = true
    };
    gridProgress.Children.Add(activity, 0, 1);
    grid.Children.Add(gridProgress);
    page.Content = grid;
}
Share:
60,032
Chad Bonthuys
Author by

Chad Bonthuys

Senior C# developer for DPO|Paygate in the Fintech space

Updated on May 22, 2020

Comments

  • Chad Bonthuys
    Chad Bonthuys about 4 years

    I want to overlay my ActivityIndicator in the middle of my form but I'm not entirely sure how I can go about this, I assume I need to wrap my stack layout in a relative layout ?

    I'm doing this in code and the closest example I have found is using XAML which uses a grid as seen below:

     <ScrollView BackgroundColor="#d3d6db">
        <RelativeLayout
            VerticalOptions="FillAndExpand"
            HorizontalOptions="FillAndExpand">
            <StackLayout Orientation="Vertical"
                         VerticalOptions="FillAndExpand"
                         Padding="10"
                         RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
                         RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}">
    
                <Grid x:Name="SegmentGrid"
                      RowSpacing="10"
                      ColumnSpacing="10">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                </Grid>
                <WebView BackgroundColor="White" VerticalOptions="FillAndExpand" Source="{Binding DescriptionHtml}" />
            </StackLayout>
    
            <ActivityIndicator IsVisible="{Binding IsBusy}"
                               IsRunning="{Binding IsBusy}"
                               Color="Black"
                               VerticalOptions="CenterAndExpand"
                               HorizontalOptions="CenterAndExpand"
                               RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
                                        Property=Height,
                                        Factor=0.33}"
                               RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
                                        Property=Height,
                                        Factor=0.33}" />
        </RelativeLayout>
    </ScrollView>