Should I create a ViewModel for each item in an ObservableCollection - c#

Being fairly new to MVVM in WPF I am running into a dilemma of what items need their own ViewModel. I am writting a plugin for Autodesk's Revit program. Using the provided API I am drilling into the parameters of elements and performing engineering calculations in a way that is easier for the end user to understand. I have outlined my approach in the image below.
The element is a model element in the program that is selected manually. An instance of the FPB class is then created by pulling parameters from the element and assigning them to the FPB's properties. The ViewModel contains an ObservableCollection that contains a list of FPB objects the user has selected. Using a combobox the user can select which FPB they want to edit. The WPF page then shows the respective values for the properties that the user needs to interact with to perform their engineering calculations.
Using WPF's databinding the values can be changed in the View and the value is subsequently changed in the ViewModel's FPB in the ObservableCollection. In order to write these values back to the Element in Revit the change needs to be raised in the API's ExternalCommand and ensured in a transaction. It then executes a method that writes the values back to Revit.
My issue is for each value changed in the view the data needs to update the element. This element is performing its own calculations that are changing the data values. These updated value then need to return all the way back to the view. The round trip is shown below.
View -> ViewModel -> FPB in ObservableCollection -> Update Element -> FPB in ObservableCollection -> ViewModel -> View
To adhere to MVVM principles do I need to create a FPBViewModel for each FPB instance? Will this allow for an easier flow of data? Or is this just unnecessarily complicating the data flow. I can add code snippets if need be, but this appears to be more of a theory question than code specifics question. Thanks in advance.

Related

Mvvm - How to capture in the ViewModel, the UI's data-bound Control's Name/Id, using a Parameter Command? Is there a clear cut way I'm missing?

Mvvm, Wpf, VS2015
Hi All! I have googled this till my fingers bleed! But just hacks and work-arounds, or partial use of code behind.
My Wpf-Mvvm app uses Speech Synthesis.
Each button, (and in the next iteration, buttons using the selectedItem Index of Lists or ComboBoxes, to select the content(text)to be synthesized) specifies different content to be synthesized.
Sticking with Mvvm principles, in my viewModel, I need to capture which button, based on the List or ComboBoxes' SelectedItem/Value that is bound to the Parameter Command.
Why? So I can have the app synthesize the content(text/words)the user selects to hear. If not, I have to make separate commands and speech synthesizer methods for each.
There must be a simple clear-cut way. What am I missing here?
I thought of updating the Model's properties to reflect user's selection and use User Settings to persist,but have not tried yet.
Otherwise the Model's properties are accessible so far as encapsulation-wise to the ViewModel, but the property values from the Model still are not visible in viewModel.
I need the command bound control's ID/Name to route the content to be synthesized based on the model's class properties. But when I instantiate the model from the viewModel,
the model's properties are not there. I presume instantiating the Model class object from the viewModel, is in a different memory location, than the Model.
So I will try using User Setting's persistence for these properties, if no one else has a better way.
There has to be a clear solution to this, I hope :)
I know the View and ViewModel are supposed to not need to know what the other is doing. In this case though, I clearly need the List or ComboBoxes' Id or Name and their SelectedItem index.
I also need the button's Id or Name, because I have several groupings of content to choose from, and several buttons starting the synthesizing method.
Example: If the text content of an announcement to be synthesized, held in separate XML(SSML) files with identifier key values of 941, 23, 508, etc.,
I need to know which the User Selected to hear.
I imagine simply capturing in the viewModel, the Data-bound List/ComboBoxes' Selected ID index would suffice.
Thank you so very much for your help.
-Flazz

A comment list Using WPF

I would like to know create this sort of comment list with WPF using using input from the user. He will input the username and a comment. Then he will press submit. There will also be a time stamp on every comment submitted. Then the system will have a limit of 5 comments once a different user inputs the 6th comment the first one will be deleted and then the 2nd comment submitted will become the first and so on until the users stop submitting.
Sadly I am completely stumped and just can not figure out how to even start it, so some help would be greatly appreciated.
I would personally go with something around this way:
ViewModel layer
You will need to create a UserComment class containing the following properies
UserName,Comment,TimeStamp
Another class which acts as the ViewModel of your UserCommentsView containing a property UserComments which returns an
ObserableCollection of UserComment type.
A ViewModel.AddUserCommentCommand property which adds a comment to
UserComments - but before it checks the count of items in
UserComments and removes the first/last (depending on your design)
before adding a new comment.
View layer
A listview with the above ViewModel as its context + items source
being ViewModel.UserComments.
A Button with its command bound to ViewModel.AddUserCommentCommand.
Have a look at Basic MVVM Listbox Binding in WPF for MVVM binding.
and if you prefer code-behind: ListView, data binding and ItemTemplate

Making a grid of objects

I'm trying to make something like a quiz application where 3 questions will be brought up on screen at a time, allowing the user the check a radio button containing "Yes" or "No", and have an answer come up appropriately to his response. The questions will continually come from a database I'm using.
After a few attempts I've figured that using x:Name property is not a great solution, since it doesn't allow me to use a loop in order to change the questions and answers. Is there any other way to make a grid with the same types of objects in each cell, being able to access each object inside each cell of the grid in the code-behind?
Here is list of steps you need to implement,
Need to create QuestionModel, contains question properties, make sure your model inherits INotifyPropertyChanged.
Need to create ViewModel, which contains data objects, public/dependency properties
Need to bind/set data objects/properties on viewmodel constructor
Need to set your ViewModel as a DataContext of your View(.xaml) (You can create this on zammel directly and codebehind as well
Need to bind your UI objects like Question/answers/yes-no with viewmodel properties accordingly
WPF/Silverlight has their own fundamentals like Data Binding, Resources, Compiler, Dependency Properties. Above steps comprises MVVM design pattern. During each steps, please google for specific stuff.

Where to Create / Get / Cache the ViewModels?

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.

How to implement CRUD Master Details on the same screen under MVVM

I have a MVVM (Prism) application that I need to implement a master details screen wheer the master is a listview and the details is displayed next to it. Read-only seems easy enough (haven't done it yet but I've got my head around WPF binding) but edit/add confuses me.
How to I make it so the master is not updated until the details is saved?
How do I make it so you can't change the master's current selection while in edit/add mode?
I've been googling a plenty but have not found any meaty examples of this.
Thanks.
PS: This view is a child view on a larger screen. This is why I want both master and detail together.
You certainly can do this, though in my opinion such a UI design fails to harness the full power of WPF. Old WinForms UIs usually didn't update most of the application until data was saved to SQL Server (or wherever) because they didn't have real business objects and a powerful binding system like WPF. Trying to copy WinForms limitations within WPF seems like a step backward to me. Why not show the latest data everywhere it is visible in the UI, including in the master view? Also, why not allow the user to edit multiple items before saving, for example marking any edited but unsaved item with an animated marker in the master view? Combine these with a generalized undo and you have a better design and more intuitive for the user.
However if your business requirements make it absolutely necessary, here is how to do it:
Preventing changes to data from being visible outside the detail until it is saved
Upon entry into your "edit/add mode", make a copy of the data objects and set your detail view's DataContext to the copy instead of the live object. When the data is "saved", copy the data from the shadow copy back into the live object and set your detail view's DataContext back where it should be.
Preventing the master's current selection from changing while in edit/add mode
Two possibilities:
During edit/add mode, change the master view to disallow mouse hit testing or keyboard focus
When edit/add mode begins, capture the "current selection" then add an event handler that watches for "current selection" changes and immediately changes the selection back to what it was. When edit/add mode ends, remove the handler. This handler can be conveniently coded using a lambda expression and using a closure on a local variable to store the current selection.
Thanks for the answer. Now I've re-read my message, I see it is rather vague. I have a screen that edits an object which contains multiple lists of other child objects. I've implemented these as different tabs in a tab control. One of these tabs edits the comments, so I wanted to display a list of comments with an edit panel for the current selection next to the list. The user could then use add, edit or delete buttons to update the list. I wanted to do this in a pure(ish) MVVM way.
I came up with the following design which seems to work with minimal hacks.
The View includes a list of the child objects simply as a ListView bound to an observable collection within the ViewModel. I included a child object buffer – this is used to buffer changes until they are ready to be saved back to the list (or thrown away).
The View also includes an edit panel bound to the buffer object in the ViewModel. The buffer is updated whenever the list view’s current selection changes using a deep copy. I tried using data binding on the Selecteditem property but the set was never called, so a small code-behind method was added to force the property to be updated when the selection was changed.
The list view and edit view are mutually exclusive. In theory you could hide the disabled one, perhaps using a flip screen. As a general pattern, it is better for my app to have both visible at the same time as the edit panel may show extra information not shown in the list view. The choice as to which panel is enabled is controlled by binding IsEnabled to a ViewModel property like IsEditCommentMode.
Commands to manage the list have to be added, these are New, Editand Delete. Note that Add and Edit will set set up the buffer then set IsEditCommentMode to true. These list management commands are only available when IsEditCommentMode is false.
The edit panel implements Save and Cancel commands, they are only be enabled when IsEditCommentMode is true. When Save is executed, it should copy from the buffer to the list (either add or update) and fire the change notification. Finally, it should set IsEditCommentMode to false.
This all works well and does not seem to violate any MVVM tenents (in my humble but often flawed opinion).

Categories

Resources