Creating dialog windows with Dependency Injection and Services - c#

I've finally given in and started learning and applying MVVM architecture on my EFCore project. However, I haven't understood how scoped services work. On my previous question I was told I needed to use one scope per viewmodel otherwise, since I'm working with async methods, I would be getting concurrent transactions on the same DbContext (which is not allowed).
I've also read here that one way of using dialog windows while still following MVVM would be to have a service that would create a dialog window container with a dynamically filled viewmodel.
So far, so good. However I've hit a snag:
I'm on my MainView, and I run a command that opens a new dialog window (with a new scope, I think). And on that dialog, I run a command that should open another dialog window. However, when trying to close, somehow I manage to close the parent dialog rather than the focused window. My relevant code is as follows:
What am I doing wrong here?
PS.: I translated the names to english for better readability, but I might've let one or another slip by - let me know if I can fix anything.
public class PurchasesViewModel : ViewModelBase
{
private readonly IDialogGenerator _purchaseDialogGenerator;
private readonly IDialogViewModelFactory _purchaseRecordVMFactory;
public ICommand CreateNewPurchase { get; set; }
public PurchasesViewModel(IServiceProvider services, IDialogGenerator purchaseVMDialogGenerator)
{
_purchaseDialogGenerator = purchaseVMDialogGenerator;
IServiceScope purchaseVMScope = services.CreateScope();
IServiceProvider provider = purchaseVMScope.ServiceProvider;
_purchaseRecordVMFactory = provider.GetRequiredService<IDialogViewModelFactory>();
CreateNewPurchase = new CreatePurchaseCommand(_purchaseDialogGenerator, _purchaseRecordVMFactory);
}
}
internal class CreatePurchaseCommand : ICommand
{
private IDialogGenerator _purchaseDialogGenerator;
private IDialogViewModelFactory _purchaseVMFactory;
public CreatePurchaseCommand(IDialogGenerator PurchaseRecordDialogGenerator, IDialogViewModelFactory PurchaseRecordVMFactory)
{
_purchaseDialogGenerator = PurchaseRecordDialogGenerator;
_purchaseVMFactory = PurchaseRecordVMFactory;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_purchaseDialogGenerator.ShownViewModel = _purchaseVMFactory.CreateDialogContentViewModel(DialogType.RecordPurchases);
_purchaseDialogGenerator.ShowDialog();
}
}
public class PurchaseRecordViewModel : DialogContentViewModelBase
{
private readonly IMessaging<PURCHASE> _purchaseMessaging;
private readonly IDialogGenerator _purchaseDialogGenerator;
private readonly IDialogGenerator _importViaXMLDialogGenerator;
private readonly IDialogViewModelFactory _importViaXMLVMFactory;
public ICommand SavePurchase { get; set; }
public ICommand ImportXML { get; set; }
public PurchaseRecordViewModel(IServiceProvider servicos, IDialogGenerator PurchaseRecordDialogGenerator)
{
_purchaseDialogGenerator = PurchaseRecordDialogGenerator;
IServiceScope scope = servicos.CreateScope();
IServiceProvider provider = scope.ServiceProvider;
_importViaXMLDialogGenerator = provider.GetRequiredService<IDialogGenerator>();
_importViaXMLVMFactory = provider.GetRequiredService<IDialogViewModelFactory>();
_purchaseMessaging = provider.GetRequiredService<IMessaging<PURCHASE>>();
SavePurchase = new SalvaPurchaseCommandAsync(PurchaseRecordDialogGenerator);
ImportXML = new ImportXMLDePurchaseCommand(_importViaXMLDialogGenerator, _importViaXMLVMFactory);
}
}
internal class ImportXMLFromPurchaseCommand : ICommand
{
private readonly IDialogGenerator _importviaXMLDialogGenerator;
private readonly IDialogViewModelFactory _importXMLVMFactory;
public ImportXMLFromPurchaseCommand(
IDialogGenerator importviaXMLDialogGenerator,
IDialogViewModelFactory importXMLVMFactory)
{
_importviaXMLDialogGenerator = importviaXMLDialogGenerator;
_importXMLVMFactory = importXMLVMFactory;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_importviaXMLDialogGenerator.ShownViewModel = _importXMLVMFactory.CreateDialogContentViewModel(DialogType.ImportXML);
_importviaXMLDialogGenerator.ShowDialog();
}
}
internal class SalvaPurchaseCommandAsync : ICommand
{
private readonly IDialogGenerator _dialogGenerator;
public SalvaPurchaseCommandAsync(IDialogGenerator dialogGenerator)
{
_dialogGenerator = dialogGenerator;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_dialogGenerator.Result = DialogResult.OK;
_dialogGenerator.Close();
}
}
public class ImportViaXMLViewModel : DialogContentViewModelBase
{
public ICommand ImportArquivoXML { get; set; }
public ICommand Import { get; set; }
private readonly IDialogGenerator _importViaXMLDialogGenerator;
public ImportViaXMLViewModel(IMessaging<PURCHASE> messaging,
IDialogGenerator importViaXMLDialogGenerator)
{
_importViaXMLDialogGenerator = importViaXMLDialogGenerator;
Import = new ImportCommand(this, messaging, _importViaXMLDialogGenerator);
}
}
internal class ImportCommand : ICommand
{
private readonly IMessaging<PURCHASE> _messaging;
private readonly IDialogGenerator _dialogGenerator;
public ImportCommand(IMessaging<PURCHASE> messaging, IDialogGenerator dialogGenerator)
{
_messaging = messaging;
_dialogGenerator = dialogGenerator;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_messaging.Message = new COMPRA();
_dialogGenerator.Result = DialogResult.OK;
_dialogGenerator.Close();
}
}
public abstract class AsyncCommandBase : ICommand
{
private bool _isExecuting;
private readonly Action<Exception> _onException;
public bool IsExecuting
{
get { return _isExecuting; }
set {
_isExecuting = value;
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
public AsyncCommandBase(Action<Exception> onException)
{
this._onException = onException;
}
public virtual event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter)
{
return !IsExecuting;
}
public async void Execute(object parameter)
{
IsExecuting = true;
try
{
await ExecuteAsync(parameter);
}
catch (Exception ex)
{
_onException(ex);
}
IsExecuting = false;
}
protected abstract Task ExecuteAsync(object parameter);
}

I found out a way to work it out. I found an answer after reading Good or bad practice for Dialogs in wpf with MVVM?.
I have created a singleton dialogStore in which I keep all the currently opened dialogs. Whenever I need a new dialog window, I "register" a new dialogGenerator with the dialogStore, and it'll open it for me. When I need to close it, the store is also responsible for that. Furthermore, so far, I've been using only one modal dialog active at a time, so whenever I close a dialog, I'll be sure it's the latest added one. But in the future, each dialogGenerator will know its set dialogStore index, and will be able to close themselves (and other dialogs) in any order needed.
My dialogStore is as follows:
public class DialogsStore : IDialogsStore
{
public List<IDialogGenerator> OpenDialogs { get; set; } = new List<IDialogGenerator>();
public void CloseDialog(DialogResult dialogResult)
{
IDialogGenerator lastDialogGenerator = OpenDialogs[OpenDialogs.Count - 1];
lastDialogGenerator.Resultado = dialogResult;
//lastDialogGenerator.DialogClosed = null;
lastDialogGenerator.Close();
}
public int RegisterDialog(IDialogGenerator dialog)
{
OpenDialogs.Add(dialog);
dialog.DialogClosed += Dialog_DialogClosed;
dialog.ShowDialog();
return OpenDialogs.Count - 1;
}
private void Dialog_DialogClosed()
{
OpenDialogs.RemoveAt(OpenDialogs.Count - 1);
}
}
One important point I have to deal with is that closing the dialog and removing it from the store didn't dispose of the scoped dialogGenerator, so I will still need to dispose from anything inside my dialogGenerator before removing it from my dialogStore.
If anyone has a better way to deal with dialog windows in MVVM with Dependency Injection, I'll be glad to hear it.

Related

Prism. Closing a dialog created with IDialogService

I am trying to use a new IDialogService which was discussed in github issue 1666. A New IDialogService for WPF. I like this new feature but I can't find a solution for one case of using IDialogService in compare with InteractionRequest.
There is a button, pressing on which non-modal dialog is opened. If user press the same button one more time, while dialog still open, dialog close. How this behavior should be implemented in a proper way?
MainWindowViewModel
public class MainWindowViewModel : BindableBase
{
private readonly IDialogService _dialogService;
public DelegateCommand CustomPopupCommand { get; }
public MainWindowViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
CustomPopupCommand = new DelegateCommand(OpenClosePopup);
}
private void OpenClosePopup()
{
// It looks like some additional logic should be implemented here.
// How to save previously opened IDialogAware instance and close it if needed?
_dialogService.Show("CustomPopupView", new DialogParameters("Title=Good Title"), result => { });
}
}
CustomPopupViewModel
public class CustomPopupViewModel : BindableBase, IDialogAware
{
private string _title;
public string Title
{
get => _title;
set => SetProperty(ref _title, value);
}
public DelegateCommand<object> CloseCommand { get; }
public CustomPopupViewModel()
{
CloseCommand = new DelegateCommand<object>(CloseDialog);
}
public event Action<IDialogResult> RequestClose;
public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.GetValue<string>(nameof(Title));
}
public void OnDialogClosed()
{
}
public bool CanCloseDialog()
{
return true;
}
public void RaiseRequestClose(IDialogResult dialogResult)
{
RequestClose?.Invoke(dialogResult);
}
private void CloseDialog(object button)
{
RaiseRequestClose(
new DialogResult(button is ButtonResult buttonResult ? buttonResult : ButtonResult.Cancel));
}
}
I have no idea how can it be implemented in proper way because method IDialogService.Show() fully decoupled from knowing about ViewModel and View. Of course except the name of View.
You can always send an event through the event aggregator, probably you have to pass some id in the dialog parameters to close the right dialog if there's more than one open at a time.
But this feels really clunky, I'd prefer to get an IDisposable from Show/ShowDialog that closes the dialog on Dispose.
public CustomPopupViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<CloseDialogEvent>().Subscribe( id => { if (id == _id) CloseMe(); } );
}
public void OnDialogOpened(IDialogParameters parameters)
{
_id = parameters.GetValue<string>("id");
}
_dialogService.Show("CustomPopupView", new DialogParameters("id=12345"), result => { });
_eventAggregator.GetEvent<CloseDialogEvent>().Publish("12345");
I find it simplest to use Prism implementation of the subscriber pattern
I use a class that will be used in the pattern and is communicated:
public class DialogStatus
{
public bool DialogResult { get; set; }
}
In my sample, I show you how I do this using a Login Dialog in WPF using Prism 8.0.0.1909
in the App.cs
protected override void OnInitialized()
{
var login = Container.Resolve<LoginDialog>();
var result = login.ShowDialog();
if (result.HasValue && result.Value)
{
base.OnInitialized();
}
else
{
Application.Current.Shutdown();
}
}
in LoginDialog.cs in my Dialogs folder
public partial class LoginDialog : Window
{
public LoginDialog(IEventAggregator eventAggregator)
{
InitializeComponent();
eventAggregator.GetEvent<CloseDialogWindowEvent>().Subscribe(OnCloseWindow);
}
private void OnCloseWindow(DialogStatus obj)
{
base.DialogResult = obj.DialogResult;
}
}
now anywhere in my code, in a ViewModel of view a custom control's view model, the only thing I need to do is pass the IEventAggregator in in the constructor and save it in a field.
private readonly IEventAggregator _eventAggregator;
public LoginControlViewModel(IAuthenticationService authenticationService
, IConnectFileImporterService connectFileImporterService
, IDialogService dialogService
, IEventAggregator eventAggregator)
{
_eventAggregator= eventAggregator;
// the other code
}
I can now close my dialog, and in this sample return true to falls to my OnInitalize in my App.cs from anywhere by calling
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = true });
or
_eventAggregator.GetEvent<CloseDialogWindowEvent>().Publish(new CloseDialogWindowEvent() { DialogResult = false});
If i understand correctly, you want to close the dailog window programmatically instead of clicking the windows's close button, right? If It is true, maybe I can provide you with a solution. Although this method is not very elegant, it is very simple.
My project use mahapps styles, I want use metrowindow as the dailoghost window. Following prism documentation, I register dialoghost window and usercontrol like this:
containerRegistry.RegisterDialogWindow<DialogHost>(nameof(DialogHost));
containerRegistry.RegisterDialog<UserEdit, UserEditViewModel>(nameof(UserEdit));
The UserEidt is a usercontrol, I place a confirm button and a cancel button in UserEidt, and both button binding DelegateCommand in UserEditViewModel. The question is, how can i close dailogwindow by clicking the cancel button?
Here is my solution, firstly define a IDailogViewModel interface:
public interface IDialogViewModel
{
Action CloseDialogWindow { get; set; }
}
Then UserEditViewModel implement this interface:
public class UserEditViewModel : BindableBase, IDialogAware,IDialogViewModel
{
public DelegateCommand CancelCmd { get; private set; }
public Action CloseDialogWindow { get; set; }
public UserEditViewModel()
{
CancelCmd = new DelegateCommand(CloseDialogWindow)
}
private void CloseDialogWindow()
{
CloseDialogWindow.Invoke();
}
}
Infact, when the dialog window popup, the UserEdit will be dialogWindow's content. So in the dialogwindow's loaded event handler, i can get the UserEdit object by using Window.Content, here is the code:
public partial class DialogHost : MetroWindow, IDialogWindow
{
public DialogHost()
{
InitializeComponent();
}
public IDialogResult Result { get; set; }
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
var dialogVM = (IDialogViewModel)((UserControl)Content).DataContext;
dialogVM.CloseDialogWindow += CloseDialogWindow;
}
void CloseDialogWindow()
{
Close();
}
}
Now,after clicking the cancel button, the dialogwindow will be close.

Passing data from one VM to another VM, using Unity and prism EventAggregator

Trying to pass data from one ViewModel to another using prism EventAggregator, but when I debug on the subscriber, the data is null.
Using version 5.0 of prism.
Update
Okay, I have tried implement the EventAggregator using prism 5.0 version. It still dosen't work, but here is what I have done.
1: Create event class
public class RoomsSelectedEvent : PubSubEvent<ObservableCollection<Room>>
{
}
2: Inject IEventAggregator on publisher ViewModel (BookingViewModel)
public class BookingViewModel : INotifyPropertyChanged, IViewBookingViewModel
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//commands
public ICommand ContinueCommand { get; set; }
public ObservableCollection<Room> RoomsList { get; private set; }
public ObservableCollection<RoomList> DropDownRooms { get; private set; }
public ObservableCollection<CustomerList> DropDownCustomers { get; private set; }
//enities
private readonly IDialogService<ContactDetailsView> _dialogServiceContactView;
private readonly IGetRoomsService _getRoomsService;
public BookingViewModel(IDialogService<ContactDetailsView> dialogServiceContactview, IGetRoomsService GetRoomsService, IEventAggregator eventAggregator)
{
// Injection
_dialogServiceContactView = dialogServiceContactview;
_getRoomsService = GetRoomsService;
_eventAggregator = eventAggregator;
//commands
ContinueCommand = new RelayCommand(ContinueCommand_DoWork, () => true);
}
// Continue Command
public void ContinueCommand_DoWork(object obj)
{
ObservableCollection<Room> RoomsSelected = new ObservableCollection<Room>();
RoomsSelected = _getRoomsService.FilterSelectedRooms(RoomsList);
//Publish event:
_eventAggregator.GetEvent<RoomsSelectedEvent>().Publish(RoomsSelected);
// Open new dialog
_dialogServiceContactView.ShowDialog();
}
}
3: Inject IEventAggregator in subscriber viewModel (ContactViewModel)
public class ContactViewModel : IViewContactViewModel, INotifyPropertyChanged
{
//aggregator
protected readonly IEventAggregator _eventAggregator;
//properties
public ObservableCollection<Room> SelectedRooms { get; set; }
public ContactViewModel(IEventAggregator eventAggregator)
{
//Injection
_eventAggregator = eventAggregator;
//Subscripe to event
_eventAggregator.GetEvent<RoomsSelectedEvent>()
.Subscribe((data) => { SelectedRooms = data; });
}
public ObservableCollection<Room> Rooms
{
get { return SelectedRooms; }
set { SelectedRooms = value; NotifyPropertyChanged(); }
}
}
I have read it's maybe because of the IEventAggregator is not the same in both ViewModels. I'm using Unity to inject it like this code:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//view & viewModels
_container = new UnityContainer();
_container.RegisterType<IViewMainWindowViewModel, MainWindow>();
_container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
_container.RegisterType<IViewBookingViewModel, BookingView>();
_container.RegisterType<IViewBookingViewModel, BookingViewModel>();
_container.RegisterType<IViewContactViewModel, ContactDetailsView>();
_container.RegisterType<IViewContactViewModel, ContactViewModel>();
_container.RegisterType<IGetRoomsService, GetRoomsService>();
_container.RegisterType<IPostReservationService, PostReservationService>();
_container.RegisterType<IGetReservationsListService, GetReservationsListService>();
//types
_container.RegisterType<IEventAggregator, EventAggregator>(new ContainerControlledLifetimeManager());
_container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));
_container.Resolve<MainWindow>().Show();
}
Found that I need to add the ContainerControlledLifetimeManager, but it still wont work.
When I debug in the subscriber viewModel, I can see that there is an event inside the instance, like on the image:
It wont catch it , thats the problem :(
Today I tried delete all packages I installed (prism 5.0) and go for the Prism.Core (6.1 version)
That resulted in the same, I can see when I debug that the event in published, but when I subscribe, it's still null.

How to run an Action on View before bound ViewModel Command is executed

As title clearly defines, I want to run an Action in my View just before a command is executed in the ViewModel.
Scenario
Assume that we have a NavigationDrawer with two or more item types (e.g. NavigationItem and ExpandableItem). Each item was bound to a command on its corresponding ViewModel.
Now I want to:
Close the drawer before executing NavigationItem command.
Show an animation for the ExpandableItem before executing the command and do not close drawer.
I have tried the following approach:
Core Project
public class MvxInteractiveCommand : MvxCommandBase, IMvxCommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public MvxInteractiveCommand(Action execute)
: this(execute, null)
{
}
public MvxInteractiveCommand(Action execute, Func<bool> canExecute)
{
_canExecute = canExecute;
_execute = execute;
Delay = 0;
}
/// <summary>
/// Delay in milliseconds to execute the action. Default is zero means immediate execution
/// </summary>
public int Delay { get; set; }
public object InteractData { get; set; }
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute();
}
public bool CanExecute()
{
return CanExecute(null);
}
public void Execute(object parameter)
{
if (CanExecute(parameter))
{
Mvx.Resolve<IMvxMessenger>().Publish(new CommandExecutingMessage(this) { Data = InteractData });
if (Delay == 0)
_execute();
else
Task.Delay(200).ContinueWith(obj => _execute());
}
}
public void Execute()
{
Execute(null);
}
}
public class CommandExecutingMessage : MvxMessage
{
public CommandExecutingMessage(object sender)
: base(sender)
{
}
public object Data { get; set; }
}
NavigationSimpleItem.cs:
public class NavigationSimpleItem : MvxNavigatingObject
{
private readonly IMvxCommand _clickCommand;
public NavigationSimpleItem(string caption, int id, Action doCommand, Func<bool> canDoCommand = null)
{
...
if (doCommand == null)
throw new ArgumentNullException("doCommand");
_clickCommand = new MvxInteractiveCommand(doCommand, canDoCommand);
}
public override IMvxCommand ClickCommand
{
get { return _clickCommand; }
}
...
}
Droid Project
MainView.cs:
public class MainView : MvxActivity
{
private MvxSubscriptionToken _token;
public MainView()
{
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.MainView);
}
protected override void OnViewModelSet()
{
base.OnViewModelSet();
_token = Mvx.Resolve<IMvxMessenger>().SubscribeOnMainThread<CommandExecutingMessage>(e =>
{
DrawerWrapper.CloseDrawer();
});
}
}
Notes: As you can see, I use Messenger Plugin to interact between the View and the ViewModel. It works fine with my first need, which is to run a simple Action on every item click.
However, my view needs to know which item is clicked and what its state is.
I also want more complex scenarios later such as waiting for an UI action to complete before command execution (something like this answer).

Communication between two viewmodels

I'm newbie in MVVM design pattern, and I have these viewmodels :
ClassAViewModel
public class ClassAViewModel : INotifyPropertyChanged
{
private int _nbre = 0;
public int Nbre
{
get
{
return _nbre;
}
set
{
_nbre = value;
PropertyChanged(this, new PropertyChangedEventArgs("Nbre"));
}
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
And ClassBViewModel
PUBLIC class ClassBViewModel: INotifyPropertyChanged
{
private Boolean _IsBiggerthanFive = false;
public bool IsBiggerthanFive
{
get
{
return _IsBiggerthanFive;
}
set
{
_IsBiggerthanFive = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsBiggerthanFive"));
}
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I need to know if a mecanism of notification between two viewmodels exists , ie in my case if _nbre > 5 in the first viewmodel, the second viewmodel will be notified and the value of _IsBiggerthanFive will be changed. So:
How can two viewmodels communicate between them without instanciate one in the other ?
What is the best way to accomplish this task?
I agree with other commenters that the mediator/pub-sub/event aggregator/messenger is a good way to go. If you're not using an MVVM framework with a built-in solution, then I recommend this simple approach that takes advantage of the Reactive extensions:
public class EventPublisher : IEventPublisher
{
private readonly ConcurrentDictionary<Type, object> subjects
= new ConcurrentDictionary<Type, object>();
public IObservable<TEvent> GetEvent<TEvent>()
{
var subject =
(ISubject<TEvent>) subjects.GetOrAdd(typeof (TEvent),
t => new Subject<TEvent>());
return subject.AsObservable();
}
public void Publish<TEvent>(TEvent sampleEvent)
{
object subject;
if (subjects.TryGetValue(typeof(TEvent), out subject))
{
((ISubject<TEvent>)subject)
.OnNext(sampleEvent);
}
}
}
That's your whole event aggregator. Pass an instance of it into each view model, and store it as a reference. Then create a class to store your event details, let's say "ValueChangedEvent":
public class ValueChangedEvent
{
public int Value
{
get { return _value; }
}
private readonly int _value;
public ValueChangedEvent(int value)
{
_value = value;
}
}
Publish like this from the first view model:
set
{
_nbre = value;
PropertyChanged(this, new PropertyChangedEventArgs("Nbre"));
_eventPublisher.Publish(new ValueChangedEvent(value));
}
Subscribe in the other class using GetEvent:
public class ClassBViewModel: INotifyPropertyChanged, IDisposable
{
private readonly IDisposable _subscriber;
public ClassBViewModel(IEventPublisher eventPublisher)
{
_subscriber = eventPublisher.Subscribe<ValueChangedEvent>(next =>
{
IsBiggerthanFive = next.Value > 5;
});
}
public void Dispose()
{
_subscriber.Dispose();
}
}
A messenger service is a solution. MVVM Light Toolkit has an implementation of this. What you can do with it, is listen to a specific type of message in your viewmodel and handle it through the messenger. http://www.mvvmlight.net/

Caliburn micro ViewModel is not picking up a message from another ViewModel

Following my learning of MVVM using Caliburn.micro framework... I'm trying to communicate two viewModels sending data through the EventAggregator like this (code with "no sense", just for test):
MainWindowViewModel.cs
namespace TOP
{
[Export(typeof(MainWindowViewModel))]
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
readonly IWindowManager windowManager;
private readonly IEventAggregator events;
private bool _Logged;
[ImportingConstructor]
public MainWindowViewModel(IWindowManager windowManager, IEventAggregator events)
{
DisplayName = "TOP";
this.events = events;
events.Subscribe(this);
this.windowManager = windowManager;
windowManager.ShowDialog(new LoginViewModel(events));
}
public bool Logged
{
get { return _Logged; }
set
{
_Logged = value;
if(_Logged== true)
InitiateApp();
}
}
public void Handle(LoginEvent message)
{
Logged = message.Logged;
}
private void InitiateApp() {
ActivateItem(new TwoWindowViewModel());
}
}
}
LoginViewModel.cs
namespace TOP{
[Export(typeof(IScreen))]
public class LoginViewModel : Screen
{
private readonly IEventAggregator _events;
[ImportingConstructor]
public LoginViewModel(IEventAggregator events)
{
DisplayName = "Login";
_events = events;
Login();
}
public void Login()
{
_events.Publish(new LoginEvent(true));
}
}
}
LoginEvent.cs
namespace TOP
{
public class LoginEvent
{
public LoginEvent(bool logged)
{
Logged = logged;
}
public bool Logged { get; private set; }
}
}
Why the Handle method of MainWindowViewModel is not picking up the published message from LoginViewModel?
Thank you for your responses.
Your MainWindowViewModel needs to implement IHandle<LoginEvent>. You already defined the method with the correct signature, so you only are missing the part where you actually tell the compiler that you implement the interface:
public class MainWindowViewModel
: Conductor<IScreen>.Collection.OneActive, IHandle<LoginEvent>
More info can be found in the documentation.

Categories