creating new view from a Message Notification fails

Mar 6, 2011 at 9:08 AM

Hello,

i use the MediatorMessageSink Attribute on a Method in my ViewModel that creates a new View (a tab page in my example). Now, since this TabPage also contains a Method with a MediatorMessageSink Attribut, I also have to Register the new VM with with Messenger:

            Mediator.Instance.Register(this);

This in turn causes a "Collection was Modified" Exception from inside the NotifyColleagues Method.

How is the recommended approach for this scenario?

 

regards

Joachim

Coordinator
Mar 6, 2011 at 6:31 PM

No idea why that is going wrong, you could email me a small (very small or I will not look at it, cut down version showing the issue) demo of it, email me at sacha<DOT>barber<AT>gmail<DOT>com or post some code here and I will try and repeat it.

Aug 23, 2011 at 6:07 PM

I have the same problem.

The simplest way to demonstrate it is to add to Your WPF demo new property in MainWindowViewModel and bind it to SelectedItem dependency property of TabControlEx.

MainWindowView.xaml:

<local:TabControlEx Grid.Row="1" x:Name="tab1"
            ItemsSource="{Binding Views}"  TabStripPlacement="Left"
                        CinchV2:NavProps.ShouldHideHostWhenNoItems="true"
                        ItemContainerStyle="{StaticResource TabItemStyleVerticalTabs}"
                        Style="{DynamicResource TabControlStyleVerticalTabs}" 
                        IsSynchronizedWithCurrentItem="True" 
                        DisplayMemberPath="DisplayText"                                    
                        SelectedItem="{Binding ActiveWorkspaceData, Mode=TwoWay}" >  
 
MainWindowViewModel.cs:
 
        private WorkspaceData _activeWorkspaceData;
        public WorkspaceData ActiveWorkspaceData
        {
            get
            {
                return _activeWorkspaceData;
            }
            set
            {
                if (_activeWorkspaceData != value)
                {

                    
                    var tmp = new ImageViewModel();

                    _activeWorkspaceData = value;
                    NotifyPropertyChanged("ActiveWorkspaceData");
                }
            }
        }
 
  
  

Now, when we try to close active view it will fire NotifyColleagues method (with key "RemoveWorkspaceItem") and change SelectedItem. 

Creating new ImageViemModel (useless in my example) will call ViewModelBase constructor and this method: 

Mediator.Instance.RegisterHandler<WorkspaceData>("RemoveWorkspaceItem", OnNotifyDataRecieved);

 

modifying List<WeakAction> wr inside foreach loop in the NotifyColleagues method.

 

Maybe, enumerating on copy inside the NotifyColleagues could fix it?

regards,

kanepl

  

  

  

 

 

 

 

 

 

Coordinator
Aug 24, 2011 at 11:55 AM
Edited Aug 24, 2011 at 11:57 AM

If I change Mediator.NotifyCollegues code to that shown below, it all seems cool, could you both verify this by grabbing the latest source code for cinch as it is now (which does not include this change yet), and then modify it in the downloaded code for Mediator as shown below, and then build it all, then use the resultant built Cinch.WPF.dll in your projects (you know reference the local copy of Cinch you just modified), and see if this fixes you issues. If it does, let me know and I will make the change in Cinch source code.

 

So just so we are clear, I will not be making this change unless one of you tries it and lets me know its ok, as I need to know it worked it your scenario, it worked in the example that "Kanepl" posted, but I want to know it works for you guys 1st, before I change the codebase again.

No one else has reported errors, so I think its quite unusual scenario I guess

 

So please verify it as I stated above, let me know on this forum and I will patch it.

 

 

 /// <summary>
        /// This method broadcasts a message to all message targets for a given
        /// message key and passes a parameter.
        /// </summary>
        /// <param name="key">Message key</param>
        /// <param name="message">Message parameter</param>
        /// <returns>True/False if any handlers were invoked.</returns>
        private bool NotifyColleagues(object key, object message)
        {
            List<WeakAction> wr;
            List<WeakAction> wrCopy=new List<WeakAction>();
            lock (_registeredHandlers)
            {
                if (!_registeredHandlers.TryGetValue(key, out wr))
                    return false;
                else
                {
                    foreach (var weakRe in wr)
                    {
                        wrCopy.Add(weakRe);
                    }
                }

            }

            foreach (var cb in wrCopy)
            {
                Delegate action = cb.GetMethod();

                if (action != null)
                    action.DynamicInvoke(message);
            }

            lock (_registeredHandlers)
            {
                wr.RemoveAll(wa => wa.HasBeenCollected);
            }

            return true;
        }
        #endregion
Aug 24, 2011 at 5:27 PM

Yes, this fixed my issue.

Thank You!

Coordinator
Aug 25, 2011 at 6:01 AM

Ok I'll ammend the Cinch codebase in next few days

Coordinator
Aug 27, 2011 at 6:55 AM

All fixed in : http://cinch.codeplex.com/SourceControl/changeset/changes/64992

Coordinator
Aug 27, 2011 at 6:55 AM

Kapel all fixed in : http://cinch.codeplex.com/SourceControl/changeset/changes/64992