-1

actually I was facing the below exception when I try to update the collection outside the UI thread in my WPF app. But found the dispatcher solution to fix the exception.

But my collections are actually dependent on each other. When I updates one collection based on the newly added data I will update my another collection. But when I use the below code, my collections are not updating properly. Please assist me to fix this

class Class1
    {
        // UI thread
        ObservableCollection<string> myCollection1 = new ObservableCollection<string>();
        ObservableCollection<string> myCollection2 = new ObservableCollection<string>();
        object lockObject1 = new object();
        object lockObject2 = new object();

        public Class1()
        {
            syncCollection();
        }

        public void syncCollection()
        {
            BindingOperations.EnableCollectionSynchronization(myCollection1, lockObject1);
            BindingOperations.EnableCollectionSynchronization(myCollection2, lockObject2);
        }


        // Assume that the below methods are being called by Non UI thread 

        //After Collection2 is updated this method will be called from the NON UI THREAD
        //If Foo is added in collection2 then add Bar in collection1
        public void UpdateCollection1()
        {
            if (myCollection2.Contains("Foo"))
            {
                Application.Current.Dispatcher.BeginInvoke((Action)delegate
                {
                    myCollection1.Add("Bar");
                });
            }
        }

        //This method will be called first from the NON UI THREAD
        public void UpdateCollection2()
        {
            Application.Current.Dispatcher.BeginInvoke((Action)delegate
            {
                myCollection2.Add("Foo");
            });
        }

    }

In the above sample code, I want add "Bar" inside Collection1 only when the Collection2 has "Foo". But when we run the code the "if" condition fails(means that collection2 doesn't have "Foo" in it).

EDIT1:

  • I have a view(xaml) where an Itemsource is an ObservableCollection. That OC is in a datamodel.
  • One of the feature in the app is to add new items to the itemsource which is the OC
  • To add new items in the OC I had to enable the synchronization to when its updating from UI thread, so I use EnableCollectionSynchronization & dispatcher.
  • Now the problem is, I have multiple ObservableCollection itemsources and they are dependent. When I apply the same with all the OCs the expected item is not adding

EDIT2 I tried to add the code replica

class ViewModel1 : INotifyPropertyChanged   
    {
        protected ObservableCollection<Datamodel1> _items;
        public ObservableCollection<Datamodel1> Items
        {
            get
            {
                return _items;
            }
            set
            {
                SetProperty<ObservableCollection<Datamodel1>>(ref _items, value);
            }
        }

        public void Set(Items obj)
        {
            BindingOperations.EnableCollectionSynchronization(obj.ItemSource1, _lock1);
            Items = obj.ItemSource1;
        }
    }

    class ViewModel2 : INotifyPropertyChanged
    {
        protected ObservableCollection<Datamodel2> _items;
        public ObservableCollection<Datamodel2> Items
        {
            get
            {
                return _items;
            }
            set
            {
                SetProperty<ObservableCollection<Datamodel2>>(ref _items, value);
            }
        }

        public void Set(Items obj)
        {
            BindingOperations.EnableCollectionSynchronization(obj.ItemSource1, _lock2);
            Items = obj.ItemSource2;
        }
    }

    class Items
    {
        public ObservableCollection<Datamodel1> ItemSource1 = new ObservableCollection<Datamodel1>();
        public ObservableCollection<Datamodel2> ItemSource2 = new ObservableCollection<Datamodel2>();
    }

    class ParentDatamodel
    {
        public string ParentProperty { get; set; }
        public string ParentProperty2 { get; set; }
    }
    class Datamodel1 : ParentDatamodel
    {
        public string MyProperty { get; set; }
        public string MyProperty2 { get; set; }
    }

    class Datamodel2 : ParentDatamodel
    { 
        public string MyProperty { get; set; }
        public string MyProperty2 { get; set; }

    }
    
    //This methods are called outside the UI thread
    class Updater
    {
        public Items items { get; set; }
        public void updaterForDatamodel1()
        {
            Application.Current.Dispatcher.BeginInvoke((Action)delegate
            {
                items.ItemSource1.Add(new Datamodel1() { MyProperty = "Foo" }) ;
            });
        }

        public void updaterForDatamodel2()
        {
            if(items.ItemSource1.Any(x => x.MyProperty == "Foo")) //This condition is getting failed
            {
                Application.Current.Dispatcher.BeginInvoke((Action)delegate
                {
                    items.ItemSource2.Add(new Datamodel2() { MyProperty = "Bar" });
                });
            }
        }
    }
Vignesh G
  • 53
  • 6
  • 2
    There really doesn't seem to be enough code to help you. You need to provide a [mcve]. – Enigmativity Aug 29 '23 at 10:59
  • 2
    If you use data binding, simply storing a new collection to the bound property and raising `NotifyPropertyChanged` will result in a UI update. This will work even if the property is modified from another thread. `found the dispatcher solution to fix the exception.` that was a solution before 2012. For the last 11 years it's using `async/await`. – Panagiotis Kanavos Aug 29 '23 at 11:03
  • 1
    Right now it's unclear what the problem is, except the verbose code. Why is `BeginInvoke` used at all? Why is `UpdateCollection1` called from another thread? The *missing* code should use `async/await` and only modify the collection *after* await – Panagiotis Kanavos Aug 29 '23 at 11:05
  • 2
    For example, `var stuf=await httpClient.GetStringAsync(); MyObservableCollection.Add(stuff);` will work without threading issues or locks. – Panagiotis Kanavos Aug 29 '23 at 11:08
  • It is unclear why you would use EnableCollectionSynchronization and Dispatcher.BeginInvoke at the same time. EnableCollectionSynchronization allows to modify a collection from threads other than the UI thread. You do however have to make sure to lock the lockObjects before accessing the collections. – Clemens Aug 29 '23 at 11:08
  • The edit does nothing more than describe the code already in the question. Post the missing code, don't describe it. You *don't* need synchronization to add items to ObservableCollection if you use `await` to get back to the UI thread – Panagiotis Kanavos Aug 29 '23 at 14:12
  • @PanagiotisKanavos - I managed to add the code replica. Thanks for the speedy response – Vignesh G Aug 29 '23 at 15:15
  • There's no code that tries to update the collections from another thread. What calls `updaterForDatamodel1`? That's where the problem is – Panagiotis Kanavos Aug 29 '23 at 15:22
  • That updaterForDatamodel1 & updaterForDatamodel2 are triggered from GUI – Vignesh G Aug 29 '23 at 15:55
  • I have multiple itemsources. Based on the trigger from the UI I use a factory pattern to correctly choose the Updater functions i.e updaterForDatamodel1 for certain case and updaterForDatamodel2 for other case – Vignesh G Aug 30 '23 at 05:55

0 Answers0