Dwayne B
Dwayne B

Reputation: 107

Passing data from a window back to ShellViewModel

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. Error

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

Answers (2)

sharp
sharp

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

BlueTriangles
BlueTriangles

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

Related Questions