Home > Blockchain >  How to add a View in Custom Control in Xamarin Forms?
How to add a View in Custom Control in Xamarin Forms?

Time:04-03

I have a Custom Control LoadingPanel that has an ActivityIndicator and a Label. I want to access this activity indicator from outside the control wherever. Simply I want to change IsRunning state of it. I am able to do it using bool IsActivityIndicatorRunning, it is running fine, but I don't want to do it for each property of ActivityIndicator repeatedly, hence I want to Add the View and use its properties. Hence I have ActivityIndicator in the custom control and want to map it to the ActivityIndicator present in the control's XAML code.

Here's my Custom Control LoadingPanel.xaml:

<StackLayout Orientation="Horizontal">
    <ActivityIndicator x:Name="activityIndicator" />
    <Label x:Name="loadingMessage" />
</StackLayout>

Here's my Code Behind of Custom Control:

public static readonly BindableProperty IsActivityIndicatorRunningProperty = BindableProperty.Create(nameof(IsActivityIndicatorRunning), typeof(bool), typeof(LoadingPanel), false, BindingMode.TwoWay, propertyChanged: IsActivityIndicatorRunningPropertyChanged);
public static readonly BindableProperty ActivityIndicatorProperty = BindableProperty.Create(nameof(ActivityIndicator), typeof(ActivityIndicator), typeof(LoadingPanel), propertyChanged: ActivityIndicatorPropertyChanged);

public bool IsActivityIndicatorRunning { get => (bool)GetValue(IsActivityIndicatorRunningProperty); set => SetValue(IsActivityIndicatorRunningProperty, value); }
public ActivityIndicator ActivityIndicator { get => (ActivityIndicator)GetValue(ActivityIndicatorProperty); set => SetValue(ActivityIndicatorProperty, value); }

public static void IsActivityIndicatorRunningPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    var control = (LoadingPanel)bindable;
    control.activityIndicator.IsRunning = (bool)newValue;    // just remember this, continue ahead, till here working fine, just revise these concepts
}

public static void ActivityIndicatorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    var control = (LoadingPanel)bindable;
    control.activityIndicator = (ActivityIndicator)newValue;
}

Here's is where I am consuming the custom control

<customControls:LoadingPanel x:Name="loadingPanel" />

Code Behind where I am consuming the custom control

async void ChangeState(object sender, EventArgs e)
{
    loadingPanel.IsActivityIndicatorRunning = true; // This is working fine
    await Task.Delay(5000);

    // here I am getting NullReferenceException as it cannot find ActivityIndicator
    loadingPanel.ActivityIndicator.IsRunning = false;
}

CodePudding user response:

There's no need for declaring fields (Controls or Views) in both the XAML and the C# Code Behind. Declare in either one of them. Wherever you are declaring the fields, make them public so that they can be accessible from outside your custom control.

Note that fields, controls, or views declared in XAML are not public by default. Same as in C# Class Properties, Objects, Fields, Controls are private by default.

You can directly use Child views of your Custom Control from outside if they are public. As you are doing it in XAML, declare it as:

<StackLayout Orientation="Horizontal">
    <ActivityIndicator x:Name="ActivityIndicator" x:FieldModifier="public" />
    <Label x:Name="LoadingMessage" x:FieldModifier="public" />
</StackLayout>
// this should work now from outside of your custom control
loadingPanel.ActivityIndicator.IsRunning = false;

Beware that you had x:Name="activityIndicator" (lowercase), but you were trying to use loadingPanel.ActivityIndicator (uppercase).

Also note that you will not be using Data Binding on Controls directly, instead you will be using Binding on properties of that control such as Label's Text and IsVisible property, ActivityIndicator's IsRunning property, etc. Hence remove these things from your code:

// Remove this if you are already declaring ActivityIndicator in XAML
public ActivityIndicator ActivityIndicator { get => (ActivityIndicator)GetValue(ActivityIndicatorProperty); set => SetValue(ActivityIndicatorProperty, value); }

// Remove this as it is not required for Controls directly
public static readonly BindableProperty ActivityIndicatorProperty = BindableProperty.Create(nameof(ActivityIndicator), typeof(ActivityIndicator), typeof(LoadingPanel), propertyChanged: ActivityIndicatorPropertyChanged);

// Remove this
public static void ActivityIndicatorPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    var control = (LoadingPanel)bindable;
    control.activityIndicator = (ActivityIndicator)newValue;
}
  • Related