0

I am trying to set up a component with data binding. This is basically a seperate content view that would have a property Item of type Item and supports binding. The following is the definition for the binding:

public static readonly BindableProperty ItemProperty
    = BindableProperty.Create(
                nameof(Item), typeof(Item), typeof(ItemComponent), null,
                defaultBindingMode: BindingMode.TwoWay,
                propertyChanged: ItemPropertyChanged);
private readonly ItemComponentViewModel vm;

static void ItemPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    var view = (ItemComponent)bindable;
    view.Item = (Item)newValue;
}

public Item Item
{
    get => (Item)GetValue(ItemProperty);
    set
    {
        SetValue(ItemProperty, value);
        if (vm != null) vm.Data = value; // break point here
    }
}

The item doesn't seem to get bound. The commented line had a breakpoint and doesn't break. The complete source code is here: https://github.com/neville-nazerane/xamarin-component-sample

The above code can be found in the ItemComponent class. This component is called in the MainPage class.

Update

Just to explain what I am trying to simulate and why:

Why do we use MVVM in pages? While we'll have better type safety and performance by using the behind code directly, when the page's logic gets bigger, it becomes cleaner to handle it with a view model and to have a view that is simply bound to it.

Why do we have components? So that we can reuse a UI we intend to use with some functionality. If this functionality becomes complex it might need a view model for the same reason explained above. Hence, if pages need view models, I don't see why components won't need them at some point too.

This being considered this does feel like a particle requirement without easy to find examples.

Neville Nazerane
  • 6,622
  • 3
  • 46
  • 79

1 Answers1

1

So after looking at your example it turns out it's a bit of a complicated problem. So if my explanation is not clear, please let me know.

Basically the problem lies in these 2 code pieces:

MainPage.xaml(line 14):

<local:ItemComponent Item="{Binding Demo}" />

ItemComponent.xaml.cs (line 43):

public ItemComponent()
{
    InitializeComponent();
    vm = new ItemComponentViewModel();
    BindingContext = vm; //this breaks the functionality
}

The first part you tell it to bind to the Demo property, and as normal it looks for this property in it's BindingContext. However in the second part you override it's BindigContext and set it to a ItemComponentViewModel this ViewModel however does not have a property Demo so the {Binding Demo} does not work on this new BindingContext you've set.

Now a possible solution for your demo application would be to change MainPage.xaml to the following code:

<?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:SampleApp"
             x:Class="SampleApp.MainPage"
             x:DataType="local:MainViewModel"
             x:Name="MyDemoPage">
    <StackLayout>
        <Label Text="Manual:" />
        <Label Text="{Binding Demo.Title}" />
        <Label Text="Component: " />
        <local:ItemComponent Item="{Binding Path=BindingContext.Demo, Source={x:Reference MyDemoPage}}" />
    </StackLayout>
</ContentPage>

Basically we now place the Demo binding outside of the BindingContext of our ItemComponent control. However if you want to use it in a ListView (if I remember correctly from your original question, this solution might not work and it's possible you'll have to drop the ItemComponentViewModel and bind directly to the properties (ListView will already make sure that the BindingContext of your ItemComponent is set to the current Item, no need to pass it around through a bindable property.

Hope this helps!

  • Thanks! simply commenting out the two lines for setting `vm` helps hit the breakpoint, thus proving your point. However, your possible solution looks like a hack. Knowing the issue, I am trying out some cleaner solutions right now – Neville Nazerane Mar 03 '19 at 18:04
  • 1
    That depends a bit on what you want, but that surpasses the scope of this question imo. You seemed to want to have a ViewModel as BindingContext for the `ItemComponent`. Since you can't have 2 BindingContexes on 1 object you're left with referencing another BindingContext. If you move away from the ViewModel (don't think I would use one for a component) there are lots of other options. The one I would probably go with is making bindable properties for all the data I want to show in the `ItemComponent` and fill them from code behind. –  Mar 03 '19 at 20:25
  • Thinking about it there might actually be a way to not reference another BindingContext and keep your ViewModel. However I consider it a less clean solution then the `x:Reference`. You split the `ItemComponent` into 2 parts. An outer component that: 1 in it's view, only contains the inner component. 2 has a bindable `Item` property, and in it's code behind it syncs the item property to the BindingContext of the inner component. So my claim about referencing being the only way to keep the ViewModel is false, don't consider it a better option though –  Mar 03 '19 at 20:41
  • Why would you say a component shouldn't need a ViewModel? Logically I don't see a difference why a page could have but a component need not. I understand overall this might be outside the question's scope but I am still trying to wrap my head around a couple of things. As of now this still seems to feel like a practical approach to me. – Neville Nazerane Mar 03 '19 at 22:27
  • I thought of the component idea too. But yeah, not only will it not be clean, there would be an additional unneeded component. I am currently thinking of some options to tweak the binding so the it is mapped to the VM's property – Neville Nazerane Mar 03 '19 at 23:16
  • Well it's hard for me to talk about this since I can't see anything of how you intent to use it and the architecture and design philosophy of your application. There are usually multiple ways to solve a problem and imo the best option is the option that is most inline with the architecture of the application. That said I personally see a component like this as a more complicated and glorified `Label` (could be wrong though, depends on your usage), and I wouldn't make a custom extension to a `Label` just so I can give it a ViewModel. –  Mar 04 '19 at 09:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/189384/discussion-between-knoop-and-neville-nazerane). –  Mar 04 '19 at 09:54