MVVM how best to display ListBox.SlectedItem - c#

I have a ListBox bound to a list of ViewModel objects on the LHS of my screen. The ListBox.SlectedItem is bound to a property on my "MainWindowViewModel", called CurrentItem. On the RHS I have a large area that displays the selected item in detail, containing many nested controls for editing and browsing the information the ViewModel contains.
There are two approaches to building the view that I am aware of.
The large area on the RHS is a ContentPresenter with the Content property bound to CurrentItem. In my resources, define a template to be used with my ViewModel object.
The large area on the RHS is a fixed UserControl containing all the required xaml to display the my ViewModel. I bind the DataContext property of my UserControl to CurrentItem.
Which is the nicer approach and why? Perhaps option 2 is better in general but option 1 could be used when there are view models of different types in the ListBox, each with their own template?

Personally I would not bind directly to the selected Object in the ViewModel because doing that you are breaking the MVVM Pattern.
What I would suggest to do is to create a ViewModel for the whole view Including your list of ViewModels and adding a SelectedViewModel Property that is bound to the selected item. So the selection infromation is also in the ViewModel.
Then you can bind the SelectedObject of the "parent" ViewModel to the RHS.
I would suggest reading this MSDN Article for an introduction how to display SelectedItems using the MVVM Pattern.

Related

Using ViewModels in ObservableCollections in Prism

As far as I know, the default way to use a ObservableCollection that is bound to a listview is with model classes as elements (ObservableCollection<MyModel>). So when a listview element is selected, we use NavigateAsync and pass the model, which then can be used by the ViewModel to "fill itself".
The problem with this approach is, that it's not possible to use ViewModel properties for binding in the listview.
For example:
I have a View, ViewModel and Model "PickList", which contains a collection of "PickLine" objects - each having a View, ViewModel and Model themselves. The PickLine object contains a property "PickedQuantity" and a property "OpenQuantity". Now in my PickList view, I don't want to bind these two to separate items (e.g. two labels), but I want to have one label to display both I a format like for example "PickedQuantity / OpenQuantity". I know this example can be solved by using multi binding or something like this. But that's not the meaning of it all.
My PickLine ViewModel already has a property "QuantityString", that I want to bind to the label of a listview element via DataTemplate. But how can I do this. Is it even possible?
Make a property that combines the two other properties and bind to that. E.g.:
public string FullQuantity {get {return $"{PickedQuantity} / {OpenQuantity}";}}
Then in the setter for PickedQuantity and OpenQuantity, you will want to call whatever PropertyChanged method you have set up to notify the bindings of a property change and pass in the FullQuantity property name so elements that are bound to FullQuantity get updated when either PickedQuantity or OpenQuantity are changed.
This way, you are only binding one label's text to one property and that label would get updated when either of the two quantity properties are changed.
Note: I am unfamiliar with Prism, but this approach should work regardless of the Mvvm framework in use.
Your PickListViewModel should expose a collection property whose items are of type PickLineViewModel (not PickLine).
Whether you need an ObservableCollection<PickLineViewModel> depends on where changes can happen - in service / model that initially created the PickLines or in the GUI or both. In any way, you have to make sure the changes are propagated from one side (the collection of view models) to the other (the collection of models). Google wrapping observable collection as a starter (hint: avoid two-way sync if possible). These blog posts are old but still relevant and make a good reading. A trivial wrapping is described in this answer.

binding wpf data templates to a hierarchy of view models

I have a task as part of a larger story to set some properties in a view to be read-only. These properties are part of a hierarchy of view-models and have been matched to views using data templates from an associated resources dictionary. The data templates follow a very simple style, eg.
<DataTemplate DataType="{ x:Type vm:MyTextFieldViewModel}">
<v:TextFieldView />
</DataTemplate>
Using the example here, the view model MyTextFieldViewModel has a public property in a base class it uses for read-only (ie. IsReadOnly). MyTextFieldViewModel is a child of the parent ViewModel that is being used here.
Is there a way in the data template to bind the IsReadOnly property of the MyTextFieldViewModel to its parent ViewModel? Or can I bind the matching property in the view to something outside of the view model specified in the data template (ie. the IsReadOnly property in the view get bound to the matching property in the parent ViewModel)?
Just to clarify a little - there isn't scope for doing this another way as this will involve a lot of extra work that we cannot justify at this point. If we are going to do anything it's in the Data template. We use Data templates elsewhere that bind ViewModels to controls (eg. to textboxes and comboboxes) where we do something similar to what I'm asking about here.
Thanks in advance for any help you can provide.

How to bind a grid to a user control

I want to create a settings menu that looks similar to VLC's advanced settings menu: Treeview on the left and some kind of control collection on the right. The controls on the right should enable the user to manipulate settings that are relevant to the current selection in the tree view. I thought about creating a grid right of the tree view. Then I have a user control for each view that needs to be displayed in the grid, based on the selection
The item in the treeview has a UserControl property that holds a reference to the relevant view. My viewmodel has a SelectedItem property that indicates which item in the tree view is currently selected.
Now I want to bind the content of the grid to the UserControl property of my SelectedItem. But I cannot figure out how to do that. I would prefer to use a XAML based solution instead of clearing the Children property of the grid and adding the user control that I want to display in code each time the SelectedItem property changes.
I'd suggest using ContentControl instead of Grid.
Considering the tree view and the content control are under the same view model: on your view model, add property for selected item (let's call it VMSelectedItem) of same type as the items in the tree view.
In XAML of the tree view add
SelectedItem="{Binding VMSelectedItem}"
In XAML of the content control
Content="{Binding VMSelectedItem.UserControl}"
Now selection in the tree will update the VMSelectedItem property that, in turn, will update the content of the content control.
I suggest you using DataTemplates that you have declared inside your resouces dictionay. You would be using only one instance of each DataTemplate which leaves nice memory footprints. You wouldnt need to store instance of view inside your viewmodel which is the basic idea of mvvm. The viewmodel would completely just holding data and information how you wish the data to be displayed.
As example you have an enum inside your viewmodel with values person, car, tree. Inside your DataTemplateSelector you will have an if on that enum that returns what the desired DataTemplate.
Basically you would have everything central instead of having everything per each TreeViewItem.

How to move IsSelected for MVVM out of the business objects?

I have a reasonably large application in C#/WPF made with MVVM (sort of).
In listviews I use the IsSelected bindinded to the selected items. These IsSelected property is located in the model of my app, not in a ViewModel.
Currently I only have one viewmodel pointing to one list in the business object (model, not view model), but in the future I possibly want to change this and than the IsSelected property inside the model is not possible anymore (otherwise both ViewModels would have the same selected items).
The selection mode is multiple and can range between 1 to 128 (max length of list) items.
I think it should be inside the viewmodel somehow, but how should I achieve this?
When using MVVM, you are not restricted to a single view model. Yes, you might expose parts of your model from your view model, but you can also expose other view models.
In this case, I'd recommend wrapping the instances of the collection in their own view model and set the binding root for your item templates to that.
For example, if you were data binding to a collection of models, you could pass the instances of each of those models to a view model that wraps the model and then exposes the IsSelected for binding on the view model.

WPF MVVM accessing item bound to child view in Command implementation

Hi I implemented something I asked in a thread here WPF MVVM Master detail view with a datagrid and a TabControl
It is a master details view with a datagrid as master and TabControl as child view. The SelectedItem of the grid is bound to the ItemsSource of the TabControl.
How can I access the Item bound to the TabControl in the Command written in the viewmodel? Basically I want to add new item in the bound collection (to child). But the bound item is the selectedItem of the datagrid - how do I find which one it is.
It would help to have some detail about the command you're trying to get to work, but you should be able to bind the CommandParameter on whatever control you're using to the same DataGrid.SelectedItem property that you're using for the TabControl's ItemsSource. That CommandParameter should then be available to you as a parameter into your command handler (might look different depending on what ICommand implementation you're using - DelegateCommand, RelayCommand, etc).
Do not wire the controls directly together but introduce some kind of a proxy property in your viewmodel that both controls bind to. One of them with Mode=TwoWay.
You can use CollectionViewSource.GetDefaultView() to return the ICollectionView that the TabControl is bound to. If you set IsSynchronizedWithCurrentItem to true on the TabControl then the ICollectionView's Current property will be what you can pass in to your command
You need a property in your ViewModel called SelectedCause . Bind the SelectedItem property for your DataGrid to this SelectedCause Property. Then, instead of binding the TabControl to SelectedItem.Solutions, bind that to SelectCause.Solutions . It's better to do it this way precisely because of the problem you are having. You need a property in your ViewModel that represents the currently selected Cause. The ViewModel is meant to be a representation of the state of the View. The selected item in the DataGrid is part of that state, so you should have a SelectedCause property in your ViewModel.

Categories