I am developing a Prism application where I need to load some data in the model in the background after the application has loaded. I have a separate view which is hidden which would show this data via a view model. I dont want to increase the application load time and neither do I want to increase the view load time. The only solution I could think of is to raise a Shell Loaded event and let the view model or model subscribe to it.
Is this a good approach or is there a better approach?
Also, I am not sure whether I should raise the Shell Loaded event in the Run of the Bootstrapper or in the Shell ViewModel. How do I link a routed command (for ex: Loaded) to an ICommand
I try to avoid to handle these kinds of events in the bootstrapper. So attaching the Loaded event of the shell view to the shell view model is a good way to go. The only reason to actually raise this event in the bootstrapper is if you need the StartupEventArgs (which I sometimes do).
There are quite a few ways of handling the loaded event.
100% MVVM use an attached property that passes the event straight to an ICommand or method. Ex: how to call a window's Loaded event in WPF MVVM?
Just handle the Loaded event it in the code behind and call the Loaded method / Command in the VM from there. This a simple and easy solution to get started.
To reduce loading time, you should try to avoid putting too much work on the UI thread. You may want to investigate the TPL or a nice framework called ReactiveUI which provides a very convient mechanism to load things asynchronously.
Related
https://github.com/brianchance/MvvmCross-UserInteraction is a very nice plugin for showing cross platform Alerts!
But for this question, can we assume it can not use a UIAlertView (or some other top level MessageBox type call on other platforms) but needs to show a Message within a given subsection of the screen (i.e. on IPhone you would need to supply a UIView to the plugin which it will use to show the message within).
So, how would you set this up so the ViewModel knows what View to use as its display container?
As a specific example, if I wanted an Error Service, as so -
public interface IErrorPFService
{
void Show();
void Hide();
void SetErrors(List<Error> errors);
}
and I create a platform specific implementation for it.
If I inject this into my ViewModel so it can control Error Show/Hide/Set how do I tell it the UIView (or equivalent) that I want my Errors to show within?
Can I just expose the IErrorPFService field as a public property and do -
MyViewModel.ErrorPFService = new ErrorPFService(View);
in my ViewDidLoad ...
Or is this coupled incorrectly vs Mvvm Practice?
I would expect the ViewModel to subscribe itself to the ErrorService.
When receiving a message it would expose it in a collection(?) and the View would bind to that collection.
This way the View is unknown to the service and the ViewModel has the chance to influence the View contrary to your solution.
It would help if you could give an example for the scenario you are describing.
Sometimes, the way you visually want to display something might not be the best way, so if it's possible for you, you might find a different and simpler way, which spares you from having to find a solution regarding what you are describing.
Generally, I always do the best I can to avoid the idea of having to actually pass a 'view' or an abstraction of it, from the view-model to view. Also, cross-platform wise, things can work very different in terms of UI interaction. You can find yourself in a situation when things are complicated just because UI works differently than what you expected.
But let's try find another perspective:
At any given point, the view knows what data \ feature it's displaying. So when you are calling from the view-model an user interaction action (by a service, property change, event, etc) the view should 'expect' it.
For example, the platform specific user interaction implementation is able to get the currently displayed top-view and interact it in a platform specific manner or based a relationship. In your example, the message-box can be displayed in a specific sub-view of the top level view.
In advanced scenarios, I guess you could try to create a cross-platform approach for this, but you should try to put in balance all the abstraction you want to create just for that. Think about doing this as a plan ... Z. If possible. Again, giving an example might help.
My application is designed to load up an XML file and display an error(s) (if any).
The problem I have is how to display both (the XML and Errors) on screen without coupling (my application does currently work).
My application currently looks like (no laughing or comments about me going on a Photoshop\UI course please):
The brown colour is a different view called XmlView.
The red box is where I want errors to be displayed.
So, the user clicks File->Open, selects the file and the .XML content is displayed in my XmlView (brown) and my error messages are shown in red. This works but, I have a horrible feeling my design is poor as I have totally coupled my MainWindow and XmlView.
The way I have this working is, when the user selects a valid XML file (from File->Open), I create an instance of my XmlView and bind it to my Views property of my MainWindow class. My XmlView takes 1 parameter which is the MainWindow type.
So, within my XmlView, to update my ErrorList, I would write code similar to
_mainWindow.ErrorList.Add(//newError)
But this now means my XmlView knows about my MainWindow which I thought was undesired.
So, finally, my question! Is my design poor or is this OK?
You should consider using an MVVM framework if you are doing MVVM.
It would depend on whose responsibility it was to load the XML, but I would suggest the XmlViewModel, not the MainViewModel.
In that case the MainViewModel should just be a conductor of other view models. In your first case, it would instantiate the XmlViewModel, passing the file path and set it as its current view.
The XmlViewModel would be responsible for loading and validating the XML. It too could have a child view model which displays the validation errors. It should load the XML asynchronously, with some form of busy notification.
The MainViewModel is likely to want to conduct many view models, so if you were going to use a framework such as Caliburn.Micro, this would be a conductor type.
Where exactly is the limit to adopt VM so it can suite better a particular View? Example:
There should be a command in UI (ex button) that should allow adding new item. Additional requirement can be that new item should be selected, ensured that its visible on control (lets say TreeView control), and to begin edit on the newly added item (in order to change predefined value that was set in VM). Lets assume that control doesn't have automatic mechanism to achieve this, so we need to do it manually. So the execution flow looks like this:
invoke add command on VM - done is View's xaml.
set SelectedItem to new item (usually we bind control's SelectedItem property to VM's CurrentItem property, and then just assign new item to CurrentItem.
ensure that new item is visible on control - this must be done in View's code behind.
Start editing - this must be done in View's code behind.
Now, since everywhere on net there are articles on using messages for almost everything, a question:
What do I break if I do it in the simple old fashion way? I use Click event instead of Command binding on adding new item, and in the method I do this:
// in View's Click event handler
ViewModel.AddCommand.Execute(null);
EnsureVisibleSelectedItem();
BeginEdit();
.. clean and clear! And what do I gain if I do it using messages:
// in ViewModel's AddCommand
AddNewItem();
SetCurrentItem();
SendMessageToEnsureVisibleSelectedItem();
SendMessageToBeginEditSelectedItem();
... where View has registered to receive these two messages.
Any light on this is greatly appreciated. To my opinion, UI can change, and VM should be able to adopt new UI without making changes to itself, so I dont quite understand current MVVM policy that is preached on internet.
I would say "make it simple".
What's really important in MVVM is:
what doesn't depend on the view should go in the ViewModel (your ViewModel must not be aware of the view in any way - not just by object reference)
everything else in the View and its code-behind.
Yes, in its code-behind. There's nothing wrong in writing code-behind if it is code that is related to the view, not logic. For instance, drag & drop management should be written in the code-behind.
To answer your question, you do not break anything in writing:
// in View's Click event handler
ViewModel.AddCommand.Execute(null);
EnsureVisibleSelectedItem();
BeginEdit();
Everything that is not related to the view is in the ViewModel, everything else in the View/code-behind. That's just fine.
No if I look at your second example:
// in ViewModel's AddCommand
AddNewItem();
SetCurrentItem();
SendMessageToEnsureVisibleSelectedItem();
SendMessageToBeginEditSelectedItem();
AddNewItem is OK (not related to the view), SetCurrentItem is OK (not related to the view), but what about SendMessageToEnsureVisibleSelectedItem and SendMessageToBeginEditSelectedItem?
EnsureVisible is typically useful for a treeview, but what if your view wasn't built with a treeview? What if the control would automatically make the new selected item visible? Of course you could ignore the message, but you would have written some useless code in ViewModel because you thought your view would need it for UI display.
You have typically written here some code in the ViewModel that is aware of how the View should be working. Yes, you have reduced the number of lines in the code-behind, but you definitely have broken the pattern.
Your "old fashion way" is actually a good way for your needs. Your ViewModel is not aware of the view, that's what's important.
Caveat: I'm still struggling with proper MVC in Eclipse plugin development, so if you see anything here that is most likely causing me more pain that I should be enduring, please let me know.
The question:
I have a View with a JFace Tree Viewer and a Table (not a table viewer... that will be changed down the road).
I have an action that is initialized with a reference to the View (this seems terrible to me, but I don't yet know how to do it the right way). When the action is run -- via a button on the view -- the action:
1) gets the Tree Viewer from the View
2) gets the underlying model
3) creates a Job
a) inside the job, loops over the model and does various things to it, including adding additional children into the model
b) uses a function exposed in the view that "clears" the Table in the view
4) adds a JobChangeListener that implements "done()".
a) inside the done() method, it expands the treeviewer via this code:
loadMethodsJob.addJobChangeListener(new JobChangeAdapter(){
public void done(IJobChangeEvent event){
view.enableActions();
view.getTestsViewer().expandAll();
}
});
Inside the Job, whenever I attempt to access the elements in the viewer, I get Invalid Thread Access errors. I believe I understand why I get them when running inside the job, but I'm not sure how to work around them correctly if I can't interact with the widgets in the job change listener. I can get it to work if I wrap every interaction with the widgets in a getDisplay().synchExec(....), but I seem to remember reading that this is not preferable.
I feel like I'm on the cusp of a big leap in understanding with Eclipse SWT, so I appreciate any guidance in getting there.
Any UI component in SWT can be accessed only by a UI thread.
Since the done method of the job runs in a separate non-UI thread, the invalid thread access is fired.
By wrapping every interaction in a Display.syncExec , you are making sure that it runs in the display thread (The UI thread).
There shouldn't be any problem with the above approach.
I'm a beginner with Eclipse RCP and I'm trying to build an application for myself to give it a go. I'm confused about how one actually goes about handling model objects. None of the examples I can find deal with the problem I'm having, so I suspect I'm going about it the wrong way.
Say I need to initialise the application with a class that holds authenticated user info. I used my WorkbenchWindowAdvisor (wrong place?) to perform some initialisation (e.g. authentication) to decide what view to show. Once that's done, a view is shown. Now, that view also needs access to the user info I had earlier retrieved/produced.
The question is, how is that view supposed to get that data? The view is wired up in the plugin.xml. I don't see any way I can give the data to the view. So I assume the view has to retrieve it somehow. But what's the proper place for it to retrieve it from? I thought of putting static variables in the IApplication implementation, but that felt wrong. Any advice or pointers much appreciated. Thanks.
The problem you are facing here is in my opinion not RCP related. Its more an architectural problem. Your view is wired with business logicand!
The solution can be done by two (common) design-patterns:
Model-View-Controler (MVC)
Model-View-Presenter (MVP)
You can find plenty information about this in the web. I am going to point a possible solution for your particular problem using MVP.
You will need to create several projects. One is of course an RCP plugin, lets call it rcp.view. Now you create another one, which doesnt make UI contributions (only org.eclipse.core.runtime to start with) and call it rcp.presenter. To simplify things, this plugin will also be the model for now.
Next steps:
Add the rcp.presenter to the
dependencies of rcp.view (its
important that the presenter has no
reference to the view)
Export all packages that you are
going to create in the rcp.presenter
so they are visible
In rcp.presenter create an interface
IPerspective that has some methods
like (showLogiDialog(), showAdministratorViews(User user), showStandardViews(User user))
Create a class PerspectivePresenter that takes IPerspective in the constructor and saves it in an attribute
In rcp.view go to your Perspective, implement your interface IPerspective, and in the constructor create a new reference presenter = new PerspectivePresenter(this)
call presenter.load() and implenent
this in the presenter maybe like this
code:
public void load()
{
User user = view.showLoginDialog(); // returns a user with the provided name/pw
user.login(); // login to system/database
if(user.isAdministrator())
view.showAdministratorViews(user);
else
view.showStandardViews(user);
}
As you can see, the view just creates a reference to the presenter, which is responsible for all the business logic, and the presenter tells the view what to display. So in your Perspective you implement those interface functions and in each one you can set up your Perspective in a different way.
For each View it goes in the same way, you will need a presenter for the view which performs operations and tells the view (using the interface) what to display and passing down the final data. The view doesnt care about the logic. This is also very usefull when using JFace-Databindings (then only bound data is passed to the view).
For example, the WorkbenchWindowAdisor will just create everything that is needed in the application. Other views, perspectives, then can enable/disable menus and so on depending on the data they got (like when isAdministrator you might want to enable an special adminMenu).
I know this is quite a heavy approach, but the Eclipse RCP is designed for big (as the name says rich) applications. So you should spend some time in the right architecture. My first RCP app was like you described...I never knew where to store things and how to handle all the references. At my work I learned about MVP (and I am still learning). It takes a while to understand the concept but its worth it.
You might want to look at my second post at this question to get another idea on how you could structure your plugins.