Search code examples
c#wpfbackgroundworkerobservablecollectiondispatcher

Looking for explaination System.NotSupportedException


So first some code, question in the end. I've got some objects called machines, they have two properties

public Logs MachineLogs {
        get { return _logs; }
        set {
            _logs = value;
            NotifyPropertyChanged ("MachineLogs");
        }
    }

    public ObservableCollection<Part> Parts {
        get { return _parts; }
        set {
            _parts = value;
            NotifyPropertyChanged ("Parts");
        }
    }

MachineLogs looks like that:

public ObservableCollection<Log> Malfunctions {
        get {
            SortCollection (_malfunctions);
            return _malfunctions;
        }
        set {
            _malfunctions = value;
            NotifyPropertyChanged ("Malfunctions");
        }
    }

    public ObservableCollection<Log> CompletedTasks {
        get {
            SortCollection (_completedTasks);

            return _completedTasks;
        }
        set {
            _completedTasks = value;
            NotifyPropertyChanged ("CompletedTasks");
        }
    }

    public ObservableCollection<LogTemplate> TaskTemplates {
        get { return _taskTemplates; }
        set {
            _taskTemplates = value;
            NotifyPropertyChanged ("TaskTemplates");
        }
    }

Now I clone Machine using serialization within BackgroundWorker and then add it to the map where I store it.

protected override void CloneReady ( object sender, RunWorkerCompletedEventArgs e )
    {
        var machine = ((Machine) e.Result);
        _map.Machines.Add (machine);

        var machineControl = new MachineControl (machine, _canvas);
        ((MainWindow) Application.Current.MainWindow).MapPanel.Selector.ProcessSelection (machineControl);

    }

Now here's the problem. Everything works alright with parts collection, when I add items to it or remove (the methods for both collections look completly the same). Exception occurs just when I'm trying to perform any operation on Malfunctions collection.

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.



private void AddNewEntry(object sender, RoutedEventArgs e) {
        if (LogList.SelectedLog == null)
        {
            var source = (ObservableCollection<Log>) LogList.ItemsSource;

            Log newLog = new Log (LogTypes.Malfunction, ((Machine) DataContext).MachineLogs.Colors.MalfunctionColor.HexValue)
            {
                DbAction = Data.DBSavers.DatabaseActions.Create,
                MachineId = ((Machine) DataContext).Db.Id
            };
            source.Insert (0, newLog);
            LogList.SelectedLog = newLog;
        }
        else
        {
            LogList.SelectedLog = null;
        }
    }

AddNewEntry is called by UI button, tried Invoking Dispatcher but still no luck. Is there any explaination for such behaviour? Problem doesn't occur when I skip part with BackgroundWorker. Reason why I cannot skip it is that I need to clone multiple machines (copy/paste stuff) and it can take a while so I don't want to freeze UI.

protected override void ProduceClone ( object sender, DoWorkEventArgs e )
    {
        var serializer   = new XmlSerializer ();
        var selector     = new MapColorSelector ();
        var machine      = serializer.SerializeMachine (e.Argument as Machine);
        machine.Color    = selector.DefaultColor;
        machine.Location = _location;

        e.Result = machine;
    }

Solution

  • I love how this site helps solving problems. Most of the answers come after describing problem and carefully looking into it. This time it was fault of sorting method. Went for LINQ and it works again, but maybe someone could explain this anyway :]

    private void SortCollection (ObservableCollection<Log> collection) {
            var sorted = CollectionViewSource.GetDefaultView (collection);
            sorted.SortDescriptions.Add (new SortDescription ("Date", ListSortDirection.Ascending));
        }