View only properties (eg: IsSelected) and the Model in MVVM - wpf

Consider a WPF app that is being written using MVVM. The app needs to display a list of Employees (FirstName, LastName, Title) and you can select multiple to delete.
The model in this case would be "Employee" and it would implement INotifyPropertyChanged.
The View would be the "EmployeeListView" which would implement the XAML to display a collection of Employees.
The ViewModel would be "EmployeeListViewModel" which would expose an ObservableCollection, that can be bound to the EmployeeListView.
My question is: Where should the "IsSelected" property live?
In the Model? (I dont like this idea, as the model now is exposing a property that is only required by a view and has nothing to do with the actual domain object, also, this property would be useless, if I implemented the view differently and didnt allow deletion of multiple employees at once).
In the "EmployeeListViewModel" as a separate Dictionary collection, that would track whether an employee is selected or not? (Or even just a HashSet containing all selected employees). I dont like this much as the binding in the view is no longer straight forward.
Implement a separate EmployeeViewModel, that wraps the Employee object and exposes the IsSelected property. The EmployeeListViewModel then will expose its collection as a ObservableCollection. I like this solution the best, but I always thought that there is one ViewModel per View and in this case, I have 2 view-models for my view. Is that a deviation from the MVVM pattern or is this the typical way to implement MVVM? (references?)

Create a reusable Generic SelectableItem that wraps each item in the EmployeeList:
Simple example:
public class SelectableItem<T>: INotifyPropertyChanged
{
public bool IsSelected {get;set;} //PropertyChanged(), etc
public T Value {get;set;}
}
then in the ViewModel:
public ObservableCollection<SelectableItem<Employee>> Employees {get;set;}
and in the View:
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}" Content="{Value.FullName}"/>
</DataTemplate>
Then you can retrieve all selected employees just by:
var selectedemployees = Employees.Where(x => x.IsSelected).Select(x => x.Value);

Related

Bind Model with the ViewModel in WPF Prism

Assume I create a bunch of Model classes dynamically (say based on some config file content on startup), say each model is an instance of a class CarModel:
public class CarModel
{
public string CarName { get; private set; }
public CarModel(string carName)
{
CarName = carName;
}
}
All of them then added to ObservableCollection CarList, and then represented in the view in XAML:
<Window.Resources>
<DataTemplate DataType="{x:Type models:CarModel}">
<views:CarView></views:CarView>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding CarList}">
</ListBox>
I then bind CarView to CarViewModel through the Prism AutoWireViewModel.
I use CarViewModel because I have bunch of extra (only view related) properties, and also handle navigation and events (through eventAggregator) that I don't want to include in my original CarModel.
My problem is, how do I bind (or link) my CarViewModel with the original CarModel that created the view?
how do I bind (or link) my CarViewModel with the original CarModel that created the view?
Either by navigating to the view and passing the respective model in the navigation parameters or (preferredly) by creating the view model yourself (through a factory) instead of using the view model locator and initializing it with its model.
That is, CarList should be contain CarViewModels. If you need full two-way synchronization (the list is changed by the database and the view simultaneously), this means a lot of boilerplate code, but most of the time, you need no synchronization at all... so a IReadOnlyColletion<CarViewModel suffices (instead of a full-blown ObservableCollection) and it can be initialized once with a simple Select.
Hint: the view model locator is meant for top-level views mainly, most of the time you're better off creating the view model and linking it to the view with a DataTemplate, i.e. going view model-first. In fact, earlier version of prism supported this approach even when navigating regions, and dropping that made regions far less widely usable sensibly.

WPF MVVM: View's ListBox with source deep in Model. How to implement?

I'm new to WPF. I need to Bind UI's ListBox to the source that is deep in Model Layer.
App scheme is on picture below. Desc:
My MainWindowViewModel Class has a Scheduler Property (Scheduler Class in Model layer).
Scheduler Class has a CurrentParser Property (Parser Class in Model layer).
Parser Class has a Result field (ParserResultMetaData Class in Model layer).
ParserResultMetaData Class has a Log field (Log is a List(Of String))
Log can be changed only programmatically from model layer (Parser adds lines during it's work).
So my question is how can I bind my ListBox to this List to match MVVM pattern.
As I get it now, ViewModel must have an ObservableCollection(Of String) witch is a copy of my List(Of String) from Model layer.
Somehow you need to notify the UI when a line is added to the collection. There are multiple ways to achieve this, but if the collection is modified from within the model layer, you need a mechanism for communicating this to other layers in one way or another.
Use an ObservableCollection in your Model layer.
While types like ObservableCollection and INotifyPropertyChanged are widely used in MVVM architectures, they are not specific to them and in some cases it can make sense to use them in the model layer directly. Using an ObservableCollection in your Parser class is one way to provide this notification mechanism. You can then bind the ItemsSource of the ListBox to Scheduler.Parser.Result.Log directly and it will update accordingly.
Create a wrapper property in your ViewModel.
If you don't want to use an ObservableCollection in your model, you can expose the List via a property in your ViewModel, for example:
public IEnumerable<string> ParserLog
{
get { return Scheduler.Parser.Result.Log; }
}
Then you need to manually notify the UI when an item is added, so you're gonna need an event (or something equivalent) which tells your ViewModel that the list changed and it needs to raise the PropertyChanged Event for the ParserLog property. Add code like this in your ViewModel:
this.Scheduler.Parser.ResultUpdated += (s, e) => this.RaisePropertyChanged("ParserLog");
This will tell the ListBox to update the items from the ParserLog property.

Sorting on Templated Column with converters

My datamodel is like this:
public class ModelA
{
public int ModelId{get;set;}
}
public class ModelB
{
public IEnumerable<ModelA> ChildObjects{get;set;}
}
Now in the Xaml, am using a DataGrid with the ItemSource as List(), and have a template column which binds to ChildObjects with a converter doing the job of getting the first element from ChildObjects and returning the value as that object's ModelId. Now all works fine till now. The issue is when I do sorting on this templated column.
I know one workaround is to have an extra property in ModelB which does the job of what converter is doing and make the sortmemberpath in xaml as that new property name, but that is not what I want as its against the model.
Is there any other perfect way to handle this scenario, as the SortMemberPath can't be made as expression as its just a contant.
You've tagged this MVVM, which I assume means your models are actually view models (or are at least wrapped by view models). That being the case, why wouldn't you add the extra property? After all, it's there to support the view. Your view needs the extra property, so your view model should provide it.

Switching a binding with a binding

I have a BlogStore class which contains two observablecollections like so
public class BlogStore {
public ObservableCollection<Blog> blogs ...
public ObservableCollection<Blog> favourites ...
}
public BlogStore blogStore ...
no I want to reuse a control which does the following binding
ItemsSource="{Binding blogStore.blogs}
so that I can switch to favourites, the following does not work, but I would like something in a similar vein.
ItemsSource={Binding blogStore{Binding category, ElementName=blogControl}
and in the controls code behind i would have a dependency property.
maybe a converter could do the trick?
If you treat BlogStore as a ViewModel then it would expose a couple of other properties.
Category to which you bind what ever control you are using to choose the category to display.
Also a CategoryBlogs property which returns either the value of blogs or favourites depending on the value of Category.
You would be implementing INotifyPropertyChanged so you would ensure that a PropertyChanged event is fired for "CategoryBlogs" when the Category property is changed.
You would be binding ItemsSource just to CategoryBlogs.

Can I have a ValueConverter in my ViewModel?

I have a combobox bound to a collection, so the user can select one of the items. So far, so good.
The content of the combo box is driven by the item, but also by a value in my viewmodel. Imagine the value in my viewmodel is the language, I have dictionary of descriptions by language in my bound item, and I want to display the correct one.
How should I go about this?
This is a classic example of why the ViewModel exists - you want to have logic which depends on trivial state in the view, as well as the main model.
Imagine you are writing a unit test to run against the ViewModel for this behaviour. You would need the ViewModel to have a property mapped to the selected item. The ViewModel would also have another property which varies according to this selected item as well as the other value in the ViewModel you mentioned.
I think of this as the test-driven approach to ViewModel design - if you can't write a unit test to evaluate it then you haven't got the mix of state and published interfaces right.
So, yes, the ViewModel can solve the problem and if you push all the state down into it you can do the unification within the ViewModel.
Make an observable collection in your viewmodel of type Item. Bind the itemsource of your viewmodel to this observable collection.
public class Item
{
public String description {get;set;}
public String language {get;set;}
public override ToString()
{
return description;
}
}
Selected item would also be bound to a property of type Item as well.
The override of ToString displays the description.
The Selected item propery will have a reference to the selected object property where you can get the language from.

Resources