Template Binding to Base Page Property

I have a base page class called CustomNavigationDetailPage that conforms to a template "PageTemplate". In the template, I have a custom navigation bar that has a hamburger menu and a user icon. In the CustomNavigationBar class, I defined bindable properties MenuIsEnabled and UserTagEnabled. When these are set, it changes the visibility of the hamburger menu and user icon accordingly. I also mirror defined these properties in the CustomNavigationDetailPage class. I then use template binding to bind the values of the MenuIsEnabled and UserTagIsEnabled properties in the control template. I get a "Cannot assign property "MenuIsEnabled": Property does not exist, or is not assignable, or mismatching type between value and property". What could I be doing wrong? Also, feel free to critique the design/architecture; still new to MVVM... Anway, here is the code:

Page control template:

<ControlTemplate x:Key="PageTemplate">
        <StackLayout Spacing="0">
            <controls:NetworkConnectionStatusBar/>
            <controls:CustomNavigationBar MenuIsEnabled="{TemplateBinding Parent.MenuIsEnabled}" UserTagIsEnabled="{TemplateBinding Parent.UserTagIsEnabled}"/>
            <ContentPresenter VerticalOptions="FillAndExpand" BackgroundColor="{StaticResource TitaniumBackground}"/>
        </StackLayout>
    </ControlTemplate>

Base page class code-behind:

    public partial class CustomNavigationDetailPage : ContentPage
    {
        public CustomNavigationDetailPage()
        {
            InitializeComponent();
        }

        public BindableProperty MenuIsEnabledProperty =
            BindableProperty.Create("MenuIsEnabled", typeof(bool), typeof(CustomNavigationDetailPage), true);
        public bool MenuIsEnabled 
        {
            get { return (bool)GetValue(MenuIsEnabledProperty); }
            set { SetValue(MenuIsEnabledProperty, value); }
        }

        public BindableProperty UserTagIsEnabledProperty =
            BindableProperty.Create("UserTagIsEnabled", typeof(bool), typeof(CustomNavigationDetailPage), true);
        public bool UserTagIsEnabled
        {
            get { return (bool)GetValue(UserTagIsEnabledProperty); }
            set { SetValue(UserTagIsEnabledProperty, value); }
        }
    }

Base page class xaml:

<?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:MNPOS.View.Pages;assembly=MNPOS.View"
             x:Class="MNPOS.View.Pages.CustomNavigationDetailPage"
             ControlTemplate="{StaticResource PageTemplate}"
             NavigationPage.HasNavigationBar="false"/>

Sign in page xaml header:

<?xml version="1.0" encoding="UTF-8"?>
<local:CustomNavigationDetailPage xmlns="http://xamarin.com/schemas/2014/forms" 
                                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                                  xmlns:local="clr-namespace:MNPOS.View.Pages;assembly=MNPOS.View"
                                  xmlns:viewmodel="clr-namespace:MNPOS.ViewModel;assembly=MNPOS.ViewModel"
                                  xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms"
                                  xmlns:utility="clr-namespace:MNPOS.View.Utility;assembly=MNPOS.View"
                                  xmlns:converter="clr-namespace:MNPOS.View.ValueConverters;assembly=MNPOS.View"
                                  x:Class="MNPOS.View.Pages.SignInPage"
                                  MenuIsEnabled="false"
                                  UserTagIsEnabled="false">
   ...
.../>

Best Answer

  • hwangcyrushwangcyrus ✭✭
    Accepted Answer

    I fixed it. Apparently bindable properties HAVE to be static otherwise they won't work as intended. Making all of the bindable properties static fixed everything. Thanks everyone for the help and suggestions!

Answers

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    Have you defined bindable properties for the CustomNavigationBar?

    <controls:CustomNavigationBar MenuIsEnabled="{TemplateBinding Parent.MenuIsEnabled}" UserTagIsEnabled="{TemplateBinding Parent.UserTagIsEnabled}"/>
    

    You have to create bindable properties for CustomNavigationBar just as you did for CustomNavigationDetailPage.

    The bindable properties for CustomNavigationDetailPage look to be set up correctly, though you might not need those properties to be bindable on the CustomNavigationDetailPage since you are setting them to a static value in the xaml.

  • hwangcyrushwangcyrus Member ✭✭

    I do not have the code in front of me at the moment, but I am certain I added the bindable properties in the CustomNavigationBar. I will show the code and xaml on Monday.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    So you are inheriting the ControlTemplate... Hmmm... Just for giggles and testing try assigning the ControlTemplate tag in the actual page and not the base case. Just to see if the properties are being lost in the inheritance.

  • hwangcyrushwangcyrus Member ✭✭

    @JGoldberger said:
    Have you defined bindable properties for the CustomNavigationBar?

    Here is my code for the custom navigation bar:

    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:local="clr-namespace:MNPOS.View.Controls;assembly=MNPOS.View" 
                 x:Class="MNPOS.View.Controls.CustomNavigationBar">
        <ContentView.Content>
            <AbsoluteLayout HeightRequest="175" BackgroundColor="{StaticResource BlueBackground}" Padding="20,20">
                <Image Source="logo.png" AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1" AbsoluteLayout.LayoutFlags="PositionProportional" />
                <Button x:Name="MenuButton" Image="menu.png" AbsoluteLayout.LayoutBounds="0, 1, -1, -1" AbsoluteLayout.LayoutFlags="PositionProportional" Clicked="OnMenuButtonClicked"/>
                <StackLayout x:Name="UserTag" Orientation="Horizontal" AbsoluteLayout.LayoutBounds="1, 1, -1, -1" AbsoluteLayout.LayoutFlags="PositionProportional">
                    <StackLayout.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnUserTagTapped"/>
                    </StackLayout.GestureRecognizers>
                    <Label x:Name="UserNameLabel" VerticalOptions="End" TextColor="{StaticResource WhiteText}" Text="Jonathan McDoe" FontSize="24"/>
                    <Image Source="user.png" />
                </StackLayout>
            </AbsoluteLayout>
        </ContentView.Content>
    </ContentView>
    

    Code-behind:

    using System;
    using MNPOS.ViewModel;
    using Xamarin.Forms;
    
    namespace MNPOS.View.Controls
    {
        public partial class CustomNavigationBar : ContentView
        {
            public CustomNavigationBar()
            {
                InitializeComponent();
            }
    
            public void OnMenuButtonClicked(object sender, EventArgs e)
            {
                if (Application.Current.MainPage is MasterDetailPage currentMainPage)
                    currentMainPage.IsPresented = !currentMainPage.IsPresented;
            }
    
            public void OnUserTagTapped(object sender, EventArgs e)
            {
                System.Diagnostics.Debug.WriteLine("Hellooo?");
            }
    
            public BindableProperty MenuIsEnabledProperty = BindableProperty.Create("MenuEnabledProperty", typeof(bool), typeof(CustomNavigationBar), true);
            public BindableProperty UserTagIsEnabledProperty = BindableProperty.Create("UserTagVisibleProperty", typeof(bool), typeof(CustomNavigationBar), true);
            public BindableProperty UserNameProperty = BindableProperty.Create("UserNameProperty", typeof(string), typeof(CustomNavigationBar), string.Empty);
    
            public bool MenuIsEnabled
            {
                get { return (bool)GetValue(MenuIsEnabledProperty); }
                set 
                {
                    if (MenuIsEnabled != value)
                    {
                        SetValue(MenuIsEnabledProperty, value);
                        MenuButton.IsVisible = value;
                    }
                }
            }
    
            public bool UserTagIsEnabled
            {
                get { return (bool)GetValue(UserTagIsEnabledProperty); }
                set 
                { 
                    if (UserTagIsEnabled != value)
                    {
                        SetValue(UserTagIsEnabledProperty, value);
                        UserTag.IsVisible = value;
                    }
                }
            }
    
            public string UserName
            {
                get { return (string)GetValue(UserNameProperty); }
                set 
                {
                    if (UserName != value)
                    {
                        SetValue(UserNameProperty, value);
                        UserNameLabel.Text = value;
                    }
                }
            }
        }
    }
    
  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai
    edited November 2018

    There is an error in the bindable property creation:

    public BindableProperty MenuIsEnabledProperty = BindableProperty.Create("MenuEnabledProperty", typeof(bool), typeof(CustomNavigationBar), true);
    public BindableProperty UserTagIsEnabledProperty = BindableProperty.Create("UserTagVisibleProperty", typeof(bool), typeof(CustomNavigationBar), true);
    public BindableProperty UserNameProperty = BindableProperty.Create("UserNameProperty", typeof(string), typeof(CustomNavigationBar), string.Empty);
    

    The first parameter of the Create method should be the name of the actual property, not the bindable property, so change the above to:

    public BindableProperty MenuIsEnabledProperty = BindableProperty.Create("MenuIsEnabled", typeof(bool), typeof(CustomNavigationBar), true);
    public BindableProperty UserTagIsEnabledProperty = BindableProperty.Create("UserTagIsEnabled", typeof(bool), typeof(CustomNavigationBar), true);
    public BindableProperty UserNameProperty = BindableProperty.Create("UserName", typeof(string), typeof(CustomNavigationBar), string.Empty);
    

    The first parameter is the name of the actual property and that name has to be the name of the bindable property minus the "Property", i,e if the bindable property is named MyBindableProperty then the actual property name has to be MyBindable.

  • hwangcyrushwangcyrus Member ✭✭

    Even with that change, it still doesn't work and gives the same error message.

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    I think I may need to get your complete project then. I will continue with you on the support case you opened so we can exchange files, etc.

  • hwangcyrushwangcyrus Member ✭✭
    Accepted Answer

    I fixed it. Apparently bindable properties HAVE to be static otherwise they won't work as intended. Making all of the bindable properties static fixed everything. Thanks everyone for the help and suggestions!

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai

    Oh, sorry I missed that, and yes I kew that but just missed that you did not have them as static.

    Thanks for letting me know this is resolved.

Sign In or Register to comment.