5
votes

I seem to be having an issue with databinding in a Xamarin Forms project. I have two views: one for listing items and another to see the details.

The list view loads the list of items and all their properties just fine, but the details page is blank and will not bind to the properties.

The viewmodel for the list looks something like this:

public class MainPageViewModel : ViewModelBase
{
    public MainPageViewModel(IItemService itemService) : base(itemService) { }

    public ObservableCollection<ItemModel> Items { get; set; } = new ObservableCollection<ItemModel>();

    public async override void Start()
    {
        base.Start();

        Items.Clear();

        var results = await service.GetItems();
        foreach (var result in results)
        {
            Items.Add(result);
        }

        IsLoading = false;
    }
}

and is bound to this xaml:

<ListView x:Name="ItemListView" ItemsSource="{Binding Items}" ItemSelected="ItemListView_ItemSelected">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <StackLayout Orientation="Vertical">
                        <Image Source="{Binding Image}" />
                    </StackLayout>
                    <StackLayout Orientation="Vertical" Grid.Column="1">
                        <Label Text="{Binding Name}" />
                        <Label Text="{Binding Description}" />
                    </StackLayout>
                </Grid>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Again that works great.

But the details view looks like this:

public class DetailsPageViewModel : ViewModelBase
{
    public DetailsPageViewModel(IItemService itemService) : base(itemService) { }

    private ItemModel item;
    public ItemModel Item
    {
        get { return item; }
        set
        {
            item = value;
            RaisePropertyChanged();
        }
    }

    protected string ItemId { get; set; }

    public void Init(string itemId)
    {
        this.ItemId = itemId;
    }

    public async override void Start()
    {
        base.Start();

        Item = await service.GetItem(ItemId);

        IsLoading = false;
    }
}

and I'm trying to bind the ItemModel property to labels on the details view:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MvvmCrossFormsDemo.Views.DetailsPageView">
    <StackLayout>
    <Label Text="{Binding Item.Name}" />
    <Image Source="{Binding Item.Image}" />
    </StackLayout>
</ContentPage>

the page comes up completely blank.

do I have to add INotifyPropertyChanged to my ItemModel? If so I don't understand why, because the ListView binds the same model without any such need, so why can't a regular label bind??

what am I doing wrong?

1

1 Answers

9
votes

You need to specify the property in your ViewModel first. The ListView is already bound directly to the property in the ViewModel, and ViewCell is then bound to an individual item already.

In the detail page, you are bound directly to the ViewModel, and hence there are no properties Name or Image, they are properties of Item, which you need to specify first.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MvvmCrossFormsDemo.Views.DetailsPageView">
    <StackLayout>
        <Label Text="{Binding Item.Name}" />
        <Image Source="{Binding Item.Image}" />
    </StackLayout>
</ContentPage>