Morse
Morse

Reputation: 9115

How to do alternative binding with ControlTemplate in Xamarin Forms

I am using ControlTemplate have consistent Navigation Bar accross Android and iOS

<ControlTemplate x:Key="NavBarTemplate">
    <StackLayout>
        <StackLayout Orientation="Horizontal" 
            VerticalOptions="CenterAndExpand" 
            IsVisible="{TemplateBinding BindingContext.IsBackButtonVisible, Mode=TwoWay}">
            <Label Text="&lt;" 
                FontSize="Large" 
                FontAttributes="Bold" 
                HorizontalTextAlignment="Center" 
                VerticalOptions="FillAndExpand" 
                VerticalTextAlignment="Center" 
                Margin="20,0,0,0" />
            <Label TextColor="{StaticResource DarkGrey}" 
                HorizontalOptions="StartAndExpand" 
                HorizontalTextAlignment="Center" 
                VerticalOptions="FillAndExpand" 
                VerticalTextAlignment="Center" 
                FontSize="Small" 
                Text="Back" />
            <StackLayout.GestureRecognizers>
                <TapGestureRecognizer Command="{TemplateBinding BindingContext.NavigateBackCommand}" />
            </StackLayout.GestureRecognizers>
        </StackLayout>
        <Image Source="logo.png" 
            VerticalOptions="Center" />
    </StackLayout>
</ControlTemplate>

In order to dynamically display back button I have added below code in OnAppearing of BasePage.cs

protected override void OnAppearing () 
{
    base.OnAppearing ();

    if (BindingContext is BaseViewModel viewmodel) 
    {
        if(Application.Current.MainPage as NavigationPage nav)
        {

            if(nav.Navigation.NavigationStack.Count >1 )
            {
                viewmodel.IsBackButtonVisible = true;
            }else
            {
                viewmodel.IsBackButtonVisible = false;
            }
            //dirty coding force to false
            if (!this.IsBackButtonVisible) //refers to local BindableProperty
            {
                viewmodel.IsBackButtonVisible = false;
            }
        }else //no viewmodel found just use localbinding 
        {
            BindingContext = this;
        }
    }
}

So in some pages I want to enforce Back button off even though NavigationStack is > 1

Right now I am achieving based on Dirty coding above by having BindableProperty in the BasePage with same name IsBackButtonVisible

My question is , is there a way to do alternative binding for ControlTemplate , I know there is FallBackValue and Default for normal Binding( which are just values not binding ofcourse) but I dont want to hard code it and seems TemplateBinding doesnt support those(intellisence wont show these options)

What I want to do is rename BindableProperty in BasePage to ForceNoBackButton and make navigation bar invisible despite of ViewModel binding.

Is there any way to do alternative binding in TemplateBinding based on conditions..

  1. If BindingContext is type of BaseViewModel then use Property IsBackButtonVisible

  2. Else if page BindingContext is this page or something other than BaseViewModel then fallback to BasePage local BindableProperty i.e ForceNoBackButton

  3. Can I do it with propertychanged event handler of BindableProperty ? If yes, how. I know how to do it if the visual element is in the current XAML, dont know how to do it for TemplatedView

Upvotes: 0

Views: 522

Answers (1)

Sharada
Sharada

Reputation: 13591

You can create two bindable properties IsBackButtonVisible and ForceNoBackButton on BasePage.

public static readonly BindableProperty ForceNoBackButtonProperty =
    BindableProperty.Create(
    "ForceNoBackButton", typeof(bool), typeof(BasePage),
    defaultValue: default(bool));

public static readonly BindableProperty IsBackButtonVisibleProperty =
    BindableProperty.Create(
    "IsBackButtonVisible", typeof(bool), typeof(BasePage),
    defaultValue: default(bool));

And update your control-template to simply use IsBackButtonVisible

<ControlTemplate x:Key="NavBarTemplate">
    ...
    <StackLayout Orientation="Horizontal" 
        ..
        IsVisible="{TemplateBinding IsBackButtonVisible}">

while modifying OnAppearing() method as follows:

protected override void OnAppearing()
{
    ..
    if (nav.Navigation.NavigationStack.Count > 1)
    {
        IsBackButtonVisible = !ForceNoBackButton;
    }
    else
    {
        IsBackButtonVisible = false;
    }

To keep the associated viewmodel in sync with this property - you can either set binding on BasePage.xaml, or add following line in BasePage() constructor to set this binding programmatically.

public BasePage()
{
    InitializeComponent();
    SetBinding(IsBackButtonVisibleProperty,
         new Binding(nameof(BaseViewModel.IsBackButtonVisible), 
              mode: BindingMode.OneWayToSource));
}

Would also recommend to have a property-changed handler for ForceNoBackButton - so you can handle changes to it's value (if they happen after the page has already loaded).

Upvotes: 1

Related Questions