Home > Mobile >  Dynamically pass argument to a BindingContext in Xamarin
Dynamically pass argument to a BindingContext in Xamarin

Time:12-19

I need to populate a custom ListView with data from a downloaded JSON.

This is the custom ListView I created:

            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <StackLayout Orientation="Vertical" Spacing="5" VerticalOptions="Center">
                                <StackLayout Orientation="Horizontal">
                                    <Label Text="{Binding TradeDate}"  Padding="25,0,0,0" FontSize="19" TextColor="Black"></Label>
                                    <Label Text="{Binding TradeGain}" Padding="0,0,25,0" FontSize="19" FontAttributes="Bold" TextColor="Green" HorizontalOptions="EndAndExpand"></Label>
                                </StackLayout>
                                <StackLayout Orientation="Horizontal">
                                    <Label Text="{Binding TradeQty}" Padding="25,0,0,0" FontSize="16"></Label>
                                    <Label Text="{Binding TradeEff}" Padding="0,0,25,0" FontSize="16" HorizontalOptions="EndAndExpand"></Label>
                                </StackLayout>
                            </StackLayout>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

Then I set the BindingContext to the class with the ItemSource:

    <ContentPage.BindingContext>
        <local:TradesView>
            <x:Arguments>
                <x:String>cacca</x:String>
            </x:Arguments>
        </local:TradesView>
    </ContentPage.BindingContext>

This is the TradeView class seen here in the BindingContext:

Unfortunately, I can't download the JSON from here since it requires authentication which of course is handled by another ContentPage.

public class TradesView
    {
        public ObservableCollection<Trade> Trades { get; set; }
        public TradesView(string trades)
        {
            Trades = new ObservableCollection<Trade>();
            JArray jArray = JArray.Parse(trades);
            foreach (JObject trade  in jArray){
                Trades.Add(new Trade(trade["date"].ToString(), trade["gain"].ToString(), trade["depth"].ToString(), "0"));
            }
            

        }
    }

The problem is that now I have no idea on how to proceed since there seems no way of passing a variable as an argument to the TradeView constructor which should handle the JSON and populate the ObservableCollection with what I need.

I just think my procedure is wrong and I should scrap a lot of what I did, but then I genuinely have no idea on how to solve this.

CodePudding user response:

There are several ways to solve this. Here is one, which uses a "Factory method" (Create) to create the page that contains your view, then set that page's Items property.

This makes sense if there is only one page that contains that view.

Usage:

var names = new List<string> { "One", "Two", "Three", "Four" };
MainPage = SelfBoundPageWithCollection.Create(names);

SelfBoundPageWithCollection.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TestXFUWP.SelfBoundPageWithCollection">
    <ContentPage.Content>
        <StackLayout>
            <CollectionView ItemsSource="{Binding Items}">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>
                            <Label Text="{Binding Name}" />
                        </StackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

SelfBoundPageWithCollection.xaml.cs:

public partial class SelfBoundPageWithCollection : ContentPage
{
    public SelfBoundPageWithCollection()
    {
        InitializeComponent();
        BindingContext = this;
    }

    public ObservableCollection<ItemModel> Items {
        get => _items;
        set {
            _items = value;
            OnPropertyChanged();
        }
    }
    private ObservableCollection<ItemModel> _items;


    public static SelfBoundPageWithCollection Create(List<string> names)
    {
        var it = new SelfBoundPageWithCollection();
        it.FillItems(names);
        return it;
    }

    // Can call this directly later, to replace Items collection.
    public void FillItems(List<string> names)
    {
        var items = new ObservableCollection<ItemModel>();
        foreach (var name in names) {
            items.Add(new ItemModel(name));
        }
        Items = items;
    }
}

Or to have a separate "ViewModel" (MVVM), do it like this:

Usage:

var names = new List<string> { "One", "Two", "Three", "Four" };
MainPage = PageWithCollection.Create(names);

PageWithCollection.xaml.cs:

public partial class PageWithCollection : ContentPage
{
    public PageWithCollection()
    {
        InitializeComponent();
        BindingContext = new ViewModelWithItems();
    }


    public static PageWithCollection Create(List<string> names)
    {
        var it = new PageWithCollection();

        var vm = (ViewModelWithItems)it.BindingContext;
        vm.FillItems(names);

        return it;
    }
}

ViewModelWithItems.cs:

public class ViewModelWithItems : Xamarin.Forms.BindableObject
{
    public ViewModelWithItems()
    {
    }


    public ObservableCollection<ItemModel> Items {
        get => _items;
        set {
            _items = value;
            OnPropertyChanged();
        }
    }
    private ObservableCollection<ItemModel> _items;


    public void FillItems(List<string> names)
    {
        var items = new ObservableCollection<ItemModel>();
        foreach (var name in names) {
            items.Add(new ItemModel(name));
        }
        Items = items;
    }
}

Both of the above refer to this model:

ItemModel.cs:

public class ItemModel
{
    public ItemModel(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}
  • Related