CollectionView 不支持由于 Dispatcher.Invoke() 中的不同线程而发生的更改

问题描述 投票:0回答:1

我在 StackOverFlow 中看到了很多问题,但他们没有在 Dispatcher 中出现此错误

我正在公司升级一个Wpf应用程序,他们给了我一个任务,让UI上的游戏列表变得更快。

应用程序设计模式是mvvm

我开发了 2 个部分。

第 1 节:从数据库中获取游戏 第 2 部分:PC 上的常规搜索。

在此之前:仅常规搜索。

这是我的代码:

public async Task FindGameInBackground()
{
    await App.Current.Dispatcher.BeginInvoke(HandleLaunchersAndGames); // Problem Here
        
    await App.Current.Dispatcher.InvokeAsync(() => Games = new (Games.OrderBy(x => x.Name)));
    GamesView = CollectionViewSource.GetDefaultView(Games);
    ...
}

HandleLaunchersAndGames 没问题,但它是在填充数据后调用 AddGame 方法

private Task AddGame(Game gameModel)
{
    var lGameModel = gameModel;
    if (!Games.Any(x => x.Name.Equals(lGameModel.Name)) ||
        !Games.Any(x => x.AId.Equals(lGameModel.AId)) ||
        Games.Where(x => x.Name.Equals(lGameModel.Name))
                .All(x => x.LauncherName != lGameModel.LauncherName))
    {

        bool threadAccess = App.Current.Dispatcher.Thread == Thread.CurrentThread;
        if (threadAccess)
        {
            Games.Add(lGameModel); // **I Get The Error Here.**
        }
        App.Current.Dispatcher.Invoke(() => Games.Add(lGameModel));

    }
    return Task.CompletedTask;
}

游戏类型:

public ObservableCollection<Game> Games
{
    get => _games ??= new ObservableCollection<Game>();
    set
    {
        _games = value;
        OnPropertyChanged();
    }
}

并且它在 UI 中绑定

我不知道为什么会收到此错误,我正在使用 Dispatcher 和 Invoke 在 AddGame 中,我正在检查线程访问,但添加对象后出现错误

c# .net xml wpf
1个回答
0
投票

您没有提供为您提供准确建议所需的所有详细信息。
因此,我将展示其中一种可能选项的一些抽象实现。

using Simplified;
using System.Collections;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;

namespace Core2023.SO.HosseinSanjabian.Question77697304
{
    public record Game(string Name, int AId, string LauncherName)
    {
        public Game() : this(string.Empty, 0, string.Empty) { }

        public override string ToString() => $"{AId}: {Name} ({LauncherName})";
    }

    public class GamesViewModel
    {
        public ReadOnlyObservableCollection<Game> Games { get; }
        private readonly ObservableCollection<Game> privateGames = new();

        private void AddGame(Game gameModel)
        {
                Thread.Sleep(5000); // Emulating some kind of delay in the execution method
                lock (((ICollection)privateGames).SyncRoot)
                    privateGames.Add(gameModel);
        }

        private async Task AddGameAsync(Game gameModel)
        {
            await Task.Run(() => AddGame(gameModel));
        }

        public RelayCommand<Game> AddGameCommand { get; }

        public GamesViewModel()
        {
            Games = new(privateGames);

            AddGameCommand = new(async game => await AddGameAsync(game));
        }
    }
}
<Window x:Class="Core2023.SO.HosseinSanjabian.Question77697304.GamesWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Core2023.SO.HosseinSanjabian.Question77697304"
        xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        mc:Ignorable="d"
        Title="GamesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <FrameworkElement.Resources>
        <local:GamesViewModel x:Key="vm"/>
        <CollectionViewSource x:Key="games" Source="{Binding Games}">
            <CollectionViewSource.SortDescriptions>
                <cm:SortDescription PropertyName="Name"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
        <local:Game x:Key="addidingGame"/>
    </FrameworkElement.Resources>
    <UniformGrid Rows="1">
        <DataGrid ItemsSource="{Binding Source={StaticResource games}}"/>
        <UniformGrid Columns="1">
            <UniformGrid Columns="2" DataContext="{DynamicResource addidingGame}">
                <TextBlock Text="AID:"/>
                <TextBox Text="{Binding AId}"/>
                <TextBlock Text="Name:"/>
                <TextBox Text="{Binding Name}"/>
                <TextBlock Text="LauncherName:"/>
                 <TextBox Text="{Binding LauncherName}"/>
           </UniformGrid>
            <Button Content="Add Game"
                    Command="{Binding AddGameCommand}"
                    CommandParameter="{DynamicResource addidingGame}"
                    Click="OnAddidingGame"/>
        </UniformGrid>
    </UniformGrid>
</Window>
using System.Collections;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace Core2023.SO.HosseinSanjabian.Question77697304
{
    public partial class GamesWindow : Window
    {
        public GamesWindow()
        {
            InitializeComponent();
            GamesViewModel viewModel = (GamesViewModel) FindResource("vm");
            ICollection games = viewModel.Games;
            BindingOperations.EnableCollectionSynchronization(games, games.SyncRoot);
        }

        private async void OnAddidingGame(object sender, RoutedEventArgs e)
        {
            await Task.Delay(100);
            Resources["addidingGame"] = new Game();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.