Ciaran Gallagher
Ciaran Gallagher

Reputation: 4020

Reusable ContentView Binding for an ActivityIndicator

I have an ActivityIndicator which I have in a number of different pages like this:

<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
                    Color="DarkBlue" IsVisible="{Binding IsLoading}" IsRunning="{Binding IsLoading}">
</ActivityIndicator>-

I'm trying to make this component more easily reusable by creating a 'ContentView', which looks like this:

Loading.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="Framework.Controls.Loading" x.Name="Loading">
  <ContentView.Content>
      <ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue" 
                         IsVisible="{Binding Source={x:Reference Name=Loading},Path=IsLoading}"
                         IsRunning="{Binding Source={x:Reference Name=Loading},Path=IsLoading}">
      </ActivityIndicator>
    </ContentView.Content>
</ContentView>

Which I am trying to consume with code like this:

<controls:Loading IsLoading="{Binding IsLoading}"></controls:Loading>

The problem I'm having is that I am not sure how to create a binding so that I can set the IsVisible and IsRunning properties on the ActivityIndicator.

My code-behind for the component looks like this:

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
    public static readonly BindableProperty LoadingIndicatorProperty =
        BindableProperty.Create(
            propertyName: nameof(IsLoading), typeof(bool),
            typeof(Loading), default(string), BindingMode.OneWayToSource);

    public bool IsLoading
    {
        get => (bool)GetValue(LoadingIndicatorProperty);
        set => SetValue(LoadingIndicatorProperty, value);
    }

    public Loading()
    {
        InitializeComponent();
    }
}

With the current code, I get build errors, so something obviously isn't right here.

The errors are as follows:

How do I correctly create a binding that will apply to the IsVisible and IsRunning properties on my Activity Indicator? And why doesn't the compiler like the ContentView 'x.Name' property?

Upvotes: 0

Views: 377

Answers (2)

Roubachof
Roubachof

Reputation: 3401

I created a Xamarin.Forms control that does exactly that, and more (handle errors and empty state as well). You can find it here:

https://github.com/roubachof/Sharpnado.Presentation.Forms#taskloaderview

The blog post explaining it in details is here:

https://www.sharpnado.com/taskloaderview-async-init-made-easy/

Upvotes: 0

Bruno Caceiro
Bruno Caceiro

Reputation: 7199

First, you have a typo:

It is x:Name and you have x.Name

So, in your View:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="Framework.Controls.Loading" 
             x:Name="Loading">
  <ContentView.Content>
      <ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue" 
                         IsVisible="{Binding IsLoading"
                         IsRunning="{Binding IsLoading">
      </ActivityIndicator>
    </ContentView.Content>
</ContentView>

then, in code-behind you can just:

XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
    public static readonly BindableProperty LoadingIndicatorProperty =
        BindableProperty.Create(
            propertyName: nameof(IsLoading), typeof(bool),
            typeof(Loading), default(string));

    public bool IsLoading
    {
        get => (bool)GetValue(LoadingIndicatorProperty);
        set => SetValue(LoadingIndicatorProperty, value);
    }

    public Loading()
    {
        InitializeComponent();
        BindingContext=this;
    }
}

Upvotes: 1

Related Questions