Hikari
Hikari

Reputation: 599

Xamarin binding property of base view

I have a base XAML View that have a custom control with bindable property that is used to show/hide a Grid control. I need to change this property from a XAML View that inherit from the base View.

the custom control view

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Syncfusion.SfBusyIndicator.XForms;
using System.Runtime.CompilerServices;

namespace TEST_SF
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class VolosLoading : ContentView
{
    private static Grid _LoadingContainer = null;

    public bool Mostra
    {
        get { return (bool)GetValue(MostraProperty); }
        set { SetValue(MostraProperty, value); OnPropertyChanged(nameof(Mostra)); }
    }

    public static BindableProperty MostraProperty = BindableProperty.Create(
       propertyName: nameof(Mostra),
       returnType: typeof(bool),
       declaringType: typeof(VolosLoading),
       defaultValue: false,
       defaultBindingMode: BindingMode.TwoWay
       , propertyChanged: MostraPropertyChanged
   );

    private static void MostraPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {

        _LoadingContainer.IsEnabled = (bool)newValue;
        _LoadingContainer.IsVisible = (bool)newValue;
    }

    public VolosLoading()
    {
        InitializeComponent();
        _LoadingContainer = (Grid)FindByName("LoadingContainer");
        OnPropertyChanged(nameof(Mostra));
    }
 }
}

its 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:busyindicator="clr-namespace:Syncfusion.SfBusyIndicator.XForms;assembly=Syncfusion.SfBusyIndicator.XForms"
    x:Class="TEST_SF.VolosLoading">

    <ContentView.Content>        
        <Grid x:Name="LoadingContainer" IsEnabled="{Binding Mostra}" IsVisible="{Binding Mostra}">            
            <Grid BackgroundColor="LightGray" Opacity="0.6" />
            <busyindicator:SfBusyIndicator x:Name="Loading" AnimationType="DoubleCircle" 
                                           ViewBoxWidth="150" ViewBoxHeight="150" 
                                           TextColor="Green" BackgroundColor="Transparent"  
                                           HorizontalOptions="Center" VerticalOptions="Center" />
        </Grid>
    </ContentView.Content>

</ContentView>

the base class

   namespace TEST_SF.Base
    {
      public class BaseClass
      {
          public static VolosLoading PageLoading = new VolosLoading { Mostra = false };
      }
    }

the base view

 <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TEST_SF"
             x:Class="TEST_SF.BasePage">
    <ContentPage.ControlTemplate>
        <ControlTemplate>
            <StackLayout>
                <Label BackgroundColor="Red" Text="Welcome to Xamarin.Forms!"
                    VerticalOptions="StartAndExpand" 
                    HorizontalOptions="CenterAndExpand" />
                <local:VolosLoading></local:VolosLoading>

                <ContentPresenter></ContentPresenter>

            </StackLayout>
        </ControlTemplate>

    </ContentPage.ControlTemplate>
    </ContentPage>

Then I have a view that inherit from the base View with a button that calls a command that execute this code:

PageLoading.Mostra = !PageLoading.Mostra;

The class is:

class MainPage_Repo : Base.BaseClass

its view:

 <local:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TEST_SF"
             x:Class="TEST_SF.MainPage">

        <ContentPage.BindingContext>
            <local:MainPage_Repo />
        </ContentPage.BindingContext>

            <StackLayout>
                <Button VerticalOptions="Center" HorizontalOptions="Center" Text="Loading SF" Command="{Binding MyMockCommand}" CommandParameter="1" />
            </StackLayout>
    </local:BasePage>

The problems are that the Grid is visible at start, and when the button is pressed nothing changes, the value of Mostra is changed correctly but he Grid is always visible.

How can i solve this?

Upvotes: 0

Views: 1054

Answers (2)

Ivan I
Ivan I

Reputation: 9990

If you have static properties in ContentView, weird problems are expected to happen, as they are shared across instances, in your case that relates to the _LoadingContainer

Even if this doesn't resolve your problem that is something that can cause huge problems and shouldn't be done.

Upvotes: 1

Tom
Tom

Reputation: 1749

The problem is when you are calling:

_LoadingContainer = (Grid)FindByName("LoadingContainer");

This line is creating a copy of the Grid container defined in the XAML by x:Name="LoadingContainer". Then, when the Mostra property changes, you are then doing:

_LoadingContainer.IsEnabled = (bool)newValue;
_LoadingContainer.IsVisible = (bool)newValue;

The above is accessing and changing the properties of the copy of the Grid view, not the actual LoadingContainer itself.

Replace all instances of _LoadingContainer with LoadingContainer, and your problem should be solved.

Also, you can remove the bindings to the IsVisible and IsEnabled properties of the Grid. These will cause unnecessary complexity in the compiled code.

EDIT

In addition, in your MostraPropertyChanged handler, you should change it to the following:

private static void MostraPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    var control = (VolosLoading)bindable;
    if (control != null)
    {
        control.LoadingContainer.IsEnabled = (bool)newValue;
        control.LoadingContainer.IsVisible = (bool)newValue;
    }
}

Upvotes: 0

Related Questions