I want to bind a list of persons to a DataGrid control.
The objects are loaded on demand, this means that initally I only have a List of IDs (int). In the DataGrid itself I want the objects (of type Person) with all their properties.
For that I used an IValueConverter that converts my List<int> to List<Person>.
I want to bind the SelectedItem to a property int SelectedId. I cannot simply bind to Person, since my Person class has no ID property. But with the value convertion the SelectedItem obviously is of type Person.
Should I initially load the objects in a Dictionary instead and bind that to the ItemsSource. SelectedItem would then be a KeyValuePair.
What other approaches are possible?
IMHO the best way to provide data in a list, is an ObservableCollecion<T>. I'm convinced that this is also the common way, but I've no source to prove it. So it's an IMHO statement.
It implements the INotifyCollectionChanged interface, so that the view is noticed about any changes (adding and removal of items). For more information pls refer here. If the item in the collection, in your case Person, is implementing INotifyPropertyChanged, the view will also be notified regarding changes of the items itself. Please have a look here for more information.
So if I where you, I would put an id property into Person and load the whole collection into an ObservableCollection. If you cannot modify Person you could create a wrapper like
public class PersonVm
{
public int Id { get; set; }
public Person Person { get; set; }
public PersonVm(int id, Person person)
{
Id = id;
Person = person;
}
}
You will have to add the implementation of INotifyPropertyChanged by yourself, if you want to have the notification feature of the items.
Btw, what I described here is a typical implementation of the MVVM pattern. It has definitely many advantages to implement a WPF application with it, but there are some disadvantages too. E.g. there is an overhead which may not be worth the effort in smaller projects. You can get an overview here.
Related
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);
I am currently working on a new project of mine that is going to be a data record visualizer (for records in Pascal). It should provide a way to define a given record with data fields and pointer fields and then there will be an example view where you can see the record "in action".
Now the problem I am having is that in this model there are records and components and the relationship between them is that one record has multiple components (data and pointer as mentioned above).
I want to use MVVM for the app but I am now unsure how I should approach this. I modelled the record and components into RecordViewModel and ComponentViewModel (with derivates DataComponentVM, PointerComponentVM).
Now to provide a look for these VMs there are 2 options as far as I know:
Deriving the ViewModels from Control and providing a ControlTemplate
Creating a UserControl using the ViewModel as DataContext
The UserControl approach works fine for the RecordViewModel but when I try to design the way the ComponentViewModels are shown (in a ContentPresenter) this approach fails because I would need to provide a collection of UserControls (instead of DataComponentViewModels) in my RecordViewModel that would make that work (and I am pretty sure that is not a good idea).
The Control approach also has the problem that the ViewModels aren't POCOs anymore which I think has a strange feel to it and is also not considered good practice.
Is there any other way to solve the problem? Do you have any other good advice for me in this regard?
Thanks in advance!
Code:
public class RecordViewModel : BaseViewModel
{
public RecordViewModel()
{
Components = new ObservableCollection<ComponentViewModel>();
}
public ObservableCollection<ComponentViewModel> Components { get; set; }
}
public class DataComponentViewModel : ComponentViewModel
{
public string Type { get; set; }
}
public class PointerComponentViewModel : ComponentViewModel
{
public object Target { get; set; }
}
Oh god why didn't I think of this before?
I was only thinking about ControlTemplates (therefore needing my ViewModels to derive from Control) when there are also DataTemplates that work exactly like I wanted them to.
I got lost as to why you think you need to provide a collection of user controls, but it sounds like what you really want is for the RecordViewModel to have some variation of:
ObservableCollection<ComponentViewModel> Components
Components is then bound in xaml to the ItemsSource property of some sort of ItemsControl. Whether or not the ComponentViewModel needs it's own UserControl depends on what you are trying to do with it.
If that doesn't start to click for you then you may want to post some code so we can sort it out.
A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.
Thats means my CustomerViewModel has a order collection of type List or IEnumerable.
No change in the list is recognized by the bound control. But with ObservableCollection it is.
This is a problem in the MVVM design pattern.
How do you cope with it?
UPDATE: Sample of how I do it:
public class SchoolclassViewModel : ViewModelBase
{
private Schoolclass _schoolclass;
private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();
public SchoolclassViewModel(Schoolclass schoolclass)
{
_schoolclass = schoolclass;
_schoolclass.Pupils = new List<Pupil>();
foreach (var p in schoolclass.Pupils)
Pupils.Add(new PupilViewModel(p));
}
public Schoolclass GetSchoolclass
{
get { return _schoolclass; }
}
public int ID { get; set; }
public string SchoolclassName
{
get { return _schoolclass.SchoolclassName;}
set
{
if(_schoolclass.SchoolclassName != value)
{
_schoolclass.SchoolclassName = value;
this.RaisePropertyChanged("SchoolclassName");
}
}
}
public ObservableCollection<PupilViewModel> Pupils
{
get{ return _pupils;}
set
{
_pupils = value;
this.RaisePropertyChanged("Pupils");
}
}
}
I deal with this by not doing it the way you describe.
If I need to present a Foo object and its related Bar objects in the view, the FooViewModel will generally implement a Bars property of type ObservableCollection<BarViewModel>.
Note that this is irrespective of whether or not the underlying Foo class has a Bars property of type IEnumerable<Bar>. The Foo class might not. The application might not even need to be able to iterate over all of the Bar objects for a Foo, except in the UI.
Edit
When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:
_Bars = new ObservableCollection<BarViewModel>(
_Foo.Bars.Select(x => new BarViewModel(x)));
but it's essentially the same thing.
But this assumes that Foo actually exposes a Bars property. It might not. Or maybe only some Bar objects should appear in the view. Or maybe they should appear intermingled with other objects, and the FooViewModel should expose a CompositeCollection of some kind.
The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.
To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different ListBox controls. Ultimately, doing this sets a Category property on the Item object. My view model is going to have a collection of CategoryViewModel objects, each with a property of type ObservableCollection<ItemViewModel>, so that dragging items back and forth between collections is simple to implement.
The thing is, there may not even be a Category class in the application's object model, let alone a collection of Category objects. Item.Category might just be a property of type string. The CategoryViewModel isn't mirroring the application's object model. It only exists to support the view in the UI.
Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)
I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.
Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.
All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.
What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.
With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.
any help with this would be great.
I have a model
public class Master
{
...
public Detail[] Details {get; set;}
}
I am populating my view model from a WCF service which returns my collection of Master objects. I have configured the service reference to return observablecollection so I can use it easily in my view model.
My view model then has
public ObservableCollection<Master> Masters {get; set;}
public Master SelectedMaster {get; set;}
In my view I have 2 listboxes - one bound to my Masters property and the other bound to SelectedMaster.Details.
This all works fine apart from when I add try to add a new detail to the SelectedMaster.
The collection of Details in the SelectedMaster is just a list of Details (not an ObservableCollection) which is clearly why.
What options do I have here? I have tried implementing INotifyPropertyChanged but that doesn't seem to work. I could have another ObservableCollection for the Details but that means I have to keep this collection in sync when the SelectedMaster is changed (the SelectedMaster property is bound to the SelectedItem on my first listbox.
Hope this comes across OK. Would really love some feedback. Would be ideal if WCF could just return the collection of details as an observablecollection as it does with the collection of Masters but it doesn't seem to work like that.
Thanks.
I don't know enough WCF to say whether it can be configured to return nested collections as other types than simple arrays, but I'll try to give the WPF perspective.
There's no magic here, a simple array doesn't implement any kind of change notification; you simply have to wrap it in some kind of view-aware collection such as ObservableCollection.
Wrap everything up and only interact with the wrapped collections, example:
public class MasterWrapper
{
...
public MasterWrapper(Master master)
{
this.Details = new ObservableCollection<Detail>();
this.Master = master;
foreach (var detail in Master.Details)
this.Details.Add(detail);
}
...
public static ObservableCollection<MasterWrapper> GetMasters()
{
ObservableCollection<MasterWrapper> results =
new ObservableCollection<MasterWrapper>();
List<Master> modelMasters = null; // Populate this from the service.
foreach (var m in modelMasters)
results.Add(new MasterWrapper(m));
return results;
}
You're right that the issue is that the Details property does not have changes notified back to the View, because it is just an array of Detail objects...
I would make it an ObservableCollection<Detail> and load up the collection in Master's constructor, like...
public Master(Detail[] details)
{
Details = new ObservableCollection<Detail>(details);
}
remember that ObservableCollection<T> takes IEnumerable<T> or List<T> as a parameter in its constructor.
A part of the editor i'm writing uses a Wpf-TreeView. I'm using DataBinding and an ItemTemplate to populate the TreeView. So far i'm manipulating the ItemsSource(mostly ObeservableCollection's) dircetly(using for example Drag&Drop). But now i read this and i'm not sure if it would realy simplify thinks for me. And before i go on with the project i whould like to know all Pros and Cons.
If Data(ItemsSource) is added, edited or delete, how to keep the Data and the ViewModel consistent? Is that something the ViewModel has to take care of? If i have to take care of the consistens how does this simplifies thinks?
MVVM is a great fit for WPF development in general, not just in TreeViews.
If Data(ItemsSource) is added, edited
or delete, how to keep the Data and
the ViewModel consistent?
Not sure exactly what you're asking here, but WPF binding handles collection changes, as long as those collection implement INotifyCollectionChanged. ObservableCollection<T> gives you a nice, useful implementation of this interface that you can use within your view models.
Bindings keep the view consistent with your view model. Generally what you're aiming for is zero code-behind in your view. Your view just binds to properties on the view model and it is the responsibility of the view model to keep related properties in sync. Here's a really simple example:
public class PersonViewModel : INotifyPropertyChanged
{
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
}
//LastName and other members omitted
public string FullName
{
get { return FirstName + " " + LastName; }
}
}
Here the FullName property is affected by changes to FirstName and LastName. The view can just bind to FullName and any changes to the other two properties will be visible in the UI.
I'd advise you to read my blog post on POCOs versus DependencyObjects as view models before you start out.