question

DmitryBarabash-9334 avatar image
0 Votes"
DmitryBarabash-9334 asked JessieZhang-2116 commented

not populating ListView using mvvm approach

Hello everyone. I need help. I am trying to populate ListView with data from API using mvvm approach.
here is my model :

 public class MakeUp
 {
     public int Id { get; set; }
     public string Brand { get; set; }
     public string Name { get; set; }
     public string Price { get; set; }
     public object PriceSign { get; set; }
     public object Currency { get; set; }
     public string ImageLink { get; set; }
     public string ProductLink { get; set; }
     public string WebsiteLink { get; set; }
     public string Description { get; set; }
     public double? Rating { get; set; }
     public string Category { get; set; }
     public string ProductType { get; set; }
     public IList<object> TagList { get; set; }
     public DateTime CreatedAt { get; set; }
     public DateTime UpdatedAt { get; set; }
     public string ProductApiUrl { get; set; }
     public string ApiFeaturedImage { get; set; }
     public IList<ProductColor> ProductColors { get; set; }
 }

 public class ProductColor
 {
     public string HexValue { get; set; }
     public string ColourName { get; set; }
 }

this is how i get the API data
interface

 [Headers("Content-Type: application/json")]
 public interface IMakeUpApi
 {
     [Get("/api/v1/products.json?brand={brand}")]
     Task<List<MakeUp>> GetMakeUps(string brand);

     [Post("/api/v1/addMakeUp")]
     Task<MakeUp> CreateMakeUp([Body] MakeUp makeUp, [Header("Authorization")] string token);
 }

modelview

     private string _name;
     private string _price;
     private double _rating;
     private string _category;
     private ObservableCollection<MakeUp> _makeupList;

     #region--prop
     public ObservableCollection<MakeUp> MakeUpList
     {
         get { return _makeupList; }
         set
         {
             _makeupList = value;
             SetProperty(ref _makeupList, value);
         }
     }
     public string Name
     {
         get { return _name; }
         set
         {
             _name = value;
             SetProperty(ref _name,value);
         }
     }
     public string Price
     {
         get { return _price; }
         set
         {
             _price = value;
             SetProperty(ref _price,value);
         }
     }
     public double Rating
     {
         get { return _rating; }
         set
         {
             _rating = value;
             SetProperty(ref _rating,value);
         }
     }
     public string Category
     {
         get { return _category; }
         set
         {
             _category = value;
             SetProperty(ref _category,value);
         }
     }
     #endregion

     #region--ctor

     public MainPageViewModel(INavigationService navigationService, IUserDialogs userDialogs) :base(navigationService,userDialogs)
     {
         _navigationService = navigationService;
         _userDialogs = userDialogs;
         Title = "Main Page"; 
         CallApi();
     }
     #endregion

     public async void CallApi()
     {
         var apiResponse = RestService.For<IMakeUpApi>("http://makeup-api.herokuapp.com");
         var product = await apiResponse.GetMakeUps("maybelline");
         MakeUpList = new ObservableCollection<MakeUp>(product);
     }

xaml View

     <StackLayout VerticalOptions="Center">
        
     <ListView 
         x:Name="CosmeticList"
         ItemsSource="{Binding MakeUpList }">
         <ListView.ItemTemplate>
             <DataTemplate>
                    
                 <ViewCell>
                     <ViewCell.View>

                         <StackLayout>
                             <Image 
                                 WidthRequest="300"
                                 HeightRequest="250"/>
                             <StackLayout>
                                 <Label
                                     Text="{Binding Brand}"
                                     FontAttributes="Bold"
                                     />
                                 <Label
                                     Text="{Binding Category}"
                                     />
                                 <Label
                                     Text="{Binding Name}"
                                     />
                                 <Label
                                     Text="{Binding Price}"
                                     FontAttributes="Bold"
                                     />
                             </StackLayout>
                                    
                         </StackLayout>
                     </ViewCell.View>
                 </ViewCell>

             </DataTemplate>
         </ListView.ItemTemplate>
     </ListView>

 </StackLayout>

I see that the collection is filling (54 elements), but what is wrong with my View? Help plz

dotnet-xamarinforms
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

JessieZhang-2116 avatar image
0 Votes"
JessieZhang-2116 answered JessieZhang-2116 commented

Hello,


Welcome to our Microsoft Q&A platform!

I see that the collection is filling (54 elements)

Hi @DmitryBarabash-9334 , do you mean you have got the list value for MakeUpList ,right?

Then have you set the BindingContext for your page(e.g. YourPage )? For example:

      public YourPage()
  {
            InitializeComponent();      
           BindingContext =  new MainPageViewModel();
           }

Update:

I found you used Prism.Mvvm in your app. In this condition, you don't need to set the BindingContext explicitly, because your app have done this when your app

RegisterForNavigation for your page.

          protected override void RegisterTypes(IContainerRegistry containerRegistry)
     {
         containerRegistry.RegisterForNavigation<NavigationPage>();
         containerRegistry.RegisterForNavigation<SignInPage, SignInPageViewModel>();
         containerRegistry.RegisterForNavigation<SignUpPage, SignUpPageViewModel>();
         containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();

      // other code

     }

And in your xaml, you defined an Image with height 250 , and you don't set property HasUnevenRows="True" for your listview , just as follows:

                                <Image 
                              WidthRequest="300"
                              HeightRequest="250"/>

So, your listview item will not show the other labels.

You can try to set the property HasUnevenRows="True" for your listview. And remove the Image if you don't need it.

 <ListView 
      x:Name="CosmeticList"
      ItemsSource="{Binding MakeUpList }" HasUnevenRows="True">
         <ListView.ItemTemplate >
             <DataTemplate>
    
                 <ViewCell >
                     <ViewCell.View>
                         <StackLayout>
                             <!--<Image 
                              WidthRequest="300"
                              HeightRequest="250"/>-->
                             <StackLayout >
                                 <Label
                                  Text="{Binding Brand}"
                                  FontAttributes="Bold"
                                  />
                                 <Label
                                  Text="{Binding Category}"
                                  />
                                 <Label
                                  Text="{Binding Name}"
                                  />
                                 <Label
                                  Text="{Binding Price}"
                                  FontAttributes="Bold"
                                  />
                             </StackLayout>
    
                         </StackLayout>
                     </ViewCell.View>
                 </ViewCell>
             </DataTemplate>
         </ListView.ItemTemplate>
     </ListView>


Best Regards,

Jessie Zhang


If the response is helpful, please click "Accept Answer" and upvote it.


Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.






· 12
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi JessieZhang-2116, thats exactly what i did not set the BindingContext in the constructor...here is my code behind

using Xamarin.Forms;

namespace TestXam
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
}

i could throw the whole project if it helps in my question

0 Votes 0 ·
JessieZhang-2116 avatar image JessieZhang-2116 DmitryBarabash-9334 ·

I found the constructor of MainPageViewModel is as follows,where did you call it? If it is convenient for you, could you please post a basic demo ?

  public MainPageViewModel(INavigationService navigationService, IUserDialogs userDialogs) :base(navigationService,userDialogs)
  {
      _navigationService = navigationService;
      _userDialogs = userDialogs;
      Title = "Main Page"; 
      CallApi();
  }

0 Votes 0 ·

yes of course, here is my base model

public class ViewModelBase : BindableBase, IInitialize, INavigationAware
{
protected INavigationService _navigationService { get; set; }
protected IUserDialogs _userDialogs { get; set; }

     private string _title;
     public string Title
     {
         get { return _title; }
         set { SetProperty(ref _title, value); }
     }

     public ViewModelBase(INavigationService navigationService, IUserDialogs userDialogs)
     {
         _navigationService = navigationService;
         _userDialogs = userDialogs;
     }

     public virtual void Initialize(INavigationParameters parameters) { }

     public virtual void OnNavigatedFrom(INavigationParameters parameters) { }

     public virtual void OnNavigatedTo(INavigationParameters parameters) { }

     public virtual void Destroy() { }
 }
0 Votes 0 ·
JessieZhang-2116 avatar image JessieZhang-2116 DmitryBarabash-9334 ·

Could you please post a basic demo to github or onedriver to that we can help you better?

0 Votes 0 ·

Don't make your API call in the constructor of the ViewModel.

0 Votes 0 ·

Hi jcmanke. fixed like this

public async void CallApi()
{
var apiResponse = RestService.For<IMakeUpApi>("http://makeup-api.herokuapp.com");
var product = await apiResponse.GetMakeUps("maybelline");
MakeUpList = new ObservableCollection<MakeUp>(product);
}
public override void OnNavigatedTo(INavigationParameters parameters)
{
CallApi();
}
judging from this phrase: Called when the page is loaded and becomes the current source of the parent frame - it's like calling a method in a constructor. but still my ListView is not populating with data. and would like to know why the API call in the ViewModel constructor cannot be done

0 Votes 0 ·
jcmanke avatar image jcmanke DmitryBarabash-9334 ·

Constructors are not async so you should not call async code inside them. It can lead to weird race conditions and deadlocks. The constructor finishes executing before the code inside of it does.

I would make CallApi return a Task, make OnNavigatedTo an async void, and await the call there.

0 Votes 0 ·
Show more comments
DmitryBarabash-9334 avatar image
0 Votes"
DmitryBarabash-9334 answered JessieZhang-2116 commented

MakeUpList gets 54 elements. вот скрины
82367-listemulatorr.png



listemulatorr.png (202.4 KiB)
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I did a test based on the simple demo you posted, when I load data in method OnNavigatedTo, it couldn't display the data indeed.But when I put the data in the constructor of MainPageViewModel, it works properly.

        public MainPageViewModel(INavigationService navigationService, IUserDialogs userDialogs) :base(navigationService,userDialogs)
     {
         _navigationService = navigationService;
         _userDialogs = userDialogs;
         Title = "Main Page";

         var datalist = PickerService.GetCities().OrderBy(c => c.Name).ToList();

         MakeUpList = new ObservableCollection<MakeUp>(datalist);
     }
0 Votes 0 ·