Can I create an instance of the ViewModel in the View or it's inappropriate? I would need it, because the Command of a MenuFlyoutItem dynamically changes depending on the content of the Button (the Button content depends on a Json file).
Usually there are two approaches for ViewModel lifetime - singleton and tied to a specific instance of the view. For global ViewModels that are tied to a certain View for the whole lifetime of the app you will create a singleton instance of the ViewModel. Example for this could be the ViewModel of the root page of the app. This page will definitely appear only once in the navigation stack so you can safely use just one instance. A problem appears when there is a way for the user to repeat one View multiple times in the navigation stack. If you were using just one singleton instance of the ViewModel, by navigating to the second instance of the View you would essentially lose the the data tied to the previous instance and after navigating back you would see different data. Example would be a shopping app, where you have a product (A) and in the product view you have a list of related items. If the user selected one of them (say product B), then in cas of singleton ViewModel the instance would be refreshed to contain data of product B and when going back you would have to manually change the data back to product A. By having multiple instances of one ViewModel for each instance of the tied View in the navigation stack you can avoid reloading the data, which could be costly in case they are acquired from a remote service, for example. You can always manage all active ViewModels in a global array and remove the references to the ones no longer needed, so they can be freed up from memory.
Related
I'm a little confused about something with Prism. Each time I navigate my main region to a different view and I handle the PageNavigationCompleted event, I check the list of active views for the region.
var views = RegionManager.Regions[Regions.Main].ActiveViews.ToList();
And every single time the list returned contains one and only one view: The view I just navigated to. No matter how many times I navigate or how many different types of views I navigate to, the list holds at most, one item. (This jibes with my experience implementing IActiveAware in my view-models. When Prism sets one view-model's IsActive property to true it sets another's to false.)
Currently, my code assumes that the first view in the list of active regions is THE active view. Because so far, it always has been.
But if there were always only at most one view active per region, then why would active regions be a list and not just a single object reference?
There must be scenarios in which the list of active views for a region contains more than one, right? Can anyone tell me what that scenario might be? Even visually, what sort of layout is that?
It depends on the control that hosts the region, and on the type of the IRegion instance which is derived from that by the RegionAdapter used. A ContentControl, for example, can only host one view at a time, obviously.
An ItemsControl, though, hosts all its views (i.e. items) at the same time.
I am new to WPF and MVVM, and I am a bit confused on how to use model and viewModel for what I need.
The application will have a global "World" object, that will contain a collection of objects of various custom classes that will be created, destroyed and updated continuously by background threads that listen to ports and sockets, being this the body of the application logic. This collection of objects needs to remain static throughout the execution of the application, regardless of page navigation, and be accessible from custom classes and editable from the UI (there will be a page for an overview of the "world" status, as well as pages for detailed views, editing of objects and configurations).
From what I've read, I gather this data should be maintained by the model, and the ViewModel should access it to prepare it for the View. Does this mean that, for every "world" custom class that I create and need to be viewed or edited by the user, there should be a ViewModel to match?
Looking at code samples and tutorials, I see that viewModels objects are linked to views in the XAML code and instantiated when said view page is loaded (and destroyed on exit). How do I link that viewModel object to a specific and existing Model object, from which it should obtain all data and to which the UI input data has to be saved?
To summarize:
World object collection is created in application start (app or
mainWindow scope). For example objects of class Task are created.
Background processes are initiated, that access the tasks collection and do things depending on what they find. (also they can change data, so it has to notify the modelview to display the changes).
GUI navigation is initiated, and controls are created dynamically, that view and edit the data in the world objects. For example, display controls to manage multiple Tasks.
How do I link the Model objects (e.g. a specific task) with the viewModel? On XAML? in the code behind of the viewModel, with some kind unique identifier for each Model instantiation? When adding the controls dinamically in codebehind, passing the model object instance to the viewModel?
What's the best way for the model to notify changes to de viewModel and viceversa?
I would appreciate any guidance or a reference to a code sample that solves a situation like this.
Thanks in advance
Have a service through which each viewmodel can access your model object - see mvvm services
Ok, this is going to be a 1000ft long question, but there's a lot to cover so here goes:
I am creating a paged items control, the purpose of which is to display very large collections in a paged format. I've created a repository on GitHub which can be found here. I have removed any styling for simplicity.
Upon starting the application, it looks like this:
This is pretty straightforward really, there's navigation buttons, an items per page selector but that isn't really important. The problem here is when you click the button "Open New Window".
This will open a new MainWindow, but on the first window, the collection disappears, as shown below:
The image above shows the old window in front, as you can see, there is no list of content as there is on the new window.
So, after smashing my head against a wall for a couple of hours, I am in need of assistance. I'll provide an overview of how the project is structured.
AnagramPagedItemsControl
The control being used for displaying the content is a custom control called AnagramPagedItemsControl, it is responsible for handling navigation between pages. I think the key property here is the PagedCollection.
The PagedCollection dependency property holds the collection which is bound to the Models property in the TestItemsViewModel.
TestItemsViewModel
This is the DataContext of the MainWindow, each window instance should create it's own view model. The CreateTestItems() method is responsible for creating the list of test items.
LazyPagedCollection
The purpose of this collection is to encapsulate the logic of a paged observable collection, it only loads pages when they are needed, hence the laziness.
It exposes methods like NextPage which are called in the AnagramPagedItemsControl when the user clicks on the various navigation buttons. The view model can also call navigation on the LazyPagedCollection, this allows the view model to call navigation without having to go through the view to do it.
TL;DR
When I create a new Window, the content of the previous window disappears. The problem is almost certainly with the control however I am stuck as to how to fix the problem.
This is quite a large problem to look at so I'd be very grateful for anyone who can look into it. Again, the source code is here, please feel free to suggest alternatives or pick out anything that I may have overlooked.
Had some time to spare, so:
The problem is the setter for the CollectionView property in the style for AnagramPagedItemsControl in generic.xaml.
This does not instantiate a new ListBox every time the style is applied; it will just create the one ListBox, the first time the style is created, and use that value over, and over again. So in effect, every instance of MainWindow shares the same ListBox.
You can see this by setting the Tag property of PART_CollectionView to (for instance) "1" in SetupBindings(ItemsControl PART_CollectionView). When you open a new window, you'll see that PART_CollectionView.Tag contains the same value you previously assigned.
Events occur in our Model and ViewModel that necessitate creating the appropriate View. The question is how to do this and avoid having any View code in the VM or M?
Here's the sequence of events so you can see this dilemma:
The user sets a number fields in a form to start a long running background process and then clicks the "Start" button. If this long running process succeeds then it needs to popup a chart with graphs to show the results. However, if the data fails processing for any reason then it can't popup charts, instead it logs an error message which is show in the a text box on the form.
Right now, that start button calls a method in the ViewModel which actually starts the background thread.
Only the background can determine when or if to create the view.
Currently we have this working by using an interface called ChartInterface. The view implements this interface and then sets a callback delegate all the way down to the backend model. When it decides to create the Chart it invokes the callback and uses the interface to pass the appropriate data and such.
However, this presents a problem because it can potentially produce dozens or hundreds of charts. So we need to have a "Dashboard" with a list of all the charts for the user to select which one to see.
So now the backend needs to decide when or if to create the Dashboard View, then add Chart View to it.
So it's getting messier because there will be increasingly more of these situations as we have lots of Models that need views so creating tons of callback delegates gets ugly fast.
An idea that seems to simplify instead of lots of callbacks will be to only pass an interface to a ViewBinder to the backend. Then each time it creates a model object, it can pass it to the ViewBinder to see if it wants to bind any view object to it.
Our thinking is that most any of the backend objects will be interesting (eventually) to monitor graphically. So if everyone of them after contructing is passed to the ViewBinder interface, then the view can decide if it wants to bind anything to it.
This is sounding better all the time.
The answer became clear while working on the code.
public interface ModelBinderInterface {
void TryBind( object model);
}
instead of one global "server locator" it's more natural for EVERY view object to implement this interface.
Then when it creates any ViewModel objects it assigns itself to the ModelBinder property of the the viewModel object.
Now the ViewModel can pass this same interface to the back end process.
When ever any relevant model gets instantiated, then it calls the ModelBinder with the object.
Then the View object can decide if it can instantiate the object, if not, it can pass the call up to it's parent which also implements ModelBinderInterface.
This way each view can handle instantiating views that it understand whether that be adding a control to a DataGridView or binding the object to a ListView, etc.
Of course, this still allows for a singleton ModelBinder because the lower levels can keep handing the call up to the top level application ModelBinder which there's only one and it can offer singleton instances.
First off, I'm new to MVVM, so please help me out on this :)
Suppose I have multiple views in my app. In my case, I have an editor view, and a browser view.
Both of them have to work with a viewmodel of a "node" I'm editing.
So where does the viewmodel actually get created ?
Suppose the Editor is told to edit a certain node - It could create a new "NodeViewModel" and work with that. But at the same time, there's a NodeBrowserView, which allows people to shortcut-select a different node.
Basicly - I need the EditorView to work with the same ViewModel as the BrowserView, so I need a generic "GetViewModelfor(X)" method.
So how is this supposed to work ? :)
Cheers :)
Both your editor view and browser view should operate on some kind of NodeViewModel. You shouldn't need separate view models just for the different view scenario.
Now, can you edit not-yet-shown-to-user node? If no (as in, user decides what is edited), view models should be created at the very first time their content needs to be presented to user. In most cases this would in some browser/details views, so that user can select element and then chose to edit it.
Edit:
Regarding your comment. NodeViewModel should be provided for editor view.
The providing part can be done for example via constructor injection or by setting view's data context manually. For example, when user browses all nodes in the browser view, he can double click on the list item and editor view will pop-up:
// this will probably be done in response to event
private void ListItemDoubleClick(object sender, EventArgs e)
{
NodeViewModel currentItem = // extract current list item
EditorView editorView = new EditorView(currentItem);
editorView.Show();
}
Alternatively, if you want to avoid this kind of strong coupling between CompositeView and EditorView you can always use events, however it's not always necessary.
One more thing I was thinking of in terms of design would be adding extra view model, call it NodesListViewModel. How the program flow might look like:
At application startup, get your nodes (be it from DB, file, service, anything)
Create instance of NodeListViewModel which takes dependency on IList<Node> (list of node entities)
NodeListViewModel will build and expose collection of NodeViewModel elements
Create instance of your main program window, which uses composite view. It needs NodeListViewModel as its data context.
Whenever user decides he needs to edit item, it's all ready. Browser has a list of all NodeViewModels, it can easily pick up current and pass it to dedicated view.
In cases like this I prefer to use a single main view model and have a "current item" that the view connects to instead. This is a lot easier to do instead of passing / creating new view models around each time a user clicks a different node / grid row / etc. I really see no need to a separate view model either when the same operations can be achieved in the overall view model. It reduces complexity and reduces the change of creating objects (view models) and leaving them hanging around because a reference to them was not released until the application is closed.