Bug 52841 - Custom ViewCell incorrectly types the BindingContext
Summary: Custom ViewCell incorrectly types the BindingContext
Status: NEEDINFO
Alias: None
Product: Forms
Classification: Xamarin
Component: Windows ()
Version: 2.3.4
Hardware: PC Windows
: Normal normal
Target Milestone: ---
Assignee: David Ortinau [MSFT]
URL:
Depends on:
Blocks:
 
Reported: 2017-02-27 14:15 UTC by David Ortinau [MSFT]
Modified: 2017-12-08 16:35 UTC (History)
8 users (show)

Tags: listview, binding, bindingcontext, viewcell, uwp
Is this bug a regression?: Yes
Last known good build: 2.3.2.127

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 52841 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
NEEDINFO

Description David Ortinau [MSFT] 2017-02-27 14:15:11 UTC
From: https://forums.xamarin.com/discussion/85620/databinding-listview-in-xamarin-forms-does-not-seem-to-work-in-uwp?

Excerpts from the thread quoted below.

Here is the relevant code:
```
public class SelectClientPage : ContentPage
{
    public SelectClientPage(ClientResult[] clients)
    {
        this.BindingContext = new SelectClientViewModel(this, clients);

        var layout = new StackLayout { Padding = 20 };

        var list = new ListView
        {
            ItemTemplate = new DataTemplate(typeof(ClientListTemplate)),
            BackgroundColor = MobileColor.LightOrigamiBlue.ToFormsColor(),
            HasUnevenRows = true,
        };
        list.SetBinding(ListView.ItemsSourceProperty, "Clients");
        list.SetBinding(ListView.SelectedItemProperty, "SelectedItem");

        layout.Children.Add(list);

        this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
        this.Content = layout;
    }

    public class ClientListTemplate : ViewCell
    {
        protected override void OnBindingContextChanged()
        {
            ClientResult client = (ClientResult)this.BindingContext;
            var pad = new ContentView {
                Padding = new Thickness(5,5),
                Content = new Label 
                { 
                    Text = client.name,
                    TextColor = Color.White,
                    FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(ListView))
                }
            };
            this.View = pad;
        }
    }
}
```

This code works fine in iOS or Android. However, in UWP, it fails in this line in the ClientListTemplate class:

ClientResult client = (ClientResult)this.BindingContext;

The reason it fails is because the BindingContext passed in to the ViewCell is of type SelectClientViewModel, which was assigned to the BindingContext of the content page initially. So it throws an Invalid Cast Exception.

Has anybody else encountered this? I'm on Xamarin.Forms v2.3.3.175.

...

For both iOS and for Android, a ClientResult instance is passed in.

However, for UWP, what is passed as the BindingContext for the ViewCell is the SelectClientViewModel instance, which is the BindingContext for the parent ContentPage.

...

The last XF version where this worked properly is 2.3.2.127. This is still a problem in 2.3.3.193 and 2.3.4.192-pre2. Works on Android, but not on UWP. If instead you use bindings instead of setting values in OnBindingContextChanged, the cell does not show any of the bound data because the data context is still wrong. I have yet to find a solution.
Comment 1 David Ortinau [MSFT] 2017-02-27 14:17:12 UTC
We need a repro project for this. Requested on the forum thread.
Comment 2 David Ortinau [MSFT] 2017-02-27 16:00:39 UTC
I talked this over with the team and if you using bindings this should not be an issue. UWP currently receives the ListView context first and then the cell context. 

We will look at addressing this inconsistency.

It's also advised to not override OnBindingContextChanged in this way. Rather follow a pattern like this sample that uses bindings: https://github.com/xamarin/xamarin-forms-samples/tree/master/CustomRenderers/ViewCell
Comment 3 phenixdotnet 2017-03-03 11:19:05 UTC
Hi.

FYI, I can reproduce this bug in UWP with Xamarin.Forms 2.3.4.192-pre2.

Do you still need a repro project ? I can send you one if you need.

Regards
Comment 4 FieldstrikeMobile 2017-03-06 10:47:16 UTC
I think I may be seeing this problem when using a Syncfusion List. Although I cant replicate it in a sample to send it back to them. I also dont know how their implementation works

Does this stacktrace look like the same bug?

Object does not match target type.

   at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Xamarin.Forms.BindingExpression.BindingExpressionPart.TryGetValue(Object source, Object& value)
   at Xamarin.Forms.BindingExpression.Unapply()
   at Xamarin.Forms.Binding.Unapply()
   at Xamarin.Forms.BindableObject.ApplyBindings(Object oldContext, Boolean skipBindingContext)
   at Xamarin.Forms.BindableObject.SetInheritedBindingContext(BindableObject bindable, Object value)
   at Xamarin.Forms.Element.SetChildInheritedBindingContext(Element child, Object context)
   at Xamarin.Forms.Element.OnBindingContextChanged()
   at Xamarin.Forms.View.OnBindingContextChanged()
   at Xamarin.Forms.Grid.OnBindingContextChanged()
   at Xamarin.Forms.BindableObject.SetInheritedBindingContext(BindableObject bindable, Object value)
   at Xamarin.Forms.Element.SetChildInheritedBindingContext(Element child, Object context)
   at Xamarin.Forms.Element.OnBindingContextChanged()
   at Xamarin.Forms.View.OnBindingContextChanged()
   at Xamarin.Forms.Grid.OnBindingContextChanged()
   at Xamarin.Forms.BindableObject.SetInheritedBindingContext(BindableObject bindable, Object value)
   at Xamarin.Forms.Element.set_Parent(Element value)
   at Xamarin.Forms.Element.OnChildRemoved(Element child)
   at Xamarin.Forms.VisualElement.OnChildRemoved(Element child)
   at Xamarin.Forms.Layout.OnInternalRemoved(View view)
   at Xamarin.Forms.Layout.InternalChildrenOnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.RemoveItem(Int32 index)
   at System.Collections.ObjectModel.Collection`1.Remove(T item)
   at Xamarin.Forms.TemplateUtilities.OnContentChanged(BindableObject bindable, Object oldValue, Object newValue)
   at Xamarin.Forms.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, Boolean silent)
   at Xamarin.Forms.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes)
   at Xamarin.Forms.BindableObject.SetValue(BindableProperty property, Object value, Boolean fromStyle, Boolean checkAccess)
   at Syncfusion.ListView.XForms.ListViewItem.AddChild(View child)
   at Syncfusion.ListView.XForms.ItemGenerator.CreateTemplate(ListViewItemInfoBase item, DataTemplate template)
   at Syncfusion.ListView.XForms.ItemGenerator.UpdateRecordItem(ListViewItemInfoBase itemInfo, Int32 rowindex, Object data)
   at Syncfusion.ListView.XForms.LayoutBase.UpdateListViewItem(ListViewItemInfoBase iteminfo, Int32 itemIndex, ItemType itemType, Object itemData)
   at Syncfusion.ListView.XForms.LinearLayout.EnsureItems(VisibleLinesCollection visibleLines)
   at Syncfusion.ListView.XForms.VisualContainer.EnsureItems()
   at Syncfusion.ListView.XForms.VisualContainer.OnSizeAllocated(Double width, Double height)
   at Xamarin.Forms.Layout.ForceLayout()
   at Syncfusion.ListView.XForms.LinearLayout.OnItemsCollectionChanged(NotifyCollectionChangedEventArgs e)
   at Syncfusion.ListView.XForms.SfListView.DisplayItems_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)
   at Syncfusion.DataSource.DisplayItems.RaiseCollectionChanged(NotifyCollectionChangedEventArgs args)
   at Syncfusion.DataSource.GroupList.RemoveItemInGroup(Object item)
   at Syncfusion.DataSource.DataSource.UpdateGroupingModel(Object sender, NotifyCollectionChangedEventArgs e)
   at Syncfusion.DataSource.DataSource.OnSourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at FieldStrikeMove.Business.Helpers.RangeObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
Comment 5 Paul DiPietro [MSFT] 2017-03-14 14:56:24 UTC
To the two prior commenters, any reproduction you can provide would be helpful.
Comment 6 David Ortinau [MSFT] 2017-05-22 21:11:43 UTC
A different solution that checks for the appropriate BindingContext from the aforementioned forum thread.

protected override void OnBindingContextChanged()
{
    if(BindingContext is ClientResult) // since the binding context is not set correctly at first, wait until it is
    {
        ClientResult client = (ClientResult)this.BindingContext;
        var pad = new ContentView {
        Padding = new Thickness(5,5),
        Content = new Label 
            {
                Text = client.name,
                TextColor = Color.White,
                FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(ListView))
            }
        };
        this.View = pad;
    }
}
Comment 8 David Ortinau [MSFT] 2017-06-20 15:39:00 UTC
Because we have not received a reply to our request for more information we are closing this issue. If you are still encountering this issue, please reopen the ticket with the requested information. Thanks!
Comment 9 Bryan 2017-08-23 21:17:19 UTC
I know this bug is closed, however, I am encountering the same issue. I have a model that I'm using for View Model with a property that is a List<object>.

    public class FeedbackData
    {
        public string FeedbackID { get; set; }
        public string FeedbackName { get; set; }
        public List<Feedbackquestion> FeedbackQuestions { get; set; }
    }

In my ViewModel, I have a property called Data where I am assigning a new FeedbackData object.

    public class FeedbackViewModel : BaseViewModel
    {

        public FeedbackData Data;

        public FeedbackViewModel(FeedbackData feedbackdata = null)
        {
            Title = "Feedback";

            Data = feedbackdata;
        }
    }

I have the following code in my app to set the ItemSource for mny ListView to the ViewModel.Data property.

            lvQuestions.ItemsSource = viewModel.Data.FeedbackQuestions;     

In my app, I am overriding the OnBindingContextChanged event for my ListView to dynamically select the DataTemplate to use. This code works fine in Droid/iOS< however fails in UWP> Specifically, the BindingContext is the full ViewModel, not the Data property.

        private void OnBindingContextChanged(object sender, EventArgs e)
        {
            var cell = (ViewCell)sender;
            if (TemplateSelector != null)
            {
                var template = TemplateSelector.SelectTemplate(cell, cell.BindingContext);
                cell.View = ((ViewCell)template.CreateContent()).View;
            }
        }

As of right now, I cannot get around the issue.

Using
Xamarin.Forms 2.3.4.267

-Bryan
Comment 10 Bryan 2017-08-23 21:23:21 UTC
Correction to my previous post. cell.BindContext is the full viewModel.Data property, not the viewModel.Data.FeedbackQuestions property.
Comment 11 David Ortinau [MSFT] 2017-08-23 21:30:50 UTC
@Bryan, I'll send this back for review with your notes, thanks.
Comment 13 Bryan 2017-09-07 14:01:41 UTC
Hi David,

I was checking to see if there was any update on this issue? I am getting ready to publish my app and I would love to have a UWP version, however, this issue is preventing me from including that platform.

Thank you!
- Bryan
Comment 14 Bryan 2017-09-25 14:17:07 UTC
An update on this?

-Bryan
Comment 15 Luiz Arantes Sa 2017-11-01 13:28:07 UTC
I am also having this issue
Comment 16 David Ortinau [MSFT] 2017-12-08 16:35:35 UTC
Trying to rebuild a reproduction project and not having success yet. 

I'll ask again, if anyone could please submit a project it will be invaluable to solving this. All our samples that I run are working and not showing this issue.