Reputation: 107
Ok I am having issues getting this figured out. Most of the tutorials I found are either not complete or not explaining the process well enough.
I created a test WPF app with Caliburn.Micro, The app's primary window (ShellViewModel) has a text box and a button, The button opens a second window with a text box and another button. When the user adds text in the second window and clicks "send" POCO object is created and should be sent to the first window and displayed in the text box of the ShellViewModel.
I am Not sure where I went wrong, there does not seem to be many articles helping on this subject.
I tried using the following articles for assistance: https://claytonone.wordpress.com/2014/06/14/caliburn-micro-part-1-getting-started/
https://caliburnmicro.com/documentation/event-aggregator
******EDIT - re-programmed the above project following the directions in https://caliburnmicro.com/documentation/event-aggregator Below is the code for this project. Note I added a POCO class to store and send the data I want to send to another window, This is more of what my final goal will be in the primary project I am working on.
Issues I am running into now: 1. When I ran the program designed from the tutorial, VS Errored saying there was not a parameterless constructor. For this, I attempted to add the constructor. Now the program ran. 2. When I type text into the second window and click send I get a "Null Reference Error", yet if I debug the "ToSend" object is created and populated with the correct data.
AppBootStrapper:
namespace CaliburnMicro
{
class AppBootstrapper : BootstrapperBase
{
private readonly SimpleContainer _container = new SimpleContainer();
public AppBootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
}
}
}
ShellViewModel:
namespace CaliburnMicro.ViewModels
{
class ShellViewModel : Screen, IHandle<EventMessage>
{
private string _messageBox;
private readonly IEventAggregator _eventAggregator;
public string MessageBox
{
get { return _messageBox; }
set
{
_messageBox = value;
NotifyOfPropertyChange(() => MessageBox);
}
}
public ShellViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public ShellViewModel()
{
}
public void OpenWindow()
{
WindowManager wm = new WindowManager();
SecondWindowViewModel swm = new SecondWindowViewModel(_eventAggregator);
wm.ShowWindow(swm);
}
public void Handle(EventMessage message)
{
MessageBox = message.Text;
}
}
}
SecondWindowViewModel
namespace CaliburnMicro.ViewModels
{
class SecondWindowViewModel: Screen
{
private string _secondTextBox;
private readonly IEventAggregator _eventAggregator;
public EventMessage Tosend = new EventMessage();
public string SecondTextBox
{
get { return _secondTextBox; }
set
{
_secondTextBox = value;
NotifyOfPropertyChange(() => SecondTextBox);
}
}
public SecondWindowViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void SendBack()
{
Tosend.Text = SecondTextBox;
_eventAggregator.PublishOnUIThread(Tosend);
Thread.Sleep(1000); //I wanted the app to wait a second before closing
TryClose();
}
}
}
This is the POCO that I want to send back to the main window from the second.
namespace CaliburnMicro.Models
{
class EventMessage
{
public string Text { get; set; }
}
}
Upvotes: 1
Views: 643
Reputation: 1211
Okay, here is a small example on how to setup AppBotstraper
with EventAggregator
.
AppBootstrapper.cs
public class AppBootstrapper : BootstrapperBase
{
private SimpleContainer _container;
public AppBootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.PerRequest<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
var instance = _container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
}
ShellViewModel.cs
public class ShellViewModel : Screen, IScreen, IHandle<EventMessage>
{
private readonly IEventAggregator _eventAggregator;
public ShellViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.Subscribe(this);
}
public void OpenWindow()
{
WindowManager wm = new WindowManager();
SecondWindowViewModel swm = new SecondWindowViewModel(_eventAggregator);
wm.ShowWindow(swm);
}
public void Handle(EventMessage message)
{
MessageBox.Show(message.Text);
}
}
Note: If you don't know about Conductors in Caliburn.Micro
I suggest you to read this. When you use conductors, you are able to start any child ViewModel
inside the UserControl
using method ActivateItem
.
Your SecondWindowViewModel
stays the same, BUT the EventMessage
class must be public class EventMessage
or you will get an error.
Upvotes: 1
Reputation: 1204
Just a thought, did you set your DataContext
so that your primary window knows that the ViewModel is the source of your data?
Upvotes: 0