I'm trying to edit a Master-Detail View multiple times in Catel.
My question is, if there is no common way in catel to handle a Master-Detail-View and multiple editions using the SaveViewModelAsync and CancelViewModelAsync methods?
The workflow works when my Master-ListItem is no UserControl with it's own ViewModel and when I change the selected Master-ListItem after save or cancel, so that a new ViewModel will be created. But I don't want to null or change the selection after a save/cancel. Also I have maybe to create a UserControl + ViewModel for the Master-ListItems.
Restrictions of the question:
I have got the information from Geert van Horrik from the discussion of his answer, that
multiple edit cycles are not supported directly
one Model should only be accesd by one ViewModel
Problem:
I can use the the SaveViewModelAsync and CancelViewModelAsync only once. After that, the ViewModels which are alive
will get no updates from the Model
don't create a BackUp from the Data, so that the changes can be reverted again
Code to Reproduce:
I have created a WPF Project, where I tested this all.
Possible solution:
I could use the EditableObjectHelper and handle this by myself
I have to instantiate the VewModel again, after the save or cancel, like it happends on a selection change in my Example Code
Restricion of the solution:
If I use a specialized UserControl for the ListItems of the Master-View, 2 ViewModels are looking on 1 Model. After the SaveViewModelAsync the ViewModel doesn't get any Notifications of the Model. So I would have to instantiate this ViewModel again too. But I would Breake the 1-1 relation of the ViewModel-Model.
My conclusion:
It seems, that I have to instantiate all these ViweModels and handle this workflow all by myself. It seems, that the ViewModelLifetimeManagement.PartlyManual (CloseViewModelOnUnloaded is Obsolete) doesn't work here. On selection change, always a new ViewModel will be created, the old one will probably just not be closed.
I don't want to misuse the framework. Maybe I have missed something. I hope somebody can help me or give me a tip, how to handle this.
Catel calls Save / Cancel as soon as the view model get's unloaded. If you want to do "intermediate" saves without changing the data model, you can:
Save the master list which will save the dirty models (you can directly modify the models from within your vm's)
Create a custom command (SaveData) that you can run from an explicit button (or input gesture binding) to save the data without calling SaveAsync on the vm
After the answer of Geert van Horrik, I currently ended up , don't using the SaveViewModelAsync and CancelViewModelAsync methods and handle the save and cancel by my own, so that the notification will not breake. I'm using the GetChildViewModels() method on the ViewModelBase and the EditableObjectHelper.CancelEditObject(Model);
Related
I am testing Catel MVVM and I would like to use the implemented IEditableObject. I have got some questions, but the documentation I've found, isn't very detailed in this point.
Did someone have a helpful link, how I have to set this up or how it works, or something like this? Or should I really have a look to the source code, to get these points and get a feeling, how catel do the work.
The questions, that I have got, are these:
What exactly does the method SaveViewModelAsync()?
Where does it save the data, or where can I configurate it?
How can I use it with Orc.EntityFramework6, or do I have this manually?
What's the different between SaveViewModelAsync() and SaveAsync()?
What's the different between CancelViewModelAsync() and CancelAsync()?
I can only cancel the editing one time. If I edit the same ViewModel again, the cancel has no effect anymore.
I think there is only an BeginEdit() missing after the first cancel, like this documentation suggests. Here some informations to this point:
I edit the ViewModel and the Model set the new value
I execute CancelViewModelAsync(), the setter in the Model is not touched
I edit the ViewModel and the Model set the new value. The current value is the original value
I execute CancelViewModelAsync(), nothing happened
I edit the ViewModel and the Model set the new value. The current value is the edited value from step 3, like the View shows
Thanks for help
Lots of questions in a single question, but will try to answer them:
Q1) What exactly does the method SaveViewModelAsync()
It calls IEditableObject.EndEdit on all models that support it (and are decorated with the ModelAttribute
Q2) Where does it save the data, or where can I configurate it?
It just approves the changes to the model, it doesn't "save" anything. So for example, if you are using Catel models, it will commit the changes made by the VM. If you would cancel, it would revert the model back to the state it was when you initialized the VM.
Q3) How can I use it with Orc.EntityFramework6, or do I have this manually?
You have to do this manually. The VM's in Catel work with models, it's up to you when / where you persist them to (e.g. a database, disk, web service, etc)
Q4) What's the different between SaveViewModelAsync() and SaveAsync()?
SaveViewModelAsync is the public method being called and takes care of the plumbing for you. SaveAsync is a method you can override to add additional save logic (e.g. storing in database, update services, etc).
Q5) What's the different between CancelViewModelAsync() and CancelAsync()?
See Q4
Is there a best practice for how to enable (CanExecute) a button in an other userControl?
A simple scenario is as follow.
When selecting one row in a datagrid in UserControl_1 a button should be enabled in UserControl_2 and also provide the selected row to UserControl_2's viewModel.
When the user then push the button in UserControl_2 it will be executed with the data from the selected row from UserControl_1.
Thanks from a Catel newbie.
If you are using WPF (assuming you are), the CanExecute will be triggered by the WPF system (CommandManager to be precise). Catel should automatically requery the commands when a property changes.
If the logic (or at least the data) for the CanExecute should be shared among several user controls (and I am talking about the view models for the several user controls), I recommend to create a service which is registered in the ServiceLocator. In the CanExecute of the commands you can query the same data (the service) and return true or false according to the logic you want to implement.
You may take a look at this post. And you 'll have a good idea about your problem. I don't post the code here since it is an answer of someone else. Please refer the link.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/198c4a4b-b4c2-4dfc-b99b-afb2a11eb4c3/call-button-even-from-user-control-to-another-user-control
I have two instances of the same ViewController class accessed in different tab items. Both use the same entity, but with a different predicate. One displays all the items, while the other displays a subset based on its predicate.
The problem occurs when I delete an object from the "All" list. It updates immediately, but when I switch over to the other tab, the object is still there, even after going back and forth in the views. Only after a period of time, around 5 to 10 seconds, does the deletion get reflected in the other view.
The ViewController class use a FetchedResultsController.
Any ideas what the cause is and how to get the results to immediate appear?
Just put a reloadData into viewWillAppear. You can also catch this when the tab bar's selected index changes.
Apparently, there is no solution. There is no way to update UIManagedDocument manually.
This guy came to the same conclusion:
Core Data managed object does not see related objects until restart Simulator
So the solution is to use the default master-detail template and to stop using UIManagedDocument. Wish there was some documentation on this, would have saved me a day of my life.
Let’s suppose there is an entity called PERSON in a CoreData model and a NSPanel (displayed as a Sheet) is used to ask for the information needed to create an instance of such entity. The NSPanel also has a Save and Cancel buttons.
What would be the best way to implement this?
My current approach is to create the NSPanel and add outlets to NSTextField’s corresponding to the FirstName and LastName attributes of the PERSON Entity.
For the Save button I’ve created a Save Action that “reads” the data entered by the user through the Outlets and manually create a PERSON Entity with the provided information.
For the Cancel button, I’ve created a Cancel Action that just closes the NSPanel.
Even though the whole thing works as intended, I find this approach messy. Things get even worse with the need to implement the Edit functionality since a new method is needed to read the data from the selected Entity and pass it to the NSTextField’s, and add the required logic to the Save Action to account for the update operation.
I wonder if a full CoreData/Binding approach is possible in this scenario?
The main issue with the Full CoreData/Binding approach, at least to my short experience on these frameworks, is the fact that once the user start entering the info on a new instance, is already to late Cancel operation. Right?
The setup you describe is by no means messy. Rather, it provides ample opportunity to enhance the UI experience.
Your last point, or "main issue", is not a problem at all. You can put up the NSPanel and have the user input new values, and simply dismiss them if Cancel is pressed. Only if the Save button is pressed do you insert a new object into the core data managedObjectContext and save.
Similarly, when editing you simply retrieve the data before and populate the fields. You keep a reference to the record, if Save is pressed you modify the attributes and save the managedObjectContext. If the operation is canceled you simply discard the reference.
I'm wondering what strategies people are using to handle the creation and editing of an entity in a master-detail setup. (Our app is an internet-enabled desktop app.)
Here's how we currently handle this: a form is created in a popup for the entity that needs to be edited, which we give a copy of the object. When the user clicks the "Cancel" button, we close the window and ignore the object completely. When the user clicks the "OK" button, the master view is notified and receives the edited entity. It then copies the properties of the modified entity into the original entity using originalEntity.copyFrom(modifiedEntity). In case we want to create a new entity, we pass an empty entity to the popup which the user can then edit as if it was an existing entity. The master view needs to decide whether to "insert" or "update" the entities it receives into the collection it manages.
I have some questions and observations on the above workflow:
who should handle the creation of the copy of the entity? (master or detail)
we use copyFrom() to prevent having to replace entities in a collection which could cause references to break. Is there a better way to do this? (implementing copyFrom() can be tricky)
new entities receive an id of -1 (which the server tier/hibernate uses to differentiate between an insert or an update). This could potentially cause problems when looking up (cached) entities by id before they are saved. Should we use a temporary unique id for each new entity instead?
Can anyone share tips & tricks or experiences? Thanks!
Edit: I know there is no absolute wrong or right answer to this question, so I'm just looking for people to share thoughts and pros/cons on the way they handle master/details situations.
There are a number of ways you could alter this approach. Keep in mind that no solution can really be "wrong" per se. It all depends on the details of your situation. Here's one way to skin the cat.
who should handle the creation of the copy of the entity? (master or detail)
I see the master as an in-memory list representation of a subset of persisted entities. I would allow the master to handle any changes to its list. The list itself could be a custom collection. Use an ItemChanged event to fire a notification to the master that an item has been updated and needs to be persisted. Fire a NewItem event to notify the master of an insert.
we use copyFrom() to prevent having to replace entities in a collection which could cause references to break. Is there a better way to do this? (implementing copyFrom() can be tricky)
Instead of using copyFrom(), I would pass the existing reference to the details popup. If you're using an enumerable collection to store the master list, you can pass the object returned from list[index] to the details window. The reference itself will be altered so there's no need to use any kind of Replace method on the list. When OK is pressed, fire that ItemChanged event. You can even pass the index so it knows which object to update.
new entities receive an id of -1 (which the server tier/hibernate uses to differentiate between an insert or an update). This could potentially cause problems when looking up (cached) entities by id before they are saved. Should we use a temporary unique id for each new entity instead?
Are changes not immediately persisted? Use a Hibernate Session with the Unit of Work pattern to determine what's being inserted and what's being updated. There are more examples of Unit of Work out there. You might have to check out some blog posts by the .NET community if there's not much on the Java end. The concept is the same animal either way.
Hope this helps!
The CSLA library can help with this situation a lot.
However, if you want to self implement :
You have a master object, the master object contains a list of child objects.
The detail form can edit a child object directly. Since everything is reference types, the master object is automatically updated.
The issue is knowing that the master object is dirty, and therefore should be persisted to your database or whatnot.
CSLA handles this with an IsDirty() property. In the master object you would query each child object to see if it is dirty, and if so persist everything (as well as tracking if the master object itself is dirty)
You can also handle this is the INotifyPropertyChanged interface.
As for some of your other questions :
You want to separate your logic. The entity can handle storage of its own properties, and integrity rules for itself, but logic for how different object interact with each other should be separate. Look into patterns such as MVC or MVP.
In this case, creation of a new child object should either be in the master object, or should be in a separate business logic object that creates the child and then adds it to the parent.
For IDs, using GUIDs as the ID can save you quite a bit of problems, because then you don't have to talk to the database to determine a correct ID. You can keep a flag on the object for if it is new or not (and therefore should be inserted or updated).
Again, CSLA handles all of this for you, but does have quite a bit of overhead.
regarding undo on cancel : CSLA has n-level undo implemented, but if you are trying to do it by hand, I would either use your CopyFrom function, or refresh the object's data from the persistance layer on cancel (re-fetch).
i just implemented such a model.but not using NH, i am using my own code to persist objects in Oracle Db.
i have used the master detail concept in the same web form.
like i have master entity grid and on detail action command i open a penal just below the clicked master record row.
On Detail Add mode, i just populate an empty entity whose id were generated in negative numbers by a static field.and on Save Detail button i saved that entity in the details list of the Master Record in Asp.NET Session.
On Detail Edit,View i populated the Detail Panel with selected Detail through ajax calls using Jquery and appended that penal just below the clicked row.
On Save Button i persisted the Master Session (containing list of Details) in database.
and i worked good for me as if multiple details a master need to fill.
also if you like you can use Jquery Modal to Popup that Panel instead of appending below the row.
Hope it helps :)
Thanks,