I am created a MainView that it's DataContext is a MainViewModel initialized in xaml.
The MainView contains a ContentControl that is bound to the Content property of the MainViewModel.
I added some content in the MainViewModel constructor, so that if the current user is not logged in, it automatucally loads LoginView (and correspondingly it's DataContext LoginViewModel) into this Content property.
Now my question is, what should I do when the user successfully logs in:
'To be called from the LoginCommand
Private Sub Login
'Do Login
If WebContext.Current.User.IsAuthenticated Then
' - Publish a global event to be subscribed and caught from the MainViewModel
' - Close LoginView
' - The MainViewModel should set it's Content property back
' to what the user initially intended to open
End If
End Sub
How is this done?
Note: I prefer using prism's EventAggregator rathen then other stuff, but I have no clue:
How to spread it out between the ViewModels
How to create events (I don't need to pass parameter, nor do I need it to be generic, just Action, LoginAction - no parameters.
How do I subscribe from the MainViewMode.
I do NOT use MEF or Unity, nor do I use seperated modules, all my application is in one single assembly.
I prefer not to write any code in the code-behind at all
Answer in both VB.NET or C# are welcommed the same
Any help would be recommended
You can go here for info regarding the EventAggregator.
You could also use the following code to create an instance of the EventAggregator without using MEF or Unity:
internal static class EventAggregatorHelper
{
private static IEventAggregator _Current = new EventAggregator();
public static IEventAggregator Current
{
get
{
return _Current;
}
}
}
And you could then call the EventAggregator like so passing in the required information to the aggregator:
EventAggregatorHelper.Current.GetEvent<SelectedItemChangedEvent>().
Subscribe(HandleSelectedItemChangedEvent);
In this case the SelectedItemChangedEvent and the subscriber that deals with this event.
The SelectedItemChangedEvent is a class declared as shown below:
public class SelectedItemChangedEvent : CompositePresentationEvent<String>
{
}
and the subscriber would be something like this:
internal void HandleSelectedItemChangedEvent(string viewName)
{
if (!String.IsNullOrEmpty(viewName))
{
//Do whatever you need to do here.
}
}
The link to the Event Aggregator I posted at the start should clear most things up for you.
Hope this helps.
Related
I am working on an editor plugin and now implementing support for code hovers.
I have extended AbstractInformationControl (and implemented IInformationControlExtension2) to create a new control for showing the hover info.
It works almost fine, but I am unable to receive property change events in my information control. What I try is like this, but the event handler does not fire:
public class MyHoverInfoControl extends AbstractInformationControl implements IInformationControlExtension2 {
public MyHoverInfoControl(Shell parentShell, String string) {
super(parentShell, string);
create();
Activator.getDefault().getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent event) {
// TODO Auto-generated method stub
}
});
}
The control itself contains a StyledText. I have also tried to add a KeyListener to this StyledText to see if key events are received, and it only seemed to work if I click into the control (it is not enough to hover over a text to show the control). But property change event does not seem to be received even after the click.
Is it possible to receive property change events for an AbstractInformationControl subclass?
I am using serenity BDD for my automation testing and Page Object Model for my framework. I have created a BasePage class which will be inherited by all the other Pages. I want to minimize the logging messages from the Pages by adding all the log.info messages to a central Base page. Example, when calling the click() method, I will log before click and after click methods as shown below in the basePage class:
public class BasePage extends PageObject{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
public static void clickBtn(WebElementFacade btnName) {
log.info("About to click " + btnName + " button");
btnName.click();
log.info("Successfully clicked on " + btnName + " button.");
}
Later I figured that instead of individually trying to figure out in advance what actions the user will perform on the webElements, and write new methods for each action (like the one shown above), just implement WebDriverFacade interface, so that I get all the unimplemented method list in BasePage from WebDriverFacade and then write the log messages inside each of them, like so:
public class BasePage extends PageObject implements WebElementFacade{
private static final Logger log = LogManager.getLogger(BasePage.class.getClass());
private final WebElementFacade element;
#Override
public void submit() {
// TODO Auto-generated method stub
}
#Override
public void sendKeys(CharSequence... keysToSend) {
// TODO Auto-generated method stub
}
#Override
public String getTagName() {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean isSelected() {
// TODO Auto-generated method stub
return false;
}
.
.
.
.
.
}
This will serve two purposes:
I will not have to create new methods for every action in BasePage class, example the 'clickBtn()' function in the first code
As I mentioned before, I will not have to figure out what method any other person who adds methods to my code might use and having to change the BasePage class to create the new actions. So basically less maintenance in the long run.
The problem I am facing is an error that I receive in the second use case:
The return types are incompatible for the inherited methods WebElementFacade.withTimeoutOf(int, TimeUnit), PageObject.withTimeoutOf(int, TimeUnit)
Now my question is:
How can solve this problem?
Is this the right way to do things or should I be going with the first method and have maintenance overhead.
Just figured that another scenario where this might be useful. To make sure that subclass methods do not use methods from pageObject and are forced to use the methods from BaseClass only. This can be done by wrapping the WebElementFacade and adding the log messages as an added functionality. Any thought on this would be appreciated.
Thank you!
Honestly it is a neat trick and if you get it working you should be proud.
I think I figured something similar out in a dynamic language.
But you are better off just adding the logging entries and learning the following.
How to name functions so you don't feel like they need renaming.
How to log clearly for debugging use.
This is because loggings power is in its flexibility.
When you learn how to dump something complex like a matrix so you can eye it and go uh-oh you are increasing your overall skills.
I apologize for not giving you code but I felt some "chase the other rabbit" advice was better.
I have an application with several graphs and tables on it.
I worked fast and just made classes like Graph and Table that each contained a request object (pseudo-code):
class Graph {
private request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
public function refresh() {
request.getData(function() {
//refresh the display
});
}
}
Upon a GUI event (say, someone changes the date range dropdown), I'd just call the setters on the Graph instance and then refresh it. Well, when I added other GUI elements like tables and whatnot, they all basically had similar methods (setDateRange and other things common to the request).
What are some more elegant OOP ways of doing this?
The application is very simple and I don't want to over-architect it, but I also don't want to have a bunch of classes with basically the same methods that are just routing to a request object. I also don't want to set up each GUI class as inheriting from the request class, but I'm open to any ideas really.
As you commented the methods are identical. In that case I would suggest the following approach.
abstract class AbstractGUIElement {
protected request;
public function setDateRange(dateRange) {
request.setDateRange(dateRange);
}
abstract function refresh();
}
Refreshing would probably be element specific so I have added it as an abstract method the inheriting types have to implement.
class Graph extends AbstractGUIElement {
public function refresh() {
// Graph specific refreshing
}
}
I have a User model which is bundled in a module installed on my Yii application. This module is third party and I do not want to alter its code.
I also have a Cv Model that has a BELONGS_TO relation with the User model.
My question is: How can I delete the cv when a user is deleted ?
I know that I can achieve this with on delete cascade ... on mysql. However, i do need to delete other data such as a photo, files, etc.
What I have tried
I have created a component that is preloaded on my application. This component attaches to an onAfterDelete event
class EventListener extends CComponent
{
public function init() {
Yii::import("application.modules.users.models.User");
User::model()->attachEventHandler('onAfterDelete', array($this, 'deleteUser'));
}
public function deleteUser($event)
{
// stuff here ...
}
}
However this does not work.
Any suggestions ?
This may help you.
User::model() is a singleton
$user1 = User::model();
$user2 = new User; // will be used in insert action
$user3 = User::model()->findByPk(10); // will be used in update/delete action
$user1, $user2 and $user3 are completely different objects.
You can attach events to objects, in this case you have to add events to all these 3 objects individually.
$user1->attachEventHandler(...);
$user2->attachEventHandler(...);
$user3->attachEventHandler(...);
look like Yii does not provide any way to add events at Class level.
Well, guys, I have just stumbled upon the same problem and I solved it this way:
You should use the init() of a Model, not your event listener collection class.
In my case I have devModel class:
public function init()
{
parent::init();
$this->onLicenseUpdated = array(new MEventProcessor, 'licenseUpdateHandler');
}
And the handler is licenseUpdateHandler($event) in a MEventProcessor class.
This way every time you work with model instance, it'll call init() for every object and attach the event handler to every instance of this Model.
Now any time the event (in my case onLicenseUpdated()) is invoked for the model - the handler will be called too.
You could also to use Behaviors.
1 - behaviors can listen to events : you just have to override their events() method
class MyBehavior extends Behavior {
public function events() {
return [
ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',
];
}
public function beforeValidate($event) {
// ...
}
}
2 - although it is more common to attach a behavior to a component in the component's behaviors() method, you can also attach them dynamically and keep the original code unmodified :
use app\components\MyBehavior;
// attach a behavior object
$component->attachBehavior('myBehavior1', new MyBehavior);
You will find some useful documentation here :
yii 1 : http://www.yiiframework.com/doc/api/1.1/CBehavior
yii 2 : http://www.yiiframework.com/doc-2.0/guide-concept-behaviors.html
I have a simple container hierarchy with a parent container which is defined in the Shell MEFBootstrapper using a Directory catalog, and child container(s) which get created off the parent with a distinct catalog.
My child containers also use DirectoryCatalog (different path than parent), and I can see that the container has assembly and part information during runtime.
However, the Initialize() method for the modules located in the child containers are never called.
My goal is to use the child containers as session constructs, allowing the user to create new sessions and toggle between them. But if I cannot get the constituent modules to initialize (and place their views into the regions), I am kind of stuck.
I had thought to raise an event from my session manager using event aggregator to allow the modules to listen for the event and self-initialize, but that doesn't seem to work either.
i. Why isn't Initialize getting called on modules loaded into a child container
ii. How can I "trigger" Initialize from the container instance (outside of the module context?) Can you iterate over the assemblies in the container and trigger Initialize that way???
[from MefBootstrapper in shell project]
protected override DependencyObject CreateShell()
{
ExportProvider ep = this.Container as ExportProvider;
this.Container.ComposeExportedValue(ep);
[from the service that manages my sessions (containers)]
[ImportingConstructor]
public SessionService(ExportProvider provider)
{
[constructor for new sessions (containers)]
private void Init(ComposablePartCatalog catalog, ExportProvider provider, string name, int callId, bool useContextProxy)
{
this._Name = name;
this._CallID = callId;
this.startTime = DateTime.Now;
this.appHost = new CompositionContainer(catalog, new ExportProvider[] { provider });
}
=====
Was asked to include my module code whose initialize method is not called (despite being loaded into the container in question...I can even lazy instantiate the module, but calling Initialize() directly causes the injection operation to fail in the method proper.
namespace Module1
{
//, InitializationMode = InitializationMode.OnDemand
[ModuleExport("Module1.ModuleInit", typeof(Module1.ModuleInit))]
public class ModuleInit : IModule
{
private readonly IRegionManager _regionManager;
public IServiceLocator _serviceLocator;
[ImportingConstructor]
public ModuleInit(IRegionManager regionManager, IServiceLocator serviceLocator)
{
_regionManager = regionManager;
_serviceLocator = serviceLocator;
}
#region IModule Members
public void Initialize()
{
// Use View Discovery to automatically display the MasterView when the TopLeft region is displayed.
_regionManager.RegisterViewWithRegion(RegionNames.TopLeftRegion, () => _serviceLocator.GetInstance<MasterView>());
}
#endregion
}
}
I downloaded your code and had a look at it. I immediately found the problem. The bootstrapper is actually getting exports thanks to a DirectoryCatalog like this:
DirectoryCatalog catalog = new DirectoryCatalog(".");
this.AggregateCatalog.Catalogs.Add(catalog);
This means that you will get the exports from the assemblies in this directory. So you simply need to copy all the the assemblies with the exported types in the directory ".", that is to say the executing directory (Debug/bin).
Just copy Module1 and Module2 in the bin directory and everthing will compose gracefully :)
Actually I found that the post-build events supposed to copy the modules in the bin directory were not working. Maybe because your renamed something. So if you want it automatically copy the assemblies after building just replace the actual post-build event by this one:
copy "$(TargetDir)\$(TargetFileName)" "$(TargetDir)\..\..\..\Shell\bin\$(ConfigurationName)\"
I already had this problem many times and resolving it is really simple.
Remove the constructor from the your module. Prism modules are not activated the same way it does for classical exported types, therefore modules cannot use ImportingConstructor to import the services you need. Instead intitialize them with the ServiceLocator within the Initialize method.
This will work:
[ModuleExport("Module1.ModuleInit", typeof(Module1.ModuleInit))]
public class ModuleInit : IModule
{
private readonly IRegionManager _regionManager;
public void Initialize()
{
_regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
_regionManager.RegisterViewWithRegion(RegionNames.TopLeftRegion, () => _serviceLocator.GetInstance<MasterView>());
}
}
I also think that this behavior is kind of disturbing.
I had this same problem where my modules Initialize() method was not being called... I realized I had left off the "override" keyword on my Initialize method that was declared virtual in the Module base class that all of my modules inherit from... added "override" and it worked!