Strange WPF UI Error

Hi Experts,

     I am getting a strange error in a WPF application.  The app displays data in a ListView(s) that use a List<> of custom classes (that implement INotifyPropertyChanged) as its ItemsSource.  The List<> gets changed all the time (add/remove/change items in the List<>, and sometimes even reload the ItemsSource all together) The app will run fine for sometimes hours and all of the sudden it'll just crash.  I have put error handling around all of the functions that I can think of that could cause the error and I caught nothing.  Finally, I put in a thing that catches all unhandled exceptions and I finally got the error message to dump into a log file.  Below is the error:

Error message:
   Collection was modified; enumeration operation may not execute.

Occured at:
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   at System.Windows.Data.CollectionView.PlaceholderAwareEnumerator.MoveNext()
   at System.Windows.Automation.Peers.ItemsControlAutomationPeer.GetChildrenCore()
   at System.Windows.Automation.Peers.ListViewAutomationPeer.GetChildrenCore()
   at System.Windows.Automation.Peers.AutomationPeer.EnsureChildren()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateChildren()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   at System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree()
   at System.Windows.ContextLayoutManager.fireAutomationEvents()
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
   at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
   at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)
   at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at App.Main()

Any help would be greatly appreciated!

Thanks!
axnst2Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Bob LearnedCommented:
That is a strange error, and it looks like an issue with code that is not thread-safe.

Here is the code in the framework for MoveNextRare:

private bool MoveNextRare()
    {
        if (this.version != this.list._version)
        {
            ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
        }
        this.index = this.list._size + 1;
        this.current = default(T);
        return false;
    }

Open in new window


It compares the version for the Enumerator<T> with the version from the List<T> instance, and if they are different, then something else has changed the value, and you have a mismatch.
axnst2Author Commented:
Any ideas on what could be causing it?  I have background thread that is constantly changing the List, but the UI should have it's own instance so i am not sure.  Below is my code:

void UpdateTaskList()
        {
            try
            {
                m_UITimer.Stop();

                lock (m_BussLogic.Lock)
                {
                    lvMoving_Tasks.ItemsSource = CUtility.ConvertListToObservableCollection(m_BussLogic.CurrentMovingTasks);
                    lvMoving_Tasks.UpdateLayout();
                }

                if (m_RefreshOfMasterListsRequired)
                {
                    m_RefreshOfMasterListsRequired = false;

                    lock (m_BussLogic.Lock)
                    {
                        lvPriority_Tasks.ItemsSource = CUtility.ConvertListToObservableCollection(m_BussLogic.CurrentPriorityTasks);
                        lvPriority_Tasks.UpdateLayout();
                    }

                    System.Threading.Thread.Sleep(100);

                    lock (m_BussLogic.Lock)
                    {
                        lvDiscretionary_Tasks.ItemsSource = CUtility.ConvertListToObservableCollection(m_BussLogic.CurrentDiscretionaryTasks);
                        lvDiscretionary_Tasks.UpdateLayout();
                    }
                }

                lock (m_BussLogic.Lock)
                {
                    for (int index = 0; index < m_BussLogic.CurrentFilteredTasks.Count; index++)
                    {
                        if (index < m_BussLogic.CurrentFilteredTasks.Count &&   // Just in case the collection got modified
                            m_BussLogic.CurrentFilteredTasks[index] != null)
                            m_BussLogic.CurrentFilteredTasks[index].Refresh();
                    }
                }
            }
            catch (Exception er)
            {
                HandleError(er);
            }
            finally
            {
                m_UITimer.Start();
            }
        }


public static ObservableCollection<CLocalTaskItem> ConvertListToObservableCollection(List<CLocalTaskItem> listToConvert)
        {
            ObservableCollection<CLocalTaskItem> observableTasks = new ObservableCollection<CLocalTaskItem>();

            for (int index = 0; index < listToConvert.Count; index++)
            {
                if (index < listToConvert.Count &&   // Just in case the collection got modified
                    listToConvert[index] != null)
                    observableTasks.Add(listToConvert[index]);
            } 

            return observableTasks;
        }

Open in new window


m_BussLogic uses the same Lock object when changing the collection.

Anything obvious that jumps out at you?
Bob LearnedCommented:
What "m_BussLogic.Lock" is in this line?  How is it declared?

lock (m_BussLogic.Lock)

"I have background thread that is constantly changing the List, but the UI should have it's own instance so i am not sure. "

How does the UI get its own instance?  Are you creating a deep copy with a different reference?
Exploring SQL Server 2016: Fundamentals

Learn the fundamentals of Microsoft SQL Server, a relational database management system that stores and retrieves data when requested by other software applications.

axnst2Author Commented:
"What "m_BussLogic.Lock" is in this line?  How is it declared?"

               In CBussLogic:

               volatile object m_Lock = new object();

               public object Lock
               {
                     get { return m_Lock; }
                     set { m_Lock = value; }
              }

              Should I maybe make m_Lock static?

"How does the UI get its own instance?  Are you creating a deep copy with a different reference?"

              Please look at my last post and the ConvertListToObservableCollection function.


I really appreaciate your help LearnedOne!  Please let me know where you think I am screwing things up!
Bob LearnedCommented:
Let's start with a review on "lock":

lock Statement (C# Reference)
http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx

In general, avoid locking on a public type, or instances beyond your code's control.
axnst2Author Commented:
Unfortunately, I do not have that luxury for I am dealing with existing code...  Any other suggestions?
Bob LearnedCommented:
Are you working with one-way, or two-way binding with what looks like a ListView (lvPriority_Tasks)?

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C#

From novice to tech pro — start learning today.