XAML 向绑定到.net Maui中集合视图的ObservableCollection〈>添加新项时显示重复项

6qqygrtg  于 2022-12-07  发布在  .NET
关注(0)|答案(1)|浏览(166)

我目前有一个集合视图,它显示从我设备中的本地数据库中提取的记录列表。数据库工作正常,添加记录、删除记录都工作正常。问题是当我添加或删除记录时,当集合视图被刷新时,它显示的是每个现有记录的副本。奇怪的是,如果我再次刷新,它将返回到正常状态,并且只显示从中提取的数据库中的表的记录。
这是我的观点模型:

[QueryProperty(nameof(Players), "Players")]
    public partial class ManagePlayersPageViewModel : ObservableObject
    {
        /// <summary>
        /// List of players being displayed 
        /// </summary>
        private ObservableCollection<Player> _players = new();
        public ObservableCollection<Player> Players
        {
            get => _players;
            set => SetProperty(ref _players, value);
        }

        [ObservableProperty] private bool isRefreshing;

        /// <summary>   
        /// Options for selection modes
        /// </summary>
        public SelectionOptions SelectionOptions { get; } = new();

        /// <summary>
        /// Adds player to list
        /// </summary>
        /// <returns></returns>
        [RelayCommand]
        async Task AddPlayer()
        {
          var task =  await Shell.Current.ShowPopupAsync(new AddPlayerPopup());
          var player = task as Player;

          if (task == null)
              return;

          if (await PlayerService.RecordExists(player))
          {
              await Shell.Current.DisplaySnackbar("Jugador ya existe");
              return;
          }
          
          await PlayerService.AddAsync(player);
          
         
          await Refresh();
        }

下面是refresh()方法:

/// <summary>
        /// Refreshs and updates UI after each database query 
        /// </summary>
        /// <returns></returns>
        [RelayCommand]
        async Task Refresh()
        {
            IsRefreshing = true;
            await Task.Delay(TimeSpan.FromSeconds(1));
            Players.Clear();

            var playersList = await PlayerService.GetPlayersAsync();
           
            foreach (Player player in playersList)
                Players.Add(player);

            IsRefreshing = false;
        }

下面是控件所在的xaml:

<RefreshView Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"
               IsRefreshing="{Binding IsRefreshing}"
               Command="{Binding RefreshCommand}">
                <CollectionView
                    ItemsSource="{Binding Players}"
                    SelectionMode="{Binding SelectionOptions.SelectionMode}">
                    <CollectionView.ItemTemplate>
                        <DataTemplate>
                            <SwipeView>
                                <SwipeView.RightItems>
                                    <SwipeItems>
                                        <SwipeItemView 
                                            Padding="0, 2.5"
                                            Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:ManagePlayersPageViewModel}}, Path= DeletePlayerCommand}"
                                            CommandParameter="{Binding .}">
                                            <Border 
                                                StrokeShape="RoundRectangle 10"
                                                Stroke="{StaticResource DimBlackSolidColorBrush}"
                                                Background="{StaticResource DimBlackSolidColorBrush}">
                                                <Grid>
                                                    <Image
                                                        Source="Resources/Images/delete.svg"
                                                        WidthRequest="35"
                                                        HeightRequest="35"
                                                        Aspect="AspectFill"/>
                                                </Grid>
                                            </Border>
                                        </SwipeItemView>
                                    </SwipeItems>
                                </SwipeView.RightItems>
                                <Grid>
                                <Border Grid.Column="0"
                                        StrokeShape="RoundRectangle 10"
                                        Stroke="{StaticResource DimBlackSolidColorBrush}"
                                        StrokeThickness="3">
                                    <Grid
                                        RowDefinitions="auto, auto, auto"
                                        Background="{StaticResource DimBlackSolidColorBrush}">
                                        <Label Grid.Row="0"
                                               Text="{Binding Name}"
                                               VerticalTextAlignment="Center"
                                               Margin="10, 2.5" 
                                               TextColor="White"/>
                                        <Label Grid.Row="1"
                                               Text="{Binding Alias}"
                                               VerticalTextAlignment="Center"
                                               Margin="10, 2.5" />
                                        <Label Grid.Row="2"
                                               Text="{Binding Team, TargetNullValue=Ninguno}"
                                               VerticalTextAlignment="Center"
                                               FontAttributes="Italic"
                                               Margin="10, 2.5" />
                                    </Grid>
                                </Border>
                                <Grid.GestureRecognizers>
                                    <TapGestureRecognizer 
                                        Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:ManagePlayersPageViewModel}}, Path=ItemTappedCommand}"
                                        CommandParameter="{Binding .}"/>
                                </Grid.GestureRecognizers>
                            </Grid>
                            </SwipeView>
                        </DataTemplate>
                    </CollectionView.ItemTemplate>
                </CollectionView>
           </RefreshView>

知道为什么会发生这种情况吗?注意:数据库在上一页被查询,并作为参数传递到集合视图所在的页,不知道这是否与它有关。注意:它曾经与列表视图控件一起工作得很好,但我没有那么多的灵活性来定制该控件,这就是为什么我要走使用集合视图的路线。
当我调试时,它显示setter中的值已经重复,但我不知道为什么或在哪里重复。只有当我添加或删除记录时才会发生这种情况。任何帮助都是感激的,谢谢!

v440hwme

v440hwme1#

如果在Refresh完成之前再次调用Refresh,则可能会出现此症状。具体来说,如果在await PlayerService.GetPlayersAsync()期间再次调用x1m0 n1,,则Players上的操作序列将是:

  • 调用#1:玩家。清除();
  • call#2:玩家。清除();
  • 呼叫#1:添加所有(现有)玩家
  • 呼叫#2:添加所有玩家(包括新玩家)。

要确定是否发生了这种情况,请添加一个检查:

async Task Refresh()
{
    if (IsRefreshing)
        throw new InvalidProgramException("Nested call to Refresh");
    IsRefreshing = true;
    try
    {
        ... your existing code ...
    }
    finally
    {
        IsRefreshing = false;
    }
}

这是否会引发该异常?
如果是这样,那么在代码后面移动Players.Clear()。这 * 可能 * 会修复它:

var playersList = await PlayerService.GetPlayersAsync();
Players.Clear();

如果这样做还不能解决问题,那么在触及玩家的语句周围添加一个lock,以确保在前一个调用完成之前,一个调用不能触及它:

...
lock (PlayersLock)
{
    Players.Clear();
    foreach (Player player in playersList)
       Players.Add(player);
}
...

// OUTSIDE of the method, define a member to act as a lock:
private object PlayersLock = new object();

相关问题