I'm working on a line-of-business silverlight application and I need a piece of advice concerning managing RIA services context lifetime.
Application - afer a few simplifications - is build of one big tab control. At the beginning there are 2 tabs: customer list and invoice list. These are plain datagrids with filtering/sorting and that sort of stuff.
User can add/edit customer or invoice selecting a row and double-clicking. Then the new tab is created with details of customer or invoice. User can open many tabs with different customers/invoices. After editing, user can save and close tab or just abandon edit and close.
The question is how to deal with data contexts.
Should I create one for customerlist and one for invoicelist and when user opens a new tab, I simply bind customer/invoive dataobject to control? This has an advantage that I dont need to refresh grids after saving changes. EDIT: This has some drawbacks. User can refresh grid - and what will happen to open detail tabs? User can also filter grid so some records being edited can be removed from datactx?
The other way is to create datacontext per tabitem. This is more safe but I need to handle refreshing grid(s).
I have no idea which method is better or maybe there is another alternative?
Use one ObservableCollection list in each case and it will automatically update the datagrids when items are changed.
Related
I have a typical master-detail scenario. User can click "Add New" in the master view and enter the details in the detail view. So for example lets say I have list of ObservableCollection of Product. When the user clicks "Add New", I add a new Product record to the ObservableCollection and then open a detail view for adding the Product details. This works well if the user does save the product. However if the user decides to cancel the addition of new Product, how do I remove the same from the ObservableCollection of Product in the master view. Right now I could think of two ways, first to pass the reference of ObservableCollection to the Details view, and second, use events to notify master view about the new Product record deletion. What do you suggest ? Also, if there is better way to handle this in prism specifically, please let me know.
regards,
Nirvan.
Passing a reference to the ObservableCollection to the details view is a no-no - it should only know about the record it is bound to.
You have not specified the UI you have in place for this, there can be a couple of ways to do this that may be better suited for your design. However one reasonably agnostic way to do it is to use the EventAggregator to publish the event which the master view can subscribe to. It is up to the master view how to deal with that event, different views can respond in different ways. In this particular case the master view can check the currently selected item, and if it is new (i.e. hasn't been saved, doesn't have key pieces of info like an ID) then it can simply remove it from the collection and discard it.
I am working on a large MVVM application. I am using the MVVM light toolkit for this. The application is like a web browser with back and forward buttons. The main view is an user control. I laid the back and forward buttons in the main view user control. The main view in turn has user controls. The user controls change when the back and forward buttons are clicked. The main ViewModel keeps track of the current user control and loads the next one depending on the button click.
All these user controls are loaded depending on the selection(ID) made on the first step. Lets say, the main view is a search screen and we select a customer. The next screens would be Address, Billing, Requests, etc. These screens does not share any data. But the data is for the same customer.
So, is it a good practice, to store the customer ID in the main view? If I do this, I should have a UserControl_Loaded event bound to a command, where I would then request for Address and Billing Info.
Or I can move the buttons(back and forward buttons) to each user control instead of the main view, Pass the customer ID with the message which would load the next view.
Which is better?
A way I've done this sort of thing in the past is to implement a class that encapsulates the data context for the operation. All of the pages will be populated with (and update) properties of this class. The main view model creates an instance of this class and a collection of the page view models, providing each with the data context. It also handles navigation from page to page, implementing CurrentPage, NavigateForwardCommand, and NavigateBackwardCommand properties.
If the user backs up to page 1 and changes the customer ID, the data context is repopulated with the information appropriate to the new customer. Since all of the pages are looking at the same data context object, all of the subsequent pages will display the right information.
You'll need to implement property-change notification in the data context object, and handle PropertyChanged in the pages. When the CustomerID property changes in the data context object, the page view models will need to refresh properties that appear in their respective views.
Suppose I have a WPF/MVVM application for managing some hypothetical customers :).
Domain model contains an entity named Customer (represented as a POCO in code).
The main screen contains a grid, bound to a view model (CustomersViewModel) that loads its data from Repository< Customer>.
The main screen also allows to create new customers (and save it to the DB).
Suppose I need to implement 'add customer' use-case. The most obvious approach is as follows:
Present the user with a dialog window to be filled out with new customer data.
Handle 'Save' button click in the ViewModel.
Create customer (var new_customer = new Customer(..)) domain object using the data from the dialog (step 1).
Call Repository< Customer>.Save(new_customer) to save the new customer to the DB.
Reload CustomersViewModel with fresh data from the DB so that newly added customer is visible in the grid.
Personally I don't like this 'quick-and-dirty' way (because of need to reload the full list of customers from DB every time a new customer is added).
Can anyone suggest a better approach (that wouldn't require refreshing the customer list from the DB)??? I feel there gotta be some best practice for handling scenarios like that:) ).
Thanks in advance!
If the saving of the Customer is successful, why can't you just add that single Customer instance to your collection of customers? No need to re-load all customers unless the user explicitly refreshes the view (usually via a refresh button).
If you are loading the list in your view through a binding (to a list of customers) you can just add the new customer to that list and everything is alredy ;-)
I have a similar application where in the object is created in UI. I solve it by adding the object in VM and then syncing it with Model on click of Save button.
I am assuming you have a list of CustomerViewModel in CustomersViewModel to which the grid view is bound to. You can add a new CustomerViewModel object to the list in CustomersViewModel. While saving the ViewModel data back into the model, the model gets in sync with VM. No need to refresh VM back from Model unless somebody else apart from your app is changing the Model data.
You could create an ObservableCollection<Customer> and fill it with the customers from the database which you want to show in the View. When you add a new customer then add it to this collection as well as save it into the database. The CustomersView binds on the ObservableCollection and is updated automatically without the need to refresh the data from the database.
The BookLibrary sample application of the WPF Application Framework (WAF) shows how this can be done.
Please am still new to MVVM, and am using it with Sivlerlight. I run into a scenario where I have a Main UserControl containing one DataGrid, Employees forexample. User can double click any datagrid record to Show the EditEmployeeWindow (non blocking) so users can at the same time edit more than one employee. The only problem I have is that (one of our requirements) that when the users click save on any Edit,New window the data has to be saved to the database directly, and with one model a Save operation will save all the changes.
So one of the solutions on my head is to create an EditorViewModel that has it's own Model (new Instance of the model) and take id of edited record. In the EditorViewModel I will load this single record freshly and would direclty save changes only to that record. Also to use MVVM-Light Messenger to send message to the MainViewModel to refresh it's data since it they'll not be the same anymore.
Please could you guide me! Am I on the right track?
Thanks in advance
It looks OK, but I strongly recommend you to store the list of editing records somewhere in the main view model to not allow multiple windows on same record. Also, as you already going to use messenger to communicate with main view model, when main v.m. will receive it's child window closed message, send message to other windows, forcing them to close, and then call refresh method on main v.m.
It actually may be a little bit tricky to identify when all windows are closed (as you do work async), there are a lot of ways to do this, but I recommend you following:
1) Track a state of the main view model (is it has opened window, waiting for all windows to close, etc)
2) When window commit operation finished notify main v.m. and remove id form the list of active records.
3) When there no more records left in the list, refresh data.
Eventually when you will have to add more logic related to popup windows, I recommend to extract popup related code to the class named something like PopupService, it should be singleton, but I strongly recommend you to get it instance through one of the IoC containers (Unity, MvvmLight one, or event MEF).
I'm not really sure how to do this in the best MVVM way...
Basically, my main app opens up a search window that shows all records in a TabPanel. Then if a record is double clicked a new tab is opened with that record. Now, I'm trying to keep things MVVM, but I can't for the life of me figure out how to close the gui tabitem when a person deletes the record (why keep it open if the record is gone).
The only way I can figure out how to do it now is to pass the instance of the TabItem as a parameter of the DeleteCommand, which to me seems like a big no-no, but I can't for the life of me figure out how to accomplish this.
If you were going to do this in the true MVVM sense, then double-clicking a record would, behind the scenes, add a record to a collection of records. That record collection is the datasource for the tabs in your control. Simply removing that item from the list (usually an ObservableCollection<T>) would result in the UI updating and the tab being removed.
Which approach are you currently using to show the tab?
Edit (in response to comment):
That is not "true" MVVM. It doesn't matter if the tabs can be more than just records. You should create View Models which abstract those details, then just put your view model instances (RecordViewModel, ReportViewModel, etc) in an observable collection and bind to that. Use datatemplates to render the correct views for each tab's content based on the type of view model the current tab is being bound to.
Josh Smith wrote an amazing article describing how MVVM works. The sample application does something very similar to what you want to do.
The application displays a TabControl which displays 1 or more workspaces. The workspace area displays two different kinds of items. The tab items are closable. Take a look, I'm sure this will solve your problem.
WPF Apps With The Model-View-ViewModel Design Pattern