XAML .NET MAUI ListView - ObservableCollection -异步方法期间未更新

6fe3ivhb  于 2023-08-01  发布在  .NET
关注(0)|答案(1)|浏览(132)

bounty还有7天到期。回答此问题可获得+50声望奖励。CompSciFly吸引更多的注意力这个问题:寻找一个完整的答案,直接解决我的问题。

我已经尝试了几种不同的方法来做到这一点,但还没有完全弄清楚的组合,让这工作的预期。这是在.NET MAUI中使用.NET 6。基本上,UI不会更新/显示ListView行和值。我发现,如果在调试过程中更改.xaml(使用热重载过程),ListView的值就会神奇地出现。因此,我确信UI没有被通知ObservableCollection已经被更改。当在我的UI - MainPage.xaml中单击GetList按钮时,将调用一个异步方法从我的API中获取数据,并使用API返回的数据填充可观察集合。下面是必要的代码:
更新:奇怪的是,将<Label Text="{Binding MyObvList.Count}" />添加到XAML中会导致UI刷新,成功显示列表及其行/变量。然而,当这个标签被移除时,它又回到了老样子。所以,我进一步相信这是一个更新的问题。
下面是我的XAML(MainPage.xaml):

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:mct="clr-namespace:CommunityToolkit.Maui.Views;assembly=CommunityToolkit.Maui"
             x:Class="MyApp.MainPage"
             x:Name="PageRoot"
             Title="My App">

    <VerticalStackLayout Spacing="25" Padding="30,0" VerticalOptions="Center">
            <Label Text="My App"
               SemanticProperties.HeadingLevel="Level2"
               FontSize="18"
               HorizontalOptions="Center" />
            <Button Text="Get List" Clicked="GetList"/>

            <StackLayout Orientation="Horizontal">
                <Label Text="Number" WidthRequest="100" />
                <Label Text="Name" WidthRequest="100" />
                <Label Text="Info" WidthRequest="100" />
            </StackLayout>

            <ListView ItemsSource="{Binding MyObvList}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal">
                                <Label Text="{Binding Number}" WidthRequest="100" />
                                <Label Text="{Binding Name}" WidthRequest="100" />
                                <Label Text="{Binding Info}" WidthRequest="300" />
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
     </VerticalStackLayout>
</ContentPage>

字符串
代码隐藏(MainPage.xaml.cs):

namespace MyApp;

public partial class MainPage : ContentPage, INotifyPropertyChanged 
{
    private ObservableCollection<MyData> _myObvList = new();
    public ObservableCollection<MyData> MyObvList {
        get { return _myObvList; }
        set {
            if(_myObvList != value) {
                _myObvList = value;
                OnPropertyChanged(nameof(MyObvList));
            }
        }
    }
    public MainPage()
    {
        InitializeComponent();        
        BindingContext = this;
        NavigationPage.SetHasNavigationBar(this, false);
        NavigationPage.SetHasBackButton(this, false);
    }
    async void GetList(object sender, EventArgs e)
    {
        try {
            List<MyAPIData> apiData = await MyApi.GetDataAsync();
            MainThread.BeginInvokeOnMainThread(() => {
                MyObvList.Clear();
                if(apiData != null && apiData.Count > 0) {
                   foreach(MyAPIData data in apiData) {
                      MyData myNewData = new();
                      myNewData.Name = = data.Person.FirstName + " " + (string.IsNullOrEmpty(data.Person.MiddleName) ? "" : data.Person.MiddleName + " ") + data.Person.LastName + (string.IsNullOrEmpty(data.Person.Suffix) ? "" : " " + data.Person.Suffix);
                      myNewData.Number = data.Number;
                      myNewData.Info = data.Info;
                      
                      MyObvList.Add(myNewData);
                   }
                }
            }
        } catch(Exception ex) {
            Debug.WriteLine($"An error occurred: {ex.Message}");
        }
    }
}


下面是MyData.cs类:

public class MyData : INotifyPropertyChanged {
    private string number { get; set; }
    public string Number {
        get { return number; }
        set {
            if(number != value) {
                number = value;
                OnPropertyChanged(nameof(Number));
            }
        }
    }
    private string name { get; set; }
    public string Name {
        get { return name; }
        set {
            if(name != value) {
                name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }
    private string info { get; set; }
    public string Info {
        get { return info; } 
        set { 
            if(info != value)  { 
                info = value; 
                OnPropertyChanged(nameof(Info)); 
            } 
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}


此外,当我下一次点击按钮插入<Label Text="{Binding MyObvList.Count}" />时,我会得到“Object reference not set to an instance of an object.”错误,因为它试图执行MyObvList.Clear();。我已经看到了各种建议的解决方案,例如将对象设置为新的ObservableCollection<MyData>while(MyObvList.Any()) { MyObvList.RemoveAt(0); },但是,我没有任何运气,未能让列表第二次填充。我尝试切换到MVVM模型,但遇到了与上面相同的问题。所以,这里有更大的事情发生。需要解决方案。

cpjpxq1n

cpjpxq1n1#

我看不出代码有什么问题。
作为解决方法,在添加所有数据后,请执行以下操作:

OnPropertyChanged(nameof(MyObvList));

字符串
我还建议在后台线程上获取数据:

List<MyAPIData> apiData;
// "await" needed, so that code later in method doesn't run until Task.Run finishes.
await Task.Run(async () =>
{
    apiData = await MyApi.GetDataAsync();
});
// NOTE: Might not need BeginInvoke here. Try it both with and without.
MainThread.BeginInvokeOnMainThread(() => {
    MyObvList.Clear();
    ...
    OnPropertyChanged(nameof(MyObvList));   // ADDED
});


在GetDataAsync运行时,按钮单击方法可能不喜欢延迟。如果上面的方法不起作用,可以这样尝试:

// *queue* to run after Button click method returns.
MainThread.BeginInvokeOnMainThread(() => {
    List<MyAPIData> apiData;
    await Task.Run(async () =>
    {
        apiData = await MyApi.GetDataAsync();
    });
    MyObvList.Clear();
    ...
    OnPropertyChanged(nameof(MyObvList));   // ADDED
});

相关问题