You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms MVVM架构下ViewModel与API调用及ListView绑定问题排查

Fixing JSON Parsing, Data Binding, and ListView Population in Xamarin Forms MVVM

Hey there, let's break down the issues in your current implementation and fix them one by one to get your product list working correctly.

Key Issues in Your Current Code

  • Model Mismatch: Your CarouselModel and CarouselData classes don't match the structure of the JSON response you shared. The JSON's data object contains enableNext and products, not a list of CarouselData items.
  • Deadlock Risk from Sync Async Call: Using .Result in the ViewModel constructor can cause deadlocks in Xamarin apps. Always use async/await properly.
  • Uninitialized Collection: Your Items list isn't initialized, so calling Items.Add() will throw a NullReferenceException.
  • No ViewModel Binding: Your page doesn't set the BindingContext to the ViewModel, so the ListView can't access the data.
  • Hardcoded ListView Template: The TextCell uses static text instead of binding to your product properties.

Corrected Implementation

Step 1: Update Model Classes to Match JSON Structure

First, let's fix the models to align with the JSON response you shared:

using Newtonsoft.Json;

namespace CameliaMaison.Models
{
    public class ApiResponse
    {
        [JsonProperty("status")]
        public long Status { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

        [JsonProperty("data")]
        public ProductData Data { get; set; }
    }

    public class ProductData
    {
        [JsonProperty("enableNext")]
        public bool EnableNext { get; set; }

        [JsonProperty("products")]
        public List<Product> Products { get; set; }
    }

    public class Product
    {
        [JsonProperty("image")]
        public string Image { get; set; }

        [JsonProperty("id")]
        public long Id { get; set; }

        [JsonProperty("code")]
        public string Code { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("description")]
        public string Description { get; set; }

        [JsonProperty("tags")]
        public string Tags { get; set; }

        [JsonProperty("price")]
        public string Price { get; set; }

        [JsonProperty("quantity")]
        public long Quantity { get; set; }

        [JsonProperty("images")]
        public List<string> Images { get; set; }
    }
}

Step 2: Fix ViewModel with Proper Async Handling and ObservableCollection

Use ObservableCollection instead of List so the UI automatically updates when data changes. Also, avoid blocking calls in the constructor:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using CameliaMaison.Models;
using Newtonsoft.Json;

namespace CameliaMaison.ViewModels
{
    public class ProductsListViewModel : BaseViewModel
    {
        private ObservableCollection<Product> _products;
        public ObservableCollection<Product> Products
        {
            get => _products;
            set
            {
                _products = value;
                OnPropertyChanged();
            }
        }

        public ICommand LoadProductsCommand { get; }

        public ProductsListViewModel()
        {
            _products = new ObservableCollection<Product>();
            LoadProductsCommand = new Command(async () => await LoadProductsAsync());
        }

        private async Task LoadProductsAsync()
        {
            try
            {
                var apiResponse = await MyHTTP.GetProductsAsync();
                if (apiResponse?.Data?.Products != null)
                {
                    Products.Clear();
                    foreach (var product in apiResponse.Data.Products)
                    {
                        Products.Add(product);
                    }
                }
            }
            catch (Exception ex)
            {
                // Add error handling here, e.g., show an alert to the user
                await Application.Current.MainPage.DisplayAlert("Error", $"Failed to load products: {ex.Message}", "OK");
            }
        }
    }

    public static class MyHTTP
    {
        public static async Task<ApiResponse> GetProductsAsync()
        {
            using (var httpClient = new HttpClient())
            {
                var response = await httpClient.GetAsync("http://bresa.lazyhost.in/api/banners");
                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsStringAsync();
                return JsonConvert.DeserializeObject<ApiResponse>(content);
            }
        }
    }

    // Base ViewModel to handle property change notifications
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Step 3: Update Page to Bind ViewModel and ListView

Update the XAML code to bind the ViewModel and display product data:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="clr-namespace:CameliaMaison.ViewModels"
             x:Class="CameliaMaison.Views.ProductsListPage"
             Title="Product List">
    <ContentPage.BindingContext>
        <vm:ProductsListViewModel />
    </ContentPage.BindingContext>

    <ListView ItemsSource="{Binding Products}"
              IsPullToRefreshEnabled="True"
              RefreshCommand="{Binding LoadProductsCommand}"
              Padding="5">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Orientation="Horizontal" Padding="10" Spacing="8">
                        <Image Source="{Binding Image}" WidthRequest="80" HeightRequest="80" Aspect="AspectFill" />
                        <StackLayout VerticalOptions="Center">
                            <Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
                            <Label Text="{Binding Price}" FontSize="Small" TextColor="#2ECC71" />
                            <Label Text="{Binding Description}" FontSize="Small" LineBreakMode="TailTruncation" TextColor="#666" />
                        </StackLayout>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Update the code-behind to trigger data loading when the page appears:

using Xamarin.Forms;
using CameliaMaison.ViewModels;

namespace CameliaMaison.Views
{
    public partial class ProductsListPage : ContentPage
    {
        private ProductsListViewModel _viewModel;

        public ProductsListPage()
        {
            InitializeComponent();
            _viewModel = (ProductsListViewModel)BindingContext;
        }

        protected override async void OnAppearing()
        {
            base.OnAppearing();
            await _viewModel.LoadProductsAsync();
        }
    }
}

Additional Notes

  • Error Handling: We added a try/catch block in LoadProductsAsync to handle network issues gracefully—you can expand this to suit your app's needs.
  • HttpClient Disposal: Using using with HttpClient ensures resources are properly cleaned up.
  • ObservableCollection: This collection type automatically notifies the UI of changes, which is critical for MVVM data binding.
  • Endpoint Check: Double-check that your API endpoint (http://bresa.lazyhost.in/api/banners) actually returns the product JSON you shared. If it's meant for banners, you'll need to switch to the correct product endpoint.

内容的提问来源于stack exchange,提问作者govind kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:52:45