【问题标题】:How to update FlexLayout when ObservableCollection content changesObservableCollection 内容发生变化时如何更新 FlexLayout
【发布时间】:2022-07-13 23:23:35
【问题描述】:

我是 Xamarin 的新手,并且难以将一些获取的对象(在我的例子中为产品)显示为 FlexLayout 中的按钮。

从 API 获取结果并将其解析为对象(产品)工作正常,因为调试表明 ObservableCollection Products 充满了 API 的产品。

我在ProductPage 中放置了一个标签,它显示了apiHelper.Products 列表中的产品数量。此标签会在一段时间后更新并显示正确的产品数量(需要先完成调用和解析)。

由于某种原因,FlexView 内容未更新/“填充”产品。当我启动硬编码的Products 列表时,它在视图初始化时已经包含产品,产品会显示出来,所以FlexView 本身似乎工作正常。

我的 FlexView(放在ProductPage.xaml):

... 
    # This label is updated when the fetch was succesfull!
<Label Grid.Column="1" Text="{ Binding Products.Count, StringFormat='Fetched products: {0}'}" HorizontalOptions="Fill"  BackgroundColor="#FF5959" TextColor="#EEF2FF" HorizontalTextAlignment="Right" VerticalTextAlignment="Center"/>

...

     # The products are not shown when the fetch was successfull
<ScrollView Grid.Row="4">
    <FlexLayout 
    BindableLayout.ItemsSource="{Binding Products}"
    x:Name="ProductsCollection"
    Wrap="Wrap"
    Direction="Row"
    JustifyContent="Center"
    AlignItems="Center"
    AlignContent="Start">
        <BindableLayout.ItemTemplate>
            <DataTemplate>
                <StackLayout Orientation="Horizontal">
                    <Button
                    Text="{Binding button_description}"
                    WidthRequest="100"
                    Margin="10"
                    HorizontalOptions="Center"
                    VerticalOptions="Center"
                    TextColor="Black"
                    BackgroundColor="#EEF2FF"
                    CommandParameter="{Binding .}"
                    Clicked="addProductToOrder"
                />
                </StackLayout>
            </DataTemplate>
        </BindableLayout.ItemTemplate>
    </FlexLayout>
</ScrollView>

ProductPage.xam.cs的构造函数中的相关代码:

public partial class ProductPage : ContentPage
{
  public ObservableCollection<Product> Products = { get; }
  private ApiHelper apiHelper; 

  public ProductPage(Order order) {
    ApiHelper apiHelper= new ApiHelper();
    _ = apiHelper.GetProductsAsync();
    this.Products = apiHelper.Products;
    BindingContext = this;
  }

我的ApiHelper 来获取产品:

  private ObservableCollection<Product> _products = new ObservableCollection<Product> { };
  public ObservableCollection<Product> Products { get => _products; set { _products = value; OnPropertyChanged("Products"); } } 

  public async Task<ObservableCollection<Product>> GetProductsAsync() {
    Uri baseAddr = new Uri("http://my.app/api/products");
    var client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(baseAddr).ConfigureAwait(false);
 
    if (response.IsSuccessStatusCode) {
      Products.Add(JsonConvert.DeserializeObject<ObservableCollection<Product>>(await response.Content.ReadAsStringAsync())[0]);
    } 

    return Products;
  }

  public event PropertyChangedEventHandler PropertyChanged;

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

有趣的是,当我替换该行时: Products.Add(JsonConvert.DeserializeObject&lt;ObservableCollection&lt;Product&gt;&gt;(await response.Content.ReadAsStringAsync())[0]) ApiHelper 和:Products = JsonConvert.DeserializeObject&lt;ObservableCollection&lt;Product&gt;&gt;(await response.Content.ReadAsStringAsync());,标签也不再更新。可能是因为关注列表的NotifyPropertyChanged没有触发吧?

我的问题:

  • 为什么我的FlexView API 调用成功后没有更新/显示产品?
  • 怎么可能Label 已更新但FlexLayout 不是虽然它们依赖于同一个列表?
  • 是否需要使用 Products.add 或其他数组函数来对数组进行突变,或者像 Products = .... 这样的东西也可以工作?

【问题讨论】:

    标签: c# xamarin xamarin.forms inotifypropertychanged


    【解决方案1】:

    除了我看到的一些事情之外,我认为问题的根本原因是在 ProductPage 类中没有触发属性更改事件。最好的设置是:

    1. 将您的 ApiHelper 类更改为仅返回 List 或 IEnumerable:

      public async Task<List<Product>> GetProductsAsync() {
      
          // previous logic to call api...
      
          var products = new List<Product>();
      
          if (response.IsSuccessStatusCode) {
              products = JsonConvert.DeserializeObject<List<Product>>(await response.Content.ReadAsStringAsync())[0]);
          }
      
          return products;
      }
      
    2. 在您的 ProductPage 中,初始化 ObservableCollection 一次,然后仅在该实例中添加和删除:

      public partial class ProductPage : ContentPage
      {
        public ObservableCollection<Product> Products = { get; private set; } = new ObservableCollection<Product>();
        private ApiHelper apiHelper; 
      
        public ProductPage(Order order) {
            ApiHelper apiHelper= new ApiHelper();
            var apiProducts = apiHelper.GetProductsAsync();
            foreach (var product in apiProducts) 
            {
                Products.Add(product);
            }
            BindingContext = this;
        }
       }
      

    我面前没有VS,但语法应该接近准确。

    另外,请注意 GetProducts async 是一个异步调用,这意味着您可能会遇到从 ProductPage ctor 调用它的阻塞问题。您可能希望从可以进行异步的方法中触发调用。一种常见的模式是在 ProductPage 中添加一个“InitAsync”方法,然后在您新建页面后、切换到它之前调用它。比如:

    var productPage = new ProductPage();
    
    await productPage.InitAsync();
    
    NavController.SwitchPage(productPage); // Or whatever
    

    【讨论】:

      猜你喜欢
      • 2013-09-14
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      • 2020-10-13
      • 1970-01-01
      • 2020-11-16
      • 2021-10-23
      • 1970-01-01
      相关资源
      最近更新 更多