【问题标题】:Displaying duplicate when adding a new item to an ObservableCollection<> bound to a Collection View in .net Maui将新项目添加到绑定到 .net Maui 中的集合视图的 ObservableCollection<> 时显示重复项
【发布时间】:2022-11-18 03:27:21
【问题描述】:

我目前有一个集合视图,它显示从我设备的本地数据库中提取的记录列表。数据库工作正常,添加记录,删除记录工作正常。问题是,当我添加或删除记录时,当集合视图刷新时,它会显示每个现有记录的副本。奇怪的是,如果我再次刷新,它会恢复正常,只显示从中提取的数据库中表的记录。

这是我的视图模型:

 [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 中的值已经是重复的,但我不知道为什么或在哪里重复。它仅在我添加或删除记录时发生。 任何帮助表示感谢谢谢!

【问题讨论】:

    标签: xaml maui collectionview


    【解决方案1】:

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

    • 调用#1:Players.Clear();
    • 调用#2:Players.Clear();
    • 呼叫#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();
    

    如果那不能解决问题,则在涉及 Players 的语句周围添加一个 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();
    

    【讨论】:

    • 非常感谢!在从数据库中获取后移动 Players.Clear() 就可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-25
    • 2011-03-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    相关资源
    最近更新 更多