Can't XLabs GridView to update binding

ChaseFlorellChaseFlorell Chase FlorellCA mod
edited January 2015 in Xamarin.Forms

I've just started getting back into Forms work again, and I have another need for a GridView. I have some "old code" that I could use that builds a Grid dynamically, but it's quite slow, and I'd rather try out the XLabs stuff.

My View and ViewModel are pretty basic.

XAML

  <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
    <!-- Binding a ListView is fine -->
    <ListView HorizontalOptions="FillAndExpand" VerticalOptions="Start" ItemsSource="{Binding Tasks}">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <Label Text="{Binding Name}" FontSize="Large"></Label>
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>

    <!-- Binding the GridView is borked -->
    <controls:GridView
        HorizontalOptions="FillAndExpand"
        VerticalOptions="Start"
        x:Name="GrdView"
        RowSpacing="5"
        Padding="5"
        ColumnSpacing="5"
        ItemWidth="400"
        ItemHeight="300"
        ItemsSource="{Binding Tasks}">
      <controls:GridView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <Label Text="{Binding Name}"  FontSize="Large" />
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </controls:GridView.ItemTemplate>
    </controls:GridView>
  </StackLayout>

Code Behind

public partial class TaskPage
{
    private readonly IProjectService _projectService;
    private readonly MenuItemViewModel _menuItem;

    public TaskPage(IProjectService projectService, MenuItemViewModel menuItem)
    {
        _projectService = projectService;
        _menuItem = menuItem;

        InitializeComponent();

        this.GrdView.ItemSelected += (sender, e) =>
        {
            DisplayAlert("selected value", e.Value.ToString(), "ok");
        };

    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();
        var project = await _projectService.GetByIdAsync(_menuItem.Id);
        ((TaskPageViewModel)BindingContext).Project = project;
    }
}

View Model

public class TaskPageViewModel : BindableViewModelBase
{
    public TaskPageViewModel()
    {
        Tasks = new ObservableCollection<TaskModel> {new TaskModel {Name = "foo"}}; // NOTICE THE INITIAL FOO (for testing)
        Project = new ProjectViewModel { Tasks = new List<TaskModel>() };
    }

    public ProjectViewModel Project
    {
        set
        {
            if (!value.Tasks.Any()) return;
            foreach (var task in value.Tasks)
            {
                Tasks.Add(task);
            }
            // yes, I realize this isn't right, but I was getting desperate.
            OnPropertyChanged(TasksPropertyName);
        }
    }

    public string TasksPropertyName = @"Tasks";

    public ObservableCollection<TaskModel> Tasks { get; private set; }
}

The problem I'm having is that (as you can see from the screenshot), the Grid doesn't bind like the ListView does.

Anyone have any thoughts? What am I missing?

note: if I rotate the device, the second Grid tile appears.

Best Answer

Answers

  • chris_riesgochris_riesgo Chris Riesgo US ✭✭✭

    @ChaseFlorell‌ - The GridViewRenderer in XLabs seems hosed when I try to use it in a PCL project (v.1.2.0 - 1.2.1). Just scanning that source code, you can see that it's not quite done because the background color is being set to Blue :)

    When I create my own GridView Control + Renderer (based on the exact code that XLabs has for that control), things work better, but I haven't dug in to see why, exactly. I'm attaching a little sample that has some ideas that might help you over the hump for now.

  • ChaseFlorellChaseFlorell Chase Florell CA mod

    Hey @chris_riesgo, In taking a look at your example (thank you very much by the way), it appears as though you're using custom Layout stuff on the android side instead of using a XAML template, is that correct? Did you have issues trying to use the template stuff?

  • chris_riesgochris_riesgo Chris Riesgo US ✭✭✭

    Hey, @ChaseFlorell‌. I did try with XAML first. Then i decided to try with C# Forms layouts just to see if the XAML bits were screwy. Appeared that the binding didn't work either way. That's when i added custom Android layout bits (axml + custom renderer).

  • ChaseFlorellChaseFlorell Chase Florell CA mod

    ah, gotcha.
    Well, it was a lot of messing around, and even though I really want to get it working, I went a different direction simply because I needed it working NOW.

    My GridView isn't totally bindable, but I can bind OnAppearing which is good enough for my needs. For reference, here's what I did.

    First I made my Grid object

    public class TempGridView : Grid
    {
        public Type TileTemplate { get; set; }
    
        public async void BuildTiles<T>(IEnumerable<T> tiles)
        {
            var enumerable = tiles as IList<T> ?? tiles.ToList();
    
            var numberOfRows = Math.Ceiling(enumerable.Count / 2f);
            for (var i = 0; i < numberOfRows; i++)
            {
                RowDefinitions.Add(new RowDefinition { Height = 120 });
            }
    
            for (var index = 0; index < enumerable.Count; index++)
            {
                var column = index % 2;
                var row = (int)Math.Floor(index / 2f);
    
                var tile = BuildTile(enumerable[index]);
                Children.Add(tile, column, row);
                await tile.FadeTo(1, 100, Easing.SpringOut);
            }
        }
    
        private View BuildTile(object item)
        {
            return (View)Activator.CreateInstance(TileTemplate, item);
        }
    }
    

    As you can see, I have a TileTemplate property, it's simply a class off of ContentView

    // this is your custom view
    // I'd like to figure out how to do this in Xaml as well... maybe another day.
    public class TaskTileTemplate : ContentView
    {
        private readonly Constraint _centerX = Constraint.RelativeToParent(parent => parent.Width / 2);
        private readonly Constraint _centerY = Constraint.RelativeToParent(parent => parent.Height / 2);
    
        public TaskTileTemplate(object item)
        {
            BindingContext = item;
    
            var innerLayout = new RelativeLayout { WidthRequest = int.MaxValue, HeightRequest = int.MaxValue };
    
            var refLabel = new Label { FontSize = 20, Opacity = 0 };
            var lbl = new Label { FontSize = 20 };
            var refActivityIndicator = new ActivityIndicator { IsRunning = false, IsVisible = false };
            var activityIndicator = new ActivityIndicator { IsRunning = false, IsVisible = false };
    
            // reference label exists becase we don't know the length of 'Name'
            refLabel.SetBinding(Label.TextProperty, "Name");
            lbl.SetBinding(Label.TextProperty, "Name");
            activityIndicator.SetBinding(IsVisibleProperty, "IsBusy");
            activityIndicator.SetBinding(ActivityIndicator.IsRunningProperty, "IsBusy");
    
            innerLayout.Children.Add(refLabel, _centerX, _centerY);
            innerLayout.Children.Add(lbl,
                Constraint.RelativeToView(refLabel, (parent, sibling) => sibling.X - sibling.Width / 2), // X
                Constraint.RelativeToView(refLabel, (parent, sibling) => sibling.Y - sibling.Height / 2)); //Y
    
            innerLayout.Children.Add(refActivityIndicator, _centerX, _centerY);
            innerLayout.Children.Add(activityIndicator,
                Constraint.RelativeToView(refActivityIndicator, (parent, sibling) => sibling.X - sibling.Width / 2), // X
                Constraint.RelativeToView(refActivityIndicator, (parent, sibling) => sibling.Y - sibling.Height / 2)); //Y
    
            var tile = new ContentView
            {
                Content = innerLayout,
                VerticalOptions = LayoutOptions.FillAndExpand
            };
    
            tile.SetBinding(BackgroundColorProperty, "Color", BindingMode.OneWay, new HexToColorConverter());
    
            var tgr = new TapGestureRecognizer();
            tgr.SetBinding(TapGestureRecognizer.CommandProperty, "StartTaskCommand");
            tgr.SetBinding(TapGestureRecognizer.CommandParameterProperty, ".");
    
            tile.GestureRecognizers.Add(tgr);
            Content = tile;
        }
    }
    

    Using these two bits, my Xaml is pretty clean

    <controls:TempGridView
        HorizontalOptions="FillAndExpand"
        VerticalOptions="FillAndExpand"
        x:Name="GrdView"
        RowSpacing="10"
        ColumnSpacing="10"
        Padding="5">
      <controls:TempGridView.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
      </controls:TempGridView.ColumnDefinitions>
    </controls:TempGridView>
    

    And so is my Xaml.cs

    public partial class TaskPage
    {
        private readonly MenuItemViewModel _menuItem;
        private readonly IProjectService _projectService;
    
        public TaskPage(IProjectService projectService, MenuItemViewModel menuItem)
        {
            _projectService = projectService;
            _menuItem = menuItem;
    
            InitializeComponent();
    
            GrdView.TileTemplate = typeof (TaskTileTemplate);
        }
    
        protected override async void OnAppearing()
        {
            base.OnAppearing();
            var project = await _projectService.GetByIdAsync(_menuItem.Id);
            ((TaskPageViewModel) BindingContext).Project = project;
            GrdView.BuildTiles(((TaskPageViewModel) BindingContext).Tasks);
        }
    }
    

    This is meeting my needs for now. @chris_riesgo‌, I'm definitely going to come back to your example when I have a little more time.

  • DarrellBookerDarrellBooker Darrell Booker US

    @chris_riesgo thanks for your solution! They really need to get the Android GridView working properly.

  • chris_riesgochris_riesgo Chris Riesgo US ✭✭✭

    @DarrellBooker - Glad that helped! Hopefully all of the bits will be in place for the guys to finish a nice Android GridView solution for Forms.

  • MikeDennisMikeDennis Scorpian 1990 US

    @chris_riesgo is there any sample for Grid view which is done with PCL project without using the platform specific assemblies?
    i am trying to add a bindable gridview for an UWP app, have all the elements on the PCL project

Sign In or Register to comment.