Reputation: 1214
I am learning and trying MVVM
using Caliburn.Micro
. I have a main ShellViewModel
and a Child UserControl Module1ViewModel
. I am trying to achieve communication between both of them using IEventAggregator
.
Now, I can change a property of ShellViewModel
(which is parent) from child control ShellViewModel and can change a property of child control from Parent and it works.
Problem : Issue I am facing is that when I enable events.Subscribe(this);
in both ViewModels I get an exception.
What I am trying to achieve is TWO WAY kind of communication. Which means I want to change some property of Parent from child at the same time I should be able to change some Property of Child from Parent. Following is my code, please check what's wrong here.
Child User Control Module1ViewModel
namespace IntelliCoreMVVM.ViewModels
{
public class Module1ViewModel:Screen , IHandle<string>
{
private IEventAggregator _events;
public Module1ViewModel(IEventAggregator events)
{
_events = events;
events.Subscribe(this);
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyOfPropertyChange(()=>FirstName);
_events.PublishOnUIThread(FirstName);
}
}
public void Handle(string message)
{
FirstName = message;
}
}
}
As you can see, i am publishing and Handling a property.
ShellViewModel
public class ShellViewModel : Conductor<object> , IHandle<string>
{
private Module1ViewModel _module1ViewModel;
private IEventAggregator _events;
public ShellViewModel(Module1ViewModel module1ViewModel,IEventAggregator events)
{
_events = events;
events.Subscribe(this);
_module1ViewModel = module1ViewModel;
}
private string _test;
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyOfPropertyChange(()=>FirstName);
_events.PublishOnUIThread(new CustomerModel{FirstName = FirstName});
}
}
public string Test
{
get { return _test; }
set
{
_test = value;
NotifyOfPropertyChange(()=>Test);
}
}
public void Handle(string message)
{
Test = message;
}
}
Upvotes: 0
Views: 299
Reputation: 169270
FirstName
raises the event which calls Handle
which sets FirstName
again and then it goes on like this in an infinite loop until you run out of stack space and get a StackOverflowException
.
The thing is that Module1ViewModel
handles all string
events including the ones that it raises itself.
What you probably want to do is define different types of events so you can distinguish between them and choose which to handle. In the below example, Module1ViewModel
handles events of type ParentToChildEvent
but it raises events of type ChildToParentEvent
. The ShellViewModel
should do the opposite.
public class Module1ViewModel : Screen, IHandle<ParentToChildEvent>
{
private IEventAggregator _events;
public Module1ViewModel(IEventAggregator events)
{
_events = events;
events.Subscribe(this);
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
_events.PublishOnUIThread(new ChildToParentEvent(FirstName));
}
}
public void Handle(string message)
{
FirstName = message;
}
}
Upvotes: 1
Reputation: 23238
The problem is that your VMs are recursively updating each other and self, e.g. subscribed to IEventAggregator
and handle string message, than update a property and publish the same again, some kind of infinite loop. You can introduce a typed events to handle from Parent to Child and vice versa, instead of just strings
public class ChildEvent
{
public string Name { get; set; }
}
public class ParentEvent
{
public string Name { get; set; }
}
public class ShellViewModel : Conductor<object>, IHandle<ChildEvent>
{
private readonly IEventAggregator _events;
public ShellViewModel(IEventAggregator events)
{
_events = events;
events.Subscribe(this);
}
private string _test;
public string Test
{
get => _test;
set
{
_test = value;
NotifyOfPropertyChange(() => Test);
}
}
public void Handle(ChildEvent message)
{
Test = message.Name;
}
}
public class Module1ViewModel : Screen, IHandle<ParentEvent>
{
private readonly IEventAggregator _events;
public Module1ViewModel(IEventAggregator events)
{
_events = events;
events.Subscribe(this);
}
private string _firstName;
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
NotifyOfPropertyChange(() => FirstName);
}
}
public void Handle(ParentEvent message)
{
FirstName = message.Name;
}
}
Btw, you don't need a Module1ViewModel
reference in ShellViewModel
in case of using an IEventAggregator
. Also do not fire events immediately in setters, because it can fire an infinite event loop between view models
Upvotes: 1