I'm using WPF and trying to program the MVVM way.
I understand how every view has its own view model and this works quite well. I am struggling to manage the interaction between views though.
Say I have two views, View1 and View2, each with its own ViewModel, ViewModel1 and ViewModel2. If I have a combobox on View1 and a button, what is the correct way to close the first view, notify the second view of the selection and show the second view once the button is pressed? It doesn't seem like it should go in the model because it's a UI thing. The ViewModel shouldn't know how to open and close WPF forms (or should it?) And the views shouldn't know about any other ViewModels (or should they?)
So how are these problems solved? In a nutshell:
1) How is data passed between views?
2) What manages the lifetime/visibility of views?
It will depend on whether you are doing view model or view first, and the exact implementation details will depend on if you are using an MVVM framework. If you aren't using a framework, then I would strongly recommend you start using one.
In your example, when the button is pressed, a method on ViewModel1 will be invoked. If doing view model first (which I would recommend), you would instantiate an instance of ViewModel2, and at this point you could pass in the combobox selection to the constructor of ViewModel2.
Depending on your framework, there will be different ways of then displaying the view associated with ViewModel2.
For 1) you can sychronize data through the DataModel. Provided each view shares the same instance of the DataModel and it implements INotifyPropertyChanged multiple views can be updated simulateneously.
Your sesond question is a matter of design, as #devdigital states it can depend on whether it is View first or ViewModel first. I would consider the introduction of a Controller class in much as the same way ASP.Net MVC works which controls which view is displayed. You can expose a ViewClosed event on the ViewModel which the controller can listen to and based on your workflow open another view.
You might consider introducing Controllers which are responsible for the lifetime management of the ViewModels. Furthermore, they mediate between the ViewModels.
The sample applications of the WPF Application Framework (WAF) show how these Controllers can be implemented.
Related
I am playing around with Xamarin.Forms (mvvm) with Prism and have noticed that some tutorials show navigating to another Page while others show navigating to a View.
At a high level, I understand the literal difference... however I do not understand when I should use one over the other? I have an inclination that some of the reasoning is around dependencies, for example:
A Page has the instance of User > navigate to a view = User is still present when the back operation is used... meanwhile if you want this same behavior in navigating to and from a page, you'll need to pass the instance around via parameters... Is this correct/the reasoning behind navigating to and from views instead of pages?
In X.Forms View's are only visual objects, they do not support any navigation or any kind of infrastructure. They can only be showed when you navigate to a Page that hosts them, it doest not make sense the phrasing navigate to a View. So you should only use Page's.. In your example project they also only navigate to Page's. In the one you say they are navigating to "Views" it is only in the name, because ViewA is a ContentPage
Technically, in pure MVVM, the ViewModel should know completely nothing about the View and vice versa. When you use Page First Navigation approach you violate the first sentence. Here is an example:
class MyViewModel
{
}
class MyView
{
public MyView()
{
InitializeComponent();
// Alternatively you can do the same thing in XAML
this.BindingContext = new MyViewModel();
}
}
As you see the View is aware of the ViewModel.
When you use a ViewModel First Navigation approach, the decision of which
View should have witch ViewModel is delegated to a dedicated class. This class then is used in a custom NavigationService to match a ViewModel to a View. So it will be possible to navigate from a ViewModel to a ViewModel. This way both ViewModel and the View know nothing about each other. The disadvantage of this approach is complexity.
This is a very short answer, however, I hope you will get the key point. There are many examples of both approaches:
ViewModel First Navigation
Page First Navigation
P.S.: Prism has very nice mechanism that handle navigation. What I wrote above and the examples that I provided are just for low level understanding of this approach. If you want to use Prism you definitely have to be familiar with it.
In the top of my detailcontroller I have a button to show a list of selected items from various options on the detail controller.
I want to initialize and keep the model, the popover and the list objects in the mastercontroller. How can I get a reference to my mastercontroller to reuse these objects?
Sharing data between views is easiest using a model tied to the component. If you make sure that everything that's selected in the master model is contained in that model, you will be able to use the data from the model in the detail view again.
Pulling data directly from another controller would go against the MVC paradigm and is not recommended (although not impossible, using e.g. the sap.ui.getCore().byId() method). I would not go there unless you have a clear view of the consequences.
I would like to use a single WizardViewController with different inputView subviews. The WizardViewController would share a searchCriteria and pass it along to each new step, with each step adding to the criteria depending on the input for that particular inputView.
The problem I am facing is how to structure my objects/views so that I can reuse WizardViewController accepting different types of input/controls. For example, I've thought of using a WizardViewModel that had members like enum wizardViewModelType (for each type of view), UIView *inputView that corresponds to each type of WizardViewModelType, and creating custom UIView subclasses that correspond to the different possible inputViews that would be needed, connecting outlets as necessary in Interface Builder. The problem is that I believe that going down this road will lead to a lot of inter-class dependencies and basically defeat the purpose of separating the inputView logic from the WizardViewController. Furthermore some of the inputView will require me to grab possible values from a web service, which is something that the ViewModel will have to handle, further breaking the logic. Perhaps I need separate ViewController for each type of input view? Or am I thinking about this in entirely the wrong way?
Normally, the right way to do it is by having a ViewController for each UIView. The logic of your application should not be on the view, but on the viewController, and you can share data by using segues (if on ios 5), and I'm sure that there are other methods for ios 4 or below.
If I wanted to share codes between the UIViewControllers, the way I would do to make a wizard is to embedded it in a navigation controller and push the next viewController on the navigation controller stack.
If you want to share code between view controllers, I don't think you should do it on the view controller class. Basically, I would think of two options to do that:
1) write a base class: the base class should extend the UIViewController and your UIViewControllers could extend this base class.
2) extract the common code into another class (better, if applicable): You create another class that share the common code, and inject it into your view controllers.
If I have a bunch of ViewControllers that are only ever going to deal with a single view, or even if my ViewController is going to deal with multiple views, why should I use another ViewControllers to manage the other ViewControllers? Why wouldn't I just change out ViewControllers at the ApplicationDelegate level?
Maybe I'm thinking about ViewController the wrong way? I'm used to writing in the MVC pattern with Ruby/.NET. For an example, if I were working with widgets I'd probably have a WidgetController and a List view, and a Detail view for the WidgetController.
What is the analogous iPhone MVC construction? I'd assume the WidgetController would subclass the ViewController and I'd have a couple different views depending on how I wanted to look at the widget data. Then, when I wanted to deal with Wodgits I'd make a WodgitController with its associated views and swap the window's subview out with the new Wodgit ViewController.
I don't see what having a RootViewController to control my controllers buys me. Where is the value? What am I missing?
I'll focus on one aspect: Compartmentalization is a very useful design principal. If you separate concerns and put things where they "belong", they will be easier to find, maintain, build upon, and co-opt for new purposes.
Consider this:
All the pipes in your house go to the same place. Do you urinate in your sink, or do you use the fixture designed for that purpose?
In the case of your app, the application delegate is responsible for responding to application events and managing universal application resources. The root view controller is responsible for managing your views and view controllers. As a view controller it has special abilities to do so with navigation control, the ability to present modal views, act as a delegate, etc. While you could create a view controller and manipulate it completely in the application delegate, the task will become exponentially more difficult as you multiply the number of views you are managing, especially if you wish to be a text field delegate, data source, etc. On the other hand, it is easy to split the root view controller into its own class. There is very little cost.
Why pee in the sink when the toilet is a step away?
I am trying to wrap my head around controllers in Cocoa Touch. The main problem is that I would like to have more than one controller “on screen” at once – I want to have a large view (with controller A) composed of smaller views controlled by their own controllers (say B). I’d like to have it this way because the division makes the code much cleaner. What’s bad is that the additional controllers (of type B) are not “first-class citizens” on the screen, for example they do not receive the autorotation queries and notifications. (And cannot easily display modal controllers, they have to send the presentModal… message to their parent controller.)
What is the difference between the A and B controllers from Cocoa viewpoint? Does the system keep some kind of pointer to the “frontmost controller”, a privileged one to which it sends notifications and such stuff? Why don’t the other controllers receive them, even though their views are on the screen? Is having multiple controllers “on screen” considered a hack? Or is it supported and I am just missing some point? Thank you.
More about the problem I am trying to solve: I am writing a simple photo browser. Photos are displayed in full screen, user can swipe left or right to change photos. The A controller takes care of the scrolling part and the B controllers take care of each photo itself.
Isolating B seemed like a good idea, since the photos are loaded from network and there is a lot that can happen, like the network might be down et cetera. In the B controller the code is fairly simple, since B only works with one particular photo. If I moved the code to the A controller, things would get messy.
The only thing I don’t like about the current solution is that I have to manually work around B not being a “first-class” controller. I have to pass some calls manually through A to B and when B wants to display a modal dialog, it has to send the presentModal… to A. Which is ugly.
There is now a first-class support for this scenario since iOS 5, it’s called controller containment.
swift controller containment
objc controller containment.
It's not closely related to the original question but important. Apple clearly states in View Controller Programming Guide that a view controller is responsible for controlling exactly one screen's content:
"Each custom view controller object you create is responsible for managing exactly one screen’s worth of content. The one-to-one correspondence between a view controller and a screen is a very important consideration in the design of your application. You should not use multiple custom view controllers to manage different portions of the same screen. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.
Note: If you want to divide a single screen into multiple areas and manage each one separately, use generic controller objects (custom objects descending from NSObject) instead of view controller objects to manage each subsection of the screen. Then use a single view controller object to manage the generic controller objects. The view controller coordinates the overall screen interactions but forwards messages as needed to the generic controller objects it manages."
However in iPad Programming Guide they also say that there may be container view controllers:
"A view controller is responsible for a single view. Most of the time, a view controller’s view is expected to fill the entire span of the application window. In some cases, though, a view controller may be embedded inside another view controller (known as a container view controller) and presented along with other content. Navigation and tab bar controllers are examples of container view controllers."
Up to my current knowledge I would not use sub-view controllers in a view controller but try to subclass NSObject and send messages to them from my main view controller.
Also check this thread:
MGSplitViewController discussion
First, and this is important, view controllers don't get "on screen" -- views do. Your "top level" controller can certainly pass along the kinds of messages you're describing to its "sub-view-controllers". In fact, this is how most apps work. Consider an app that has a tab bar, and where the views use navigation controllers. You actually have several view controllers "running" at the same time, each with its own view on screen at once -- your "root" view controller will be an instance (or subclass) of UITabBarController, which then has several nested UINavigationControllers, each which will display nested view controllers (like an instance or a subclass of UITableViewController).
You might want to read up a bit on how responder chains work. Consider a touch event. It will be generated for the view closest to the top of the stack, that can receive events, which is also underneath the tap. If that view can't handle it, it gets passed up the view hierarchy food chain until something deals with it (and then eats it).
As to the specifics of your question, on the whole, I'm not sure exactly what the strategy you describe is really doing to benefit you in terms of complexity. It depends on how exactly you're implementing things, but having separate view controllers for each little subview may require more bookkeeping code than just having one view controller that knows about all its sub-view components.
This is a pretty old question, but since I guess there are people who might face the same problem today I'd like to share my solution.
I was writing this application that had this one screen with a lot of information, pagination, controls etc. Since according to Apple's MVC documentation on the role of ViewControllers, you should not implement the logic in view itself, or access the data model directly from it, I had to choose between having a Massive ViewController with a few thousand lines of code which was both hard to maintain and debug(even with unit tests) or find a new way.
My solution was to use UIContainerView like below:
this way, you can implement each part's logic in it's own ViewController, and the parent view controller takes care of constraints and sizing of the views.
Note: This answer is just a guide to show the way, you can find a good and detailed explanation on how it works and how to implement it HERE
Actually you can make it work earlier than iOS 5, since most of us are targeting 4.x and 5.x at the same time. I've created a solution that works in both, and it works great, few apps in appstore use it :) Read my article about this or just download and use a simple class that I've created for this purpose.