xamarin.formsbindableproperty

Xamarin.Forms: Bindable Property in CustomView not hit


I define a CustomView with one Bindable Property. To make things clearer, I removed all code unnessesary to understand my problem. This is my 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:vm="clr-namespace:Packer.ViewModels"
             x:DataType="vm:ContainerItemViewModel"
             x:Class="Packer.CustomViews.ContainerView">
    <ContentView.BindingContext>
        <vm:ContainerItemViewModel/>
    </ContentView.BindingContext>
    <StackLayout Orientation="Vertical">
        <Label Text="Hello World!" />
    </StackLayout>
</ContentView>

...and this is my code behind:

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Packer.CustomViews
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ContainerView : ContentView
    {
        public static readonly BindableProperty ItemIdProperty =
            BindableProperty.Create(nameof(ItemId), typeof(int), typeof(ContainerView), -1);
 
        public ContainerView()
        {
            InitializeComponent();
        }

        public int ItemId
        {
            get => (int)GetValue(ItemIdProperty);
            set
            {
                SetValue(ItemIdProperty, value);
            }
        }
    }
}

With xmlns:custom="clr-namespace:Packer.CustomViews" This CustomView is called as follows:

<StackLayout Orientation="Horizontal">
     <custom:ContainerView HorizontalOptions="FillAndExpand" ItemId="{Binding ItemId}" />
     <Label Text="{Binding ItemId}" />
</StackLayout>

Debugging this app as UWP, I can't see property ItemId being hit. However, the label following the Custom View is displayed properly, as well as the "Hello World" Label defined in the Custom View.

This is my configuration:

enter image description here


Solution

  • Updates:

    When you set this in xaml:

    <ContentView.BindingContext>
        <vm:ContainerItemViewModel/>
    </ContentView.BindingContext>
    

    It just like you set this in page:

    <custom:ContainerView BindingContext=ContainerViewModel HorizontalOptions="FillAndExpand" ItemId="{Binding ItemId}" />
    

    Of course we could not get the ItemId property change because we maually set the BindingContext to ContainerViewModel (We know that ItemId is not in it).

    But when I set

    Content.BindingContext = new ContainerItemViewModel();
    

    It means we just set the BindingContext for the ContainerView's Content. We could also receive the changes of ItemId.


    If you want to Detect property changes for BindableProperty, you could use a callback method instead of adding some logic in Setter.

    public static readonly BindableProperty ItemIdProperty =
               BindableProperty.Create(nameof(ItemId), typeof(int), typeof(ContainerView), -1,propertyChanged:OnItemIdPropertyChanged);
    
    private static void OnItemIdPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var a = (int)newValue;
    }
    

    I don't know why you use a ContainerItemViewModel for ContainerView. Because the BindingContext can inherit from the parent page. If you still want to do so, you could delete:

    <!--<ContentView.BindingContext>
        <vm:ContainerItemViewModel/>
    </ContentView.BindingContext>-->
    

    And set it in code-behind like this:

    public ContainerView()
    {
        InitializeComponent();
        // Notice here, please set Content.BindingContext here
        Content.BindingContext = new ContainerItemViewModel();
    }
    

    Now if you set a breakpoint in OnItemIdPropertyChanged method, and it will hit the code. If you still have any question, feel free to ask.

    Hope it helps!