How can I initialize a Prism module without View and ViewModel for working with EventAggregator? - wpf

I am writing an application using Prism that contains three modules. First one has a view to configure a "Person", second one is a service that generates that "Person" and third one is the visualization of all people. These three modules communicate with EventAggregator system. But I have problems with the messages on the service one.
In this service module I only have the service implementation and the module definition.
This service is a people manager that receives a message from EventAggregator, creates a "Person" with a task and send a message to the third module with this "Person".
Service:
private List<Person> people = new();
public PeopleControllerService(IEventAggregator eventAggregator, ICommonParametersService commonParameters)
{
this._eventAggregator = eventAggregator;
eventAggregator.GetEvent<GeneratePersonEvent>().Subscribe(GeneratePerson);
this._commonParameters = commonParameters;
}
private void GeneratePerson()
{
Person newPerson = new(this._commonParameters.DefaultPersonTask);
this.People.Add(newPerson);
this._eventAggregator.GetEvent<AssignedPersonEvent>().Publish(newPerson);
}
Module definition:
private PeopleControllerService moduleController;
public void OnInitialized(IContainerProvider containerProvider)
{
IEventAggregator eventAggregator = containerProvider.Resolve<IEventAggregator>();
ICommonParametersService commonParametersService = containerProvider.Resolve<ICommonParametersService>();
this.moduleController = new(eventAggregator, commonParametersService);
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
}
The problem is that when I send the "GeneratePersonEvent" message it never reaches the PeopleControllerService and the "GeneratePerson" method is never executed.
I've tried using a view and a viewModel, programming the service in the viewModel and assigning the view to a dummy and hidden region in the app and I've verified that it works that way.
Modified module definition:
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.DummyRegion, "PeopleController");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PeopleController>();
}
How can I use the EventAggregator without using a dummy view? Do I have to add something in the "RegisterTypes" method? I've tried with:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<PeopleControllerService>();
}
but it doesn't work either.
I've checked this post: Can I get EventAggregator Subscribe Message without view, viewmodel in prism?, and there it says that it is possible, but doesn't describe how to implement.

Most of the time you want exactly one instance of a service, and you have to tell the container:
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<PeopleControllerService>();
}
Also, you want your service to implement an interface so that you can pass different implementations to the consumers of your service, the most obvious case is your tests.
You need to actually create the instance of your service, too. Normally, you inject it into some consumer, but if it's completely decoupled and only talks through the event aggregator, you have to create the instance manually:
// in App.xaml.cs
protected override void OnInitialized()
{
Container.Resolve<PeopleControllerService>();
base.OnInitialized();
}
Hint: if the service implements an interface, the application doesn't need to personally know the controller module.

Related

How to load module when using events

I'm very newbie in C#\Prism ecosystem.
I want to connect modules via events, but if just send event:
_eventAggregator.GetEvent<LoginSuccessEvent>().Publish(new LoginSuccessEventArgs(user));
then my event handler is not working.
As I understand it happens, because reciever ViewModel is not created (I checked with break point in debugger).
But if I navigate from event sender:
_regionManager.RequestNavigate(RegionNames.RootRegion, "WorksheetListView");
_regionManager.RequestNavigate(RegionNames.WorksheetDetailsRegion, "WorksheetDetailsView");
_eventAggregator.GetEvent<LoginSuccessEvent>().Publish(new LoginSuccessEventArgs(user));
Then before first RequestNavigate command reciever ViewModel constructor is called.
_regionManager = regionManager;
_model = container.Resolve<WorksheetListModel>();
OpenWorksheetCommand = new DelegateCommand(OpenWorksheet);
Worksheets = _model.WorksheetList;
eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(OnLoginSuccessEvent);
I tried to add ViewModel class registration to reciever module:
_container.RegisterType<WorksheetListViewModel>();
But no luck. I don't want to add this registration to sender, because hard relation is maked.
But I want to have weak relation between modulel and to do navigation from RECIEVER, but not from SENDER. So sender will don't know anything about reciever.
How can I achive this?
Thanks.
It's a bit unclear what you're trying to achieve, but as far as I get it, your problem's that events are there for those instances that are currently alive. It seems you want more of a state, so that view models that are created after the user logged in can check whether a user is logged in and act accordingly.
I suggest you create a service to hold the currently logged in user and keep the event, because they complement each other nicely.
Example:
public interface IUserManager : INotifyPropertyChanged // this is optional if you keep the event
{
// returns null if no user is logged in
string CurrentUserName { get; }
// returns true if user name and password are valid and updates CurrentUserName
bool TryLogin( string userName, string password );
}
Ok. I found a tons of question like ('EventAggregator don't work around modules').
My solution is very easy. I create a instance of my viewmodel, so constructor with event subscribtion is invoked too.
using Microsoft.Practices.Unity;
using Prism.Modularity;
using Prism.Regions;
using Prism.Events;
using Prism.Unity;
using WorksheetListModule.ViewModels;
using WorksheetListModule.Views;
using WorksheetListModule.Models;
namespace WorksheetListModule
{
public class WorksheetListModule : IModule
{
IRegionManager _regionManager;
IUnityContainer _container;
IEventAggregator _eventAggregator;
public WorksheetListModule(RegionManager regionManager, IUnityContainer container, IEventAggregator eventAggregator)
{
_regionManager = regionManager;
_container = container;
_eventAggregator = eventAggregator;
}
public void Initialize()
{
WorksheetListViewModel vm = new WorksheetListViewModel(_regionManager, _container, _eventAggregator);
_container.RegisterInstance<WorksheetListViewModel>(vm);
_container.RegisterType<WorksheetListModel>();
_container.RegisterTypeForNavigation<WorksheetListView>();
_container.RegisterTypeForNavigation<WorksheetDetailsView>();
}
}
}
Key feature here is RegisterInstance function.
So now I can do navigation in event reciever and event sender now don't know any information about reciever internal structure.

Notify property changed in WCF service

I have a WPF application which calls WCF service methods through a Client which exposes these methods. Is there any way to bind my application to a property of the service, and to get notified when this property changes? I know INotifyPropertyChanged but I have some doubts about its efficiency in this case... Thanks
EDIT : Actually, all I want is my application to be notified of the changes that happen on the server side.
There are a couple of questsions here. You can bind your code to the client end of a WCF service and by using a partial class definition you can add an INotifyPropertyChanged interface to it so that it meets your design. But actually wiring up the mechanism for pushing updates from the server would be much harder.
In fact, Events will work over WCF, and reasonably performant i.e. you won't have the delay associated with polling. However I wouldn't try to squeeze your WCF code into fitting the INotifyPropertyChanged pattern. Instead use a more bespoke interface for the client/server comms and then expose the INotifyPropertyChanged back in the ViewModel.
Just add a delegate to your service, then call the service from your view model or code behind and reflect the changes with your properties that implement the INotifyPropertyChanged interface:
In Service:
public delegate void ServcieUpdate(SomeDataType data);
public ServcieUpdate OnServcieUpdated { get; set; }
When data is updated:
if (OnServcieUpdated != null) OnServcieUpdated(data);
In view model:
private ServiceClient serviceClient = new ServiceClient();
private ObservableCollection<SomeDataType> data = new
ObservableCollection<SomeDataType>();
public YourViewModel()
{
serviceClient.OnServiceUpdated += OnServcieUpdated;
}
public ObservableCollection<SomeDataType> Data
{
get { return data; }
set { data = value; NotifyPropertyChanged("Data");
}
public void OnServcieUpdated(SomeDataType data)
{
Data = data;
}
Please take a look at the Delegates (C# Programming Guide) page on MSDN in you are unfamiliar with using delegate objects.

Register views with regions depending on the user rights in Silverlight

We have a module class which implements the IModule interface and it has only one method Initialize() in which we register our views.
Is it possible to register these views after successful login?
I want to prohibit the registration of several views depending on the current user. But the user logs on after the module initialization.
Is there a way to provide a callback where Prism can evaluate if the registration is active? Or do I have the chance to disable registrations of the Region Manager? Any other ideas?
Thanks
The easiest way to communicate among multiple modules (or even within) in Prism is using the EventAggregator. Here is what I would do:
Create a CompositePresentationEvent for UserLoginEvent that takes relevant parameter regarding the user
Publish the event using IEventAggregator when the user successfully logs in
On your module initialize, subscribe to the UserLoginEvent, in the handler register the appropriate views
Repeat the above, but opposite, for UserLogout if desired.
In your infrastructure lib:
public class UserLoginEvent : CompositePresentationEvent<User> { }
Then in your module:
public class YourModule : IModule
{
private readonly IUnityContainer container;
private readonly IRegionManager regionManager;
private readonly IEventAggregator events;
public YourModule(IUnityContainer container, IRegionManager manager, IEventAggregator events)
{
this.container = container;
this.regionManager = manager;
this.events = events;
}
public void Initialize()
{
....
events.GetEvent<UserLoginEvent>().Subscribe(RegisterUserViews);
}
private void RegisterUserViews(User u)
{
// check user permissions
// register and create views using container, regionManager
}
}
In whatever module / code your user logs in... I assume you can get the IEventAggregator (similar to above) and then do something like:
OnUserLogin(User u)
{
eventAggregator.GetEvent<UserLoginEvent>().Publish(u);
}
Hope this helps! if a user logs out, then you may want to store references to your views in your module and remove them on a UserLogoutEvent which would work like the above code but just doing the opposite.

How to share data resources between widgets in GWT

I am using GWT and AppEngine for a project. I would like to know how can I share data (ArrayList objects)between widgets, so I could centralize the logic and reduce the number of RPC calls to the server.
I have thought of two ways, but I don't know which is better:
1) When I instantiate the widget, I pass the ArrayList object as a parameter, although I don't know how to do that because the widget gets instantiated with :
ThisAppShell shell = GWT.create(ThisAppShell.class);
2) By using a mechanism like eventBus
http://www.dev-articles.com/article/Gwt-EventBus-(HandlerManager)-the-easy-way-396001
When the user loads the application,after the login process is complete, I would like to download a list of employees which should be available for all widgets. This should all be done in the onModuleLoad() method. I would like to download them all at startup because I would like to implement some sort of caching mechanism. For example, I want to have 2 ArrayList instances:
- emplListOnStart which is populated when the application is loading
- emplListChanges, an array on which the user will make modifications from inside widgets.
After the user has finished making the changes (he presses the "Save" button), the two arrays will be compared, the differences will be saved in appengine (via RPC) and also updated in emplListOnStart.
This is the code for the EntryPoint class:
public class ThisApp implements EntryPoint {
ThisAppShell shell = GWT.create(ThisAppShell.class);
LoginServiceAsync loginService = GWT.create(LoginService.class);
private ArrayList<Employee> emplListOnStart;
private ArrayList<Employee> emplListChanges;
public void onModuleLoad() {
RootLayoutPanel.get().clear();
RootLayoutPanel.get().add(shell);
loginService.isAuthenticated(new AsyncCallback<UserDto>() {
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
public void onSuccess(UserDto result) {
//Here I should load the emplListOnStart list;
}
});
shell.getLogoutLink().addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
loginService.logout(new AsyncCallback() {
public void onFailure(Throwable caught) {
}
public void onSuccess(Object result) {
//Here the user will get logged out
}
});
Window.Location.assign("");
}
});
}
}
And here is the code for the widget:
public class ThisAppShell extends Composite {
private static ThisAppShellUiBinder uiBinder = GWT
.create(ThisAppShellUiBinder.class);
interface ThisAppShellUiBinder extends UiBinder<Widget, ThisAppShell> {
}
#UiField
Anchor logout_link;
#UiField
StackLayoutPanel stackLPanel;
#UiField
TabLayoutPanel tabLPanel;
public ThisAppShell() {
initWidget(uiBinder.createAndBindUi(this));
initializeWidget();
}
public void initializeWidget() {
stackLPanel.add(new HTML("Manage empl."), new HTML("Employees"), 30);
stackLPanel.add(new HTML("Manage Dept."), new HTML("Departments"), 30);
// Add a home tab
HTML homeText = new HTML("This is the home tab");
tabLPanel.add(homeText, "Home");
// Add a tab
HTML moreInfo = new HTML("This is the more info tab");
tabLPanel.add(moreInfo, "More info");
// Return the content
tabLPanel.selectTab(0);
}
public Anchor getLogoutLink() {
return logout_link;
}
}
Is this possible, or how could this be done better?
Thank you.
I think there are two ways to do it:
Create a setter on your widget to set your ArrayList instances (setData()). You can then call this function in the onSuccess method of your loginService.
Inject the singleton instance of a global EventBus into your widget (using i.e. gin/guice) and fire an event containing your data. In the widget you have to attach an EventHandler for the specific event (i.e. LoadEmplListEvent).
I think both solutions are fine to use.
Solution one creates a tighter coupling to your widget but is easier to implement and I think you should take this route if you only have a small number of widgets where you work
with the data.
Solution is a cleaner approach because it de-couples your widgets from the rest. You fire the event the data in your onSuccess method once and you don't care about the widgets.
The widgets that are interested in the data will make sure that they handle the event appropriately (by handling the event). I guess if you have a lot of widgets that have to deal with the data the second approach is the one to go for.

In Prism, how do I inject an interface into my module constuctor instead of a type?

In my prism application I'm getting the error Activation error occured while trying to get instance of type CustomerModule, key \"\".
It's caused by the fact that my customers module I'm trying to inject a "menuManager" of type IMenuManager:
namespace CustomerModule
{
public class CustomerModule : IModule
{
private readonly IRegionManager regionManager;
private readonly IUnityContainer container;
private readonly IMenuManager menuManager;
public CustomerModule(IUnityContainer container,
IRegionManager regionManager,
IMenuManager menuManager)
{
this.container = container;
this.regionManager = regionManager;
this.menuManager = menuManager;
}
public void Initialize()
{
container.RegisterType<IMenuManager, MenuManager>(new ContainerControlledLifetimeManager());
...
However, if I change the CustomerModule constructor to inject a type instead of an interface, then it works:
public CustomerModule(IUnityContainer container,
IRegionManager regionManager,
MenuManager menuManager)
So where do I need to register my MenuManager as implementing IMenuManager? It seems that registering it in CustomerModule's Initialize method is too late.
ANSWER:
I put it in ConfigureContainer() and it worked fine, be sure to leave in "base.ConfigureContainer()":
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType<MenuManager>(new ContainerControlledLifetimeManager());
}
Why would you ask for a MenuManager in the same module you are registering it in?
Unless you really think your MenuManager should be something provided by an external module, you might consider putting this registration in your bootstrapper if your modules will be dependent on it. It'd be something you'd put in your ConfigureContainer method of your bootstrapper.
As you've probably realised that's because the constructor is being called before the initialise method.
Two solutions are:
1) Have a ShellBootstrapper class in your Shell project with a method thats called when the program loads.
Have the method in the bootstrapper register any globabl Interfaces with your container.
2) Alternatively take the IMenuManager out of the constructor and just resolve it after you've registered it.
public void Initialize()
{
container.RegisterType<IMenuManager, MenuManager>(new ContainerControlledLifetimeManager());
this.menuManager = container.Resolve<IMenuManager>();
}
Hope this helps!

Resources