Databinding ListView in Xamarin.Forms does not seem to work in UWP

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.

Thanks.

Answers

  • kentuckerkentucker USMember ✭✭✭✭✭

    I usually do my data templates in xaml and have not noticed any issues. If you put a break point on the line which does a cast what is it showing as the type? You can use as to prevent the cast error.

     ClientResult client = this.BindingContext as ClientResult;
    
     string name = "unknown";
     if(client!=null)
     {
          name=client.Name;
      }
            var pad = new ContentView {
                Padding = new Thickness(5,5),
                Content = new Label 
                { 
                    Text = name,
                    TextColor = Color.White,
                    FontSize = Device.GetNamedSize(NamedSize.Medium, typeof(ListView))
                }
            };
            this.View = pad;
    
  • Hi Ken,

    Unfortunately, that isn't the issue. What I am trying to figure out is why the BindingContext for the ViewCell is not a ClientResult. 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.

    This should not be the case because I specifically call this line:

    list.SetBinding(ListView.ItemsSourceProperty, "Clients");

    Clients is defined in my SelectClientViewModel class as:

        private ClientResult[] _Clients;
        public ClientResult[] Clients { get { return _Clients; } }
    

    So I am setting the ItemSource of the ListView to an array of ClientResult. Therefore, the template for the ListView should be receiving an instance of ClientResult as it's BindingContext.

    That isn't happening in UWP, and I'm trying to figure out if there is a bug or if I am doing something wrong.

  • kentuckerkentucker USMember ✭✭✭✭✭

    I would not use an array for binding to use an ObservableCollection instead

  • Well, you should be able to bind to any collection type.. Anyways, I tried switching to ObservableCollection, and that did not work either.

    Since the binding code works in iOS and Android, I believe this is a bug in the ListView implementation in Windows.

  • JohnOsbornJohnOsborn USMember ✭✭
    edited February 2017

    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.

  • DavidOrtinauDavidOrtinau USForum Administrator, Xamarin Team, Insider, University Xamurai

    I've opened a bug report on this. Can someone please provide a simple reproduction project for this?

    https://bugzilla.xamarin.com/show_bug.cgi?id=52841

  • DavidOrtinauDavidOrtinau USForum Administrator, Xamarin Team, Insider, University Xamurai

    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 that 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

  • JohnOsbornJohnOsborn USMember ✭✭

    It does not work with bindings either. There was a similar Bugzilla report for the built-in cells that was closed.

  • MarshallJMarshallJ USMember ✭✭

    @LinusConcepcion.9230 said:
    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.

    Thanks.

    I had this exact issue, change your code for OnBindingContextChanged to this:

    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;
        }
    }
    
  • @DavidOrtinau Has there been any progress? Still experiencing it in current version.

Sign In or Register to comment.