programing

이 유형의 CollectionView는 디스패처 스레드와 다른 스레드에서 SourceCollection으로의 변경을 지원하지 않습니다.

bestprogram 2023. 4. 12. 23:07

이 유형의 CollectionView는 디스패처 스레드와 다른 스레드에서 SourceCollection으로의 변경을 지원하지 않습니다.

ViewModel에서 비동기 방식으로 데이터를 채우는 DataGrid가 있습니다.My Data Grid는 다음과 같습니다.

<DataGrid ItemsSource="{Binding MatchObsCollection}"  x:Name="dataGridParent" 
                      Style="{StaticResource EfesDataGridStyle}" 
                      HorizontalGridLinesBrush="#DADADA" VerticalGridLinesBrush="#DADADA" Cursor="Hand" AutoGenerateColumns="False" 
                      RowDetailsVisibilityMode="Visible"  >

뷰 모델에 비동기 방식을 구현하기 위해 http://www.amazedsaint.com/2010/10/asynchronous-delegate-command-for-your.html을 사용하고 있습니다.

뷰 모델 코드는 다음과 같습니다.

public class MainWindowViewModel:WorkspaceViewModel,INotifyCollectionChanged
    {        

        MatchBLL matchBLL = new MatchBLL();
        EfesBetServiceReference.EfesBetClient proxy = new EfesBetClient();

        public ICommand DoSomethingCommand { get; set; }
        public MainWindowViewModel()
        {
            DoSomethingCommand = new AsyncDelegateCommand(
                () => Load(), null, null,
                (ex) => Debug.WriteLine(ex.Message));           
            _matchObsCollection = new ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC>();                

        }       

        List<EfesBet.DataContract.GetMatchDetailsDC> matchList;
        ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> _matchObsCollection;

        public ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC> MatchObsCollection
        {
            get { return _matchObsCollection; }
            set
            {
                _matchObsCollection = value;
                OnPropertyChanged("MatchObsCollection");
            }
        }        
        //
        public void Load()
        {            
            matchList = new List<GetMatchDetailsDC>();
            matchList = proxy.GetMatch().ToList();

            foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
            {
                _matchObsCollection.Add(match);
            }

        }

ViewModel의 Load() 메서드에서 볼 수 있듯이 먼저 서비스로부터 matchList(데이터 계약 클래스 목록)를 가져옵니다.「이다」는, 「match List」의 「match Obs Collection(Data Contract Class) Observable Collection」의 「match Obs Collection(Data Contract Class)」의 「Ovservatable Collection」의 「match Obs Collection」의 「match Collection」의 「Match List」의에러(제목 이으로의 변경을 ."라고됩니다.「CollectionView」 「CollectionView」 「CollectionView」 「SourceCollection」 。여기에 이미지 설명 입력

누가 내게 어떤 해결책이라도 제안해 줄 수 있나요?또한 가능하면 View에서 DataGrid를 바인딩하는 방법과 더 나은 방법이 있다면 비동기식으로 새로 고치는 방법을 알고 싶습니다.

ObservableCollection은 UI 스레드에 작성되므로 UI 스레드에서만 수정할 수 있으며 다른 스레드에서는 수정할 수 없습니다.이를 스레드 어피니티라고 합니다.

가 있는 는, 간단하게 UI 를 사용해 .put the delegate on UI DispatcherUI를 사용합니다. 하면 .

    public void Load()
    {
        matchList = new List<GetMatchDetailsDC>();
        matchList = proxy.GetMatch().ToList();

        foreach (EfesBet.DataContract.GetMatchDetailsDC match in matchList)
        {
            App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE
            {
                _matchObsCollection.Add(match);
            });
        }
    }

확실히 WPF 4.5에서는 문제없이 이 작업을 수행할 수 있습니다.

이 문제를 해결하려면 동기화 컨텍스트를 사용해야 합니다.스레드를 시작하기 전에 동기화 컨텍스트를 UI 스레드에 저장해야 합니다.

var uiContext = SynchronizationContext.Current;

그런 다음 스레드에 사용합니다.

uiContext.Send(x => _matchObsCollection.Add(match), null);

이 튜토(http://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I)를 보세요.

다음과 같이 할 수 있습니다.

App.Current.Dispatcher.Invoke((System.Action)delegate
             {
               _matchObsCollection.Add(match)
             });

.NET 4.5+의 경우: Daniel의 답변을 따를 수 있습니다.이 예에서는 퍼블리셔가 올바른 스레드로 호출 또는 호출해야 하는 책임을 퍼블리셔에 부여합니다.

var uiContext = SynchronizationContext.Current;
uiContext.Send(x => _matchObsCollection.Add(match), null);

또는 서비스/뷰 모델/모든 것에 책임을 부여하고 단순히 Collection Synchronization을 활성화할 수도 있습니다.이렇게 하면 전화를 걸 때 어떤 스레드에 있는지, 어떤 스레드에서 전화를 걸지 걱정할 필요가 없습니다.퍼블리셔에 대한 책임은 더 이상 없습니다.(이로 인해 퍼포먼스 오버헤드가 다소 발생할 수 있지만 중앙 서비스에서 이를 수행하면 많은 예외를 줄일 수 있고 애플리케이션 유지보수가 쉬워집니다.)

private static object _lock = new object();

public MainWindowViewModel()
{
    // ...
    _matchObsCollection = new ObservableCollection<EfesBet.DataContract.GetMatchDetailsDC>();
    BindingOperations.EnableCollectionSynchronization(_matchObsCollection , _lock);
}

상세정보 : https://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.enablecollectionsynchronization(v=vs.110).aspx

Visual Studio 2015 (Pro) visual 、 [ Debug -- ] > [ Windows -- ]> [ Threads ]으로 이동하여 쉽게 디버깅하여 현재 사용하고 있는 스레드를 확인합니다.

AsyncObservableCollection(http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/))에서도 같은 문제가 발생한 적이 있습니다.

여기서 해결책을 찾았습니다.https://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/ 새로운 클래스를 생성하여 Observable Collection 대신 사용합니다.그것은 나에게 효과가 있었다.

public class AsyncObservableCollection<T> : ObservableCollection<T>
{
    private SynchronizationContext _synchronizationContext = SynchronizationContext.Current;

    public AsyncObservableCollection()
    {
    }

    public AsyncObservableCollection(IEnumerable<T> list)
        : base(list)
    {
    }

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the CollectionChanged event on the current thread
            RaiseCollectionChanged(e);
        }
        else
        {
            // Raises the CollectionChanged event on the creator thread
            _synchronizationContext.Send(RaiseCollectionChanged, e);
        }
    }

    private void RaiseCollectionChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (SynchronizationContext.Current == _synchronizationContext)
        {
            // Execute the PropertyChanged event on the current thread
            RaisePropertyChanged(e);
        }
        else
        {
            // Raises the PropertyChanged event on the creator thread
            _synchronizationContext.Send(RaisePropertyChanged, e);
        }
    }

    private void RaisePropertyChanged(object param)
    {
        // We are in the creator thread, call the base implementation directly
        base.OnPropertyChanged((PropertyChangedEventArgs)param);
    }
}

내 경우 (기입)ObservableCollection비동기 태스크로 액세스 할 수 없습니다.Appinstance) 를 사용합니다.TaskScheduler.FromCurrentSynchronizationContext()장애 발생 시 컬렉션을 정리하려면:

        // some main task
        Task loadFileTask = Task.Factory.StartNew(...);

        Task cleanupTask = loadFileTask.ContinueWith(
            (antecedent) => { CleanupFileList(); },
            /* do not cancel this task */
            CancellationToken.None,
            /* run only if faulted main task */
            TaskContinuationOptions.OnlyOnFaulted,
            /* use main SynchronizationContext */
            TaskScheduler.FromCurrentSynchronizationContext());

BackgroundWorker를 사용하는 경우 UI의 동일한 스레드에서 이벤트를 발생시켜야 합니다.

예를 들어 보기 A와 B가 2개 있고 A 내부의 다음 코드가 WakeUpEvent 이벤트를 발생시키는 경우

//Code inside codebehind or viewmodel of A
    var worker = new BackgroundWorker();
    worker.DoWork += WorkerDoWork; //<-- Don't raise the event WakeUpEvent inside this method
    worker.RunWorkerCompleted += workerRunWorkerCompleted; // <-- Raise the event WakeUpEvent inside this method instead
    worker.RunWorkerAsync();

//Code inside codebehind or viewmodel of view B
    public ViewB () {
        WakeUpEvent += UpdateUICallBack;
    }
    private void UpdateUICallBack() {
        //Update here UI element
    }

WorkerDoWork 메서드는 UI와 다른 스레드에서 실행됩니다.

이 에러도 발생하고 있습니다.

"이러한 유형의 CollectionView는 디스패처 스레드와 다른 스레드에서 SourceCollection으로의 변경을 지원하지 않습니다.

"Release Android"라는 새 구성을 만들었는데, 이 구성은 "Release" 구성의 복사본이며 아카이브 관리자에서 새 릴리스를 만드는 데 사용되었습니다.설정을 「Release」로 되돌리면, 모든 것이 정상적으로 구축됩니다.에러는 없습니다.

이게 도움이 됐으면 좋겠네요.

언급URL : https://stackoverflow.com/questions/18331723/this-type-of-collectionview-does-not-support-changes-to-its-sourcecollection-fro