I'm using MVVM in WPF in C#.NET. Say I have the following example model:
public class Dealer
{
public string Name { get; set; }
public List<Car> Cars { get; set; }
}
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
}
In my view, I have a list of dealers, and if I select a dealer in the ListView, then I should see details about the dealer, including the dealer's name and the list of cars.
My question is, how can I bind the List of Cars to a list view, given that it's a property of the Dealer object? Specifically, what should I have in my view model? I need the list of cars to update if I add anything to the collection, so I have a feeling that I'll need an ObservableCollection. Ho开发者_运维问答wever, this seems to lead to having two collections (a List and an ObservableCollection) for every dealer, and having this duplication of data seems inefficient from both a coding point of view and performance. Any ideas?
What we have been doing is wrapping the Model lists within an ObservableCollection. This ObservableCollection is usually also a ViewModel for the Child object, so we can control its view and be able to pass the ViewModel to another view when necessary. You will have two lists, but they will contain the same object references. It may seem like a duplication of effort, but with a decent use of Inheritance, you can still maintain a DRY approach. Remember the idea of MVVM is to create an Abstraction between your View and the Model, it may seem like you are saving yourself the effort upfront by not wrapping the Child objects, however we have found that there is almost always a need to tranform these Child objects later.
public class DealerViewModel : ViewBase
{
// This concept can be wrapped in a ListViewBase for a more DRY approach
private ObservableCollection<CarViewModel> cars;
public ObservableCollection<CarViewModel> Cars
{
get { return cars; }
set {
if (cars == value) return;
cars = value;
RaisePropertyChanged("Cars");
}
}
// This concept can be wrapped in a ListViewBase for a more DRY approach
private CarViewModel selectedCar;
public CarViewModel SelectedCar
{
get { return selectedCar; }
set {
if (selectedCar== value) return;
selectedCar= value;
RaisePropertyChanged("SelectedCar");
}
}
public DealerViewModel(Dealer dealer)
{
// This concept can be wrapped in a ListViewBase for a more DRY approach
Cars = new ObservableCollection(dealer.Cars.Select(c => new CarViewModel(c)));
}
}
public class CarViewModel
{
}
Ok, firstly, is there any reason you're using a ListView over a ListBox? If you're not sure which to use, I would use a ListBox. Have one ListBox bound to your list of dealers sitting on your view model, which would be an ObservableCollection if you're going to add/remove dealers. Then bind the SelectedValue of that ListBox to a Dealer property on your view model, called perhaps SelectedDealer.
Then have a second ListBox which shows the list of cars for the selected dealer. Bind the DataContext property of the car ListBox to the SelectedDealer, and bind the ItemsSource to Cars. Then your car ListBox will be bound to the collection of cars sitting on the selected Dealer.
If you want to add/remove cars and have the UI updated, then you'll want to use an ObservableCollection. You don't want your business objects returning ObservableCollections, so this conversion should be done on your view models. You could have an ObservableCollection property on your view model called SelectedCars perhaps, and bind your car list box to this. In the setter for SelectedDealer, you could do something like:
public ObservableCollection<Car> SelectedCars { get; set; }
private Dealer selectedDealer;
public Dealer SelectedDealer
{
get
{
return this.selectedDealer;
}
set
{
if (this.selectedDealer != value)
{
this.selectedDealer = value;
this.SelectedCars = new ObservableCollection(this.SelectedDealer.Cars);
OnPropertyChanged(() => this.SelectedDealer);
}
}
}
In this case you wouldn't set the DataContext of your car listbox to the SelectedDealer, the DataContext wouldn't be set, and the view model would be used implicitly. Then just bind the ItemsSource of the car listbox to the SelectedCars property.
personaly i would abstract the 'cars' collection into another object for exaple a 'Stock' object. i would never expose a list or observable collection as that breaks encapsulation. i would expose an ienumerable with addstock and removestock methods on the Stock object to encapaulate the relevant behavior. In the view model that behavior would be to call the corresponding method on the Model and fire the relevant INotifyPropertyChanged events. The Stock object would have its own view model so binding would be to the IEnumerable on the stock viewModel.
精彩评论