【问题标题】:CollectionView doesn't update unless I logout user除非我注销用户,否则 CollectionView 不会更新
【发布时间】:2021-08-23 15:31:20
【问题描述】:

您好,我正在使用如下所示的 firebase 实时数据库构建 Xamarin 应用程序:

我设法通过使用 useremail 作为键创建了一个显示用户所有宠物的集合视图。但是我遇到了问题。如果我在 addPetPage 上的数据库中添加一个新宠物并返回到带有 collectionView 的页面,它不会更新集合视图,除非我注销用户然后重新登录。为什么会这样?

这是我的 ViewModel 代码

class MyIDPageViewModel : INotifyPropertyChanged
{
    FirebaseHelper firebaseHelper = new FirebaseHelper();

    public IList<PetProfile> source;
    public ObservableCollection<PetProfile> PetInfo { get; private set; }
    public IList<PetProfile> EmptyPetInfo
    {
        get => source;
        private set
        {
            if (value != source)
            {
                source = value;
                OnPropertyChanged(nameof(EmptyPetInfo));
            }
        }
    }

    public MyIDPageViewModel()
    {
        source = new List<PetProfile>();
        CreatePetProfileCollection();
    }
   
    private async void CreatePetProfileCollection()
    {
        var petProfiles = await firebaseHelper.GetAllUserPetInfos();
        if (petProfiles != null)
        {
            EmptyPetInfo = new ObservableCollection<PetProfile>();
            foreach (var groupitems in petProfiles)
            {
                EmptyPetInfo.Add(new PetProfile() { PetName = groupitems.PetName, UserEmail = groupitems.UserEmail, Breed = groupitems.Breed, DOB = groupitems.DOB, Gender = groupitems.Gender, Weight = groupitems.Weight, CareInformation = groupitems.CareInformation });
            }
        }
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

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

ID xamlPage:

<?xml version="1.0" encoding="utf-8" ?>

<ContentPage.BindingContext>
    <local:MyIDPageViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
    <StackLayout>
        <Label 
               Text="My Id"
               VerticalOptions="Start" 
               HorizontalOptions="Start" 
               Padding="15"
               FontSize="Title"
               FontAttributes="Bold"
               TextColor="Black"/>
        <Label Text="Name:" FontSize="Subtitle" Margin="15,0" FontAttributes="Bold"/>
        <Label x:Name="UserName" FontSize="Medium" Margin="15,0"/>
        <Label Text="Email Address:" FontSize="Subtitle" Margin="15,0" FontAttributes="Bold"/>
        <Label x:Name="UserEmail" FontSize="Medium" Margin="15,0"/>
        <Label Text="Phone Number:" FontSize="Subtitle" Margin="15,0" FontAttributes="Bold"/>
        <Label x:Name="UserPhoneNumber" FontSize="Medium" Margin="15,0"/>
        <Label Text="Address:" FontSize="Subtitle" Margin="15,0" FontAttributes="Bold"/>
        <Label x:Name="UserAddress" FontSize="Medium" Margin="15,0"/>

        <Label Text="My Pets:" FontSize="Large" Margin="15,0" FontAttributes="Bold"/>
        <CollectionView  ItemsSource="{Binding EmptyPetInfo}" >
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <Grid Padding="10">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Label Grid.Column="0"
                               Grid.Row="0"
                   Text="{Binding PetName}"
                   FontAttributes="Bold" />
                        <Label Grid.Row="0"
                   Grid.Column="2"
                   Text="View Profile"
                   FontAttributes="Italic"
                   VerticalOptions="End" />
                        
                        <Label Grid.Column="1"
                               Grid.Row="0"
                   Text="{Binding PetName}"
                   FontAttributes="Bold" />
                        <Label Grid.Row="0"
                   Grid.Column="2"
                   Text="View Profile"
                   FontAttributes="Italic"
                   VerticalOptions="End" />
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>

        <Label Text="+ Add pet" HorizontalOptions="Center" FontAttributes="Bold">
            <Label.GestureRecognizers>
                <TapGestureRecognizer Tapped="AddaPet_Tapped"/>
            </Label.GestureRecognizers>
        </Label>
        <Button Text="Edit" HorizontalOptions="End" Margin="15" Clicked="Edit_Clicked"/>

    </StackLayout>
</ContentPage.Content>

FirebaseHelper:

public async Task<List<PetProfile>> GetAllUserPetInfos()
    {
        var useremail = Preferences.Get("UserSignInEmail", "");

        var PetProfiles = await GetAllPetInfos();
        await firebase
          .Child("PetProfiles")
          .OnceAsync<PetProfile>();
        return PetProfiles.Where(a => a.UserEmail == useremail).ToList();
       
    }

将宠物添加到数据库代码

 public async Task AddPetInfo(string petname, string breed, string dob, string weight, string gender, string careinformation)
    {
        var useremail = Preferences.Get("UserSignInEmail", "");                   
            
        await firebase        
          .Child("petProfiles")
          .PostAsync(new PetProfile() { UserEmail = useremail, PetName = petname, Breed = breed, DOB = dob, Weight = weight, Gender = gender, CareInformation = careinformation });              
    }

【问题讨论】:

  • 有趣.. 你是如何运行这个程序的?你能调试它吗?您能否确定当您“返回页面”时是否调用构造函数MyIDPageViewModel,您能否澄清返回页面的含义?
  • 你要绑定什么?空宠物信息?然后在 CreatePetProfileCollection 结束时,您需要引发 PropertyChanged 事件,因为 EmptyPetInfo 是一个列表,并且在添加项目时不会通知任何人。
  • 在 Visual Studio 上的 android 模拟器上运行它。好吧,在我的 idPage 上,我可以点击 addPetPage。在这个新页面上,我可以添加有关添加到我的实时数据库中的新宠物的详细信息(我已经检查过它们确实出现在数据库中)。但是,如果我从 addpetPage 返回到 idPage,则宠物不会出现在 collectionview 中。我还导航到其他页面,然后返回 idPage 仍然没有更新。但是,如果我停止调试并再次运行它,它将出现。返回页面时如何调用 MyIDPageViewModel?
  • 我已经更新了它绑定的 xaml 页面。你还想要什么其他的 sn-ps?
  • @Caswell 它是不可观察的。我已经编辑了我上面的问题,所以你可以看到它

标签: c# xamarin firebase-realtime-database data-binding uicollectionview


【解决方案1】:

我会将 ViewModel 代码更改为如下所示:

class MyIDPageViewModel : INotifyPropertyChanged
{
    FirebaseHelper firebaseHelper = new FirebaseHelper();

    public ObservableCollection<PetProfile> EmptyPetInfo { get; }
        = new ObservableCollection<PetProfile>();

    public MyIDPageViewModel()
    {
        Task.Run(CreatePetProfileCollection);
    }
   
    private async Task CreatePetProfileCollection()
    {
        var petProfiles = await firebaseHelper.GetAllUserPetInfos();
        if (petProfiles == null)
        {
            return;
        }
        
        foreach (var groupitems in petProfiles)
        {
            EmptyPetInfo.Add(
                new PetProfile() 
                { 
                    PetName = groupitems.PetName,
                    UserEmail = groupitems.UserEmail,
                    Breed = groupitems.Breed,
                    DOB = groupitems.DOB,
                    Gender = groupitems.Gender,
                    Weight = groupitems.Weight,
                    CareInformation = groupitems.CareInformation
                }
            );
        }
    }
  1. 如果您使用的是 ObservableCollection,则对属性使用 RaisePropertyChanged 是没有意义的,只需实例化并重用该实例
  2. 您应该避免使用async void,除非您将它用于事件。即使是我在这里修改过的代码,我也会考虑将其更改为在生命周期方法而不是构造函数中获取项目的模式,因为您只是触发并忘记了Task

如果在滚动列表或触摸 CollectionView 之前项目仍未出现,则需要将用于将项目添加到 ObservableCollection 的代码封装在一段编组回 UI 线程的代码中。由于您使用的是 Xamarin.Forms,因此可以使用 Device.BeginInvokeOnMainThread() 完成此操作。在这种情况下,我会从 foreach 中提取一个方法:

private void AddPetInfos(IEnumerable<PetProfile> petInfos)
{
    foreach (var groupitems in petProfiles)
    {
        EmptyPetInfo.Add(
            new PetProfile() 
            { 
                PetName = groupitems.PetName,
                UserEmail = groupitems.UserEmail,
                Breed = groupitems.Breed,
                DOB = groupitems.DOB,
                Gender = groupitems.Gender,
                Weight = groupitems.Weight,
                CareInformation = groupitems.CareInformation
            }
        );
    }
}

然后包装这个方法:

Device.BeginInvokeOnMainThread(AddPetInfos(petProfiles));

这将确保您在正确的线程上添加项目,以便ObservableCollection 通知正确线程上的视图。

【讨论】:

    猜你喜欢
    • 2021-05-14
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 2012-11-12
    • 1970-01-01
    相关资源
    最近更新 更多