ViewModel properties not binding

markmunizmarkmuniz ✭✭USUniversity ✭✭

Hello I have some properties in my viewmodel that I want to bind to a custom control. However, these properties are null once they reach the custom controller. Is there something I am missing?

My ViewModel:

public MyViewModel()
    {
      Tiles = new List<TileInfo>();
      Tiles.Add(new TileInfo() { Id = 1, Title = "Title1" });
      Tiles.Add(new TileInfo() { Id = 2, Title = "Title1" });
      Tiles.Add(new TileInfo() { Id = 3, Title = "Title1" });
      Tiles.Add(new TileInfo() { Id = 4, Title = "Title1" });
      Tiles.Add(new TileInfo() { Id = 5, Title = "Title1" });
      Tiles.Add(new TileInfo() { Id = 6, Title = "Title1" });

      TileColor = Color.Green;
    }

My page containing the control and viewmodel:

<ContentPage.BindingContext>
  <vm:MyViewModel />
</ContentPage.BindingContext>

<ctrl:DynamicTileView Tiles="{Binding Tiles}" TileColor="{Binding TileColor}"/>

And the code for the controller:

public static readonly BindableProperty TilesProperty =
       BindableProperty.Create<DynamicTileView, List<TileInfo>>(c => c.Tiles, new List<TileInfo>());

    public static readonly BindableProperty TileColorProperty =
       BindableProperty.Create<DynamicTileView, Color>(c => c.TileColor, default(Color));

    public List<TileInfo> Tiles
    {
      get
      {
        return (List<TileInfo>)GetValue(TilesProperty);
      }

      set
      {
        SetValue(TilesProperty, value);
      }
    }
    public Color TileColor
    {
      get
      {
        return (Color)GetValue(TileColorProperty);
      }

      set
      {
        SetValue(TileColorProperty, value);
      }
    }

Best Answers

Answers

  • markmunizmarkmuniz ✭✭ USUniversity ✭✭

    The view model properties are:

    public List<TileInfo> Tiles { get; set; }
    
    public Color TileColor { get; set; }
    
    public class TileInfo
    {
        public int Id { get; set; }
    
        public string Title { get; set; }
    }
    
  • adamkempadamkemp mod USInsider, Developer Group Leader mod

    I don't know what you mean by this:

    However, these properties are null once they reach the custom controller.

    What is the "custom controller"? How are you testing this?

  • markmunizmarkmuniz ✭✭ USUniversity ✭✭

    Sorry, meant to say custom control. Which is this:
    <asi:DynamicTileView Tiles="{Binding Tiles}" TileColor="{Binding TileColor}" />

    I want to bind the Tiles and TileColor from my view model to my control to dynamically add to a grid.

    So for example, I add the Tiles to another grid with a method called BuildTiles() but Tiles and TileColor do not contain the values that were set in the view model. The code-behind for my DynamicTileView control is below.

        public Grid BuildTiles()
        {
          var tiledMenu = new Grid
          {
            ColumnSpacing = 1,
            RowSpacing = 1
          };
    
          var rowDef = new RowDefinition { Height = new GridLength(1.0, GridUnitType.Star) };
          var tiles = new Collection<Tuple<Grid, int, int>>();
    
          for (var i = 0; i < Tiles.Count; i++)
          {
            var column = i % 2;
            var row = i / 2;
            tiles.Add(new Tuple<Grid, int, int>(BuildGridElement(Tiles[i]), column, row));
          }
    
          // this has to run on the UI thread to prevent UI Locking.
          Device.BeginInvokeOnMainThread(() =>
          {
            tiledMenu.RowDefinitions.Clear();
            tiledMenu.Children.Clear();
    
            foreach (var tile in tiles)
            {
              if (tile.Item3 % 2 != 0)
              {
                tiledMenu.RowDefinitions.Add(rowDef);
              }
              tiledMenu.Children.Add(tile.Item1, tile.Item2, tile.Item3);
            }
          });
    
          return tiledMenu;
        }
    
        private Grid BuildGridElement(TileInfo value)
        {
          var tileLabel = new Label
          {
            Text = value.Title,
            XAlign = TextAlignment.Center,
            YAlign = TextAlignment.Center,
            BackgroundColor = Color.Transparent,
            TextColor = Color.White
          };
    
          var tile = new Grid
          {
            RowDefinitions =
            {
              new RowDefinition { Height = new GridLength(1.0, GridUnitType.Star) },
              new RowDefinition { Height = new GridLength(1.0, GridUnitType.Star) }
            },
            VerticalOptions = LayoutOptions.FillAndExpand
          };
    
          tile.Children.Add(tileLabel);
    
          return tile;
        }
    
  • adamkempadamkemp mod USInsider, Developer Group Leader mod

    You still were't quite clear about how you're testing this. An example project that reproduces the issue would help a lot.

    I am noticing in your BindableProperty definitions that you don't have a callback for when the property changes. That makes me question whether you understand how this works. Let me ask this: what in your code should cause the BuildTiles method to be called when the Tiles property changes or the collection changes?

  • markmunizmarkmuniz ✭✭ USUniversity ✭✭

    I'm fairly new to Binding and admit I don't fully understand the way it works. I'm calling BuildTiles within my DynamicTileView constructor and setting that to the Content of the view.

    public DynamicTileView()
    {
      Content = BuildTiles();
    }
    

    BuildTiles is not called anywhere else.

  • markmunizmarkmuniz ✭✭ USUniversity ✭✭

    Thanks for the help and suggestions Adam. I was able to get this to finally work by binding an ObservableCollection of strings. But if I try to bind an ObservableCollection of TileInfo objects, I get this output:

    Binding: System.Collections.ObjectModel.ObservableCollection'1[namespace.MyViewModel+TileInfo] can not be converted to type 'System.Collections.ObjectModel.ObservableCollection'1[namespace.DynamicTileView+TileInfo]'

    Any idea why this is?

  • markmunizmarkmuniz ✭✭ USUniversity ✭✭

    Awesome thanks!

Sign In or Register to comment.