PrismLibrary / Prism

Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Xamarin Forms, and Uno / Win UI Applications..
Other
6.28k stars 1.64k forks source link

Event listener is not being trigger unless the view is manually resolved from the container. #1386

Closed MikeAlhayek closed 6 years ago

MikeAlhayek commented 6 years ago

Description

When my app first starts, I want to require the user to login. Once logged in, I want to show the default landing page with the user name and buttons.

My thought, when a user login, I would publish an event called UserLoggedIn then on the home view-model, I would listen for that event. When the event is triggered, I would show the landing/home view.

So I created the event like so

public class UserLoggedIn : PubSubEvent<IUserPassport>
{
}

Then in the LoginViewModel I handle the login and publish the event like so

    private void HandleLogin(LoginView loginView)
    {
        try
        {
            User user = AuthenticationService.Authenticate(Username, loginView.GetPassport());

            IUserPassport passport = PassportManager.Get(user.Username);

            if (passport == null)
            {
                // Create a new session 
                passport = new UserPassport(new CustomIdentity(user), RegionManager);
            }
            // Assign the current session
            PassportManager.SetCurrent(passport);

            // Trigger the event
            EventAggregator.GetEvent<UserLoggedIn>().Publish(passport);

            // Deactivate the login view
            RegionManager.GetMainRegion().Deactivate(loginView);
        }
        catch (Exception ex)
        {
              //Do something with the error
        }
    }

Here is how I configure my IoC continer in the Bootstrapper class

    protected override void ConfigureContainer()
    {
        RegisterTypeIfMissing(typeof(DbContext), typeof(ProductionContext), false);
        RegisterTypeIfMissing(typeof(IUnitOfWork), typeof(EntityUnitOfWork), false);
        RegisterTypeIfMissing(typeof(IPassportManager), typeof(PassportManager), true);
        RegisterTypeIfMissing(typeof(IAuthenticationService), typeof(AuthenticationService), true);

        base.ConfigureContainer();
    }

Here is also how I register the modules from the bootstrapper class

    protected override void ConfigureModuleCatalog()
    {
        base.ConfigureModuleCatalog();

        RegisterLoginModule();
        RegisterLandingModule();
    }

    private void RegisterLoginModule()
    {
        Type loginModule = typeof(LoginModule);
        ModuleCatalog.AddModule(new ModuleInfo()
        {
            ModuleName = loginModule.Name,
            ModuleType = loginModule.AssemblyQualifiedName,
            InitializationMode = InitializationMode.WhenAvailable
        });
    }

    private void RegisterLandingModule()
    {
        Type landingModule = typeof(LandingModule);
        ModuleCatalog.AddModule(new ModuleInfo()
        {
            ModuleName = landingModule.Name,
            ModuleType = landingModule.AssemblyQualifiedName,
            InitializationMode = InitializationMode.WhenAvailable
        });
    }

Finally in my HomeViewModel aka my landing view, I have the following code to listen for the UserLoggedIn event.

public class HomeViewModel : BindableBase
{
    protected IUnityContainer Container { get; set; }
    protected ICoreRegionManager RegionManager { get; set; }
    private IEventAggregator EventAggregator { get; set; }

    public HomeViewModel(IUnityContainer container, ICoreRegionManager regionManager, IEventAggregator eventAggregator)
    {
        Container = container;
        RegionManager = regionManager;
        EventAggregator = eventAggregator;
        eventAggregator.GetEvent<UserLoggedIn>().Subscribe(ShowTheLangingPage);
    }

    private void ShowTheLangingPage(User user)
    {
        var homeView = Container.Resolve<HomeView>();

        RegionManager.AddToMainRegion(homeView);
        FullName = user.FirstName;
    }

    // I am using PropertyChange.Fody package, so this property will automatically raise the PropertyChange event.
    public string FullName { get; set; }
}

Problem is that theShowTheLangingPage method never get triggered in my HomeViewModel as expected.

I made sure the the View HomeView and HomeViewModel are wired up correctly, by directly loading the HomeView on module initialization for testing.

Additionally, if add Container.Resolve<HomeView>(); just before I publish the event, the ShowTheLangingPage I called. Its like I have to resolve the HomeView manually for it to listen for the event.

Is this a bug? Is not, what am I doing wrong that is preventing the listener from firing? Not, my login process takes place in my LoginModule while the HomeView is in a different module called LandingModule

Steps to Reproduce

  1. Create 2 modules LoginModule and LandingModule
  2. in the Initialize method in the LoginModule : IModule class add the following code to show the login at start up
        public void Initialize()
        {
            var view = Container.Resolve<LoginView>();
            IRegion region = RegionManager.Regions["MainContent"];
            region.Add(view);
        }
  3. In the LoginViewModel after a user is authenticated, I use commandParameter to pass the form to the command. Then I execute the HandleLogin (from above)
  4. In my landing page I added event listener as you can see in my HomeViewModel above.

### Expected Behavior
The `HomeViewModel.ShowTheLangingPage()` to get triggered automatically when the event is raised.

### Actual Behavior
The `HomeViewModel.ShowTheLangingPage()` never get called unless I add the following code `Container.Resolve<HomeView>()` just before the event is published from the `LoginViewModel`

### Basic Information

- Version with issue:


### Screenshots

<!-- If the issue is a visual issue, please include screenshots showing the problem if possible -->

### Reproduction Link

<!-- REQUIRED - Please upload or provide a link to a reproduction case. If no reproduction sample is included, this issue may be closed or ignored until a sample has been provided -->
brianlagunas commented 6 years ago

Please provide a running sample that replicates the issue. There is not enough information here to help. I don't know what your shell looks like, what's in the shell, how the MainView gets in the shell or when. It sounds like your MainView doesn't exist at the time you fire the event, which is why it will work if you manually resolve then object. You may have a design issue.

lock[bot] commented 4 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.