Reputation: 8079
I have a Model class with a collection and a ViewModel wrapper class for that Model class. The wrapper class implements INotifyPropertyChanged
and has a wrapper property for every property in the model class. I've implemented it this way to make the model class as independent from any WPF namespace as possible, because these classes are also used in another project (a windows service). The implementation (simplified) looks like this:
Model
public class FbiDirectory
{
private string type;
private ObservableCollection<PluginValue> pluginValues = new ObservableCollection<PluginValue>();
public string Type
{
get
{
return this.type;
}
set
{
this.type = value;
}
}
public ObservableCollection<PluginValue> PluginValues
{
get
{
return this.pluginValues;
}
set
{
this.pluginValues = value;
}
}
}
ViewModel wrapper
public class FbiDirectoryViewModel : INotifyPropertyChanged
{
private FbiDirectory fbiDirectory = new FbiDirectory();
public string Type
{
get
{
return this.fbiDirectory.Type;
}
set
{
this.fbiDirectory.Type = value;
this.OnPropertyChanged("Type");
this.OnPropertyChanged("Title");
}
}
public ObservableCollection<PluginValue> PluginValues
{
get
{
return this.fbiDirectory.PluginValues;
}
set
{
this.fbiDirectory.PluginValues = value;
this.OnPropertyChanged("PluginValues");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
My question is if there is a way to make the PluginValues
collection in the Model of type List
and still have the features of ObservableCollection
in the ViewModel. Perhaps with some sort of converter or casting or anything like this.
Upvotes: 3
Views: 6959
Reputation: 1992
You do not have any inheritance or other "forcing" relationship between your Model
and ViewModel
, so you have a few options:
Option 1
If there is no inheritance, you can have a properties with the same name, but different types in your models. Just be careful while mapping between those two classes.
Option 2
If the properties do need to have the same type, just use IList<>
or IEnumerable<>
.
IList<object> list1 = new ObservableCollection<object>();
IList<object> list2 = new List<object>();
All collections in C# implement IList
, so all collections can be stored in a variable or property with the type IList
. If you don't need any special list functions, like Add
, Clear
, Contains
, etc, you could event use IEnumerable<>
as property type.
Option 3
If there is inheritance between Model
and ViewModel
, properties can be overridden with the new
keyword.
public class Model
{
public List<object> Plugins { get; set; }
}
public class ViewModel : Model
{
public new ObservableCollection<object> Plugins { get; set; }
}
The listing above is valid code and changes the type of Plugin
. Just be aware that usage of the new
keyboard can have some strange effects.
And assuming you use WPF (guess because of MVVM and C#) option 3 will not work. WPF uses reflection for databinding. If you overwrite a property with new, WPF will find two implementations and not knowing which one to use, it will throw an exception.
Conclusion
I personally prefer option 1. There is no reason why Models, DTOs and ViewModels should inherit from the same base and there is no reason why they can't have properties with the same name, but different types.
If you want you ViewModels to inherit from your Models, use option two.
Upvotes: -1
Reputation: 16991
You can always keep the model class as a regular List<>
and have the matching property on the View Model be an ObservableCollection<>
. I do this quite frequently without any issues. In the constructor for the View Model (that takes the Model as an argument), I just instantiate the View Model's ObservableCollection<>
from the contents of the property on the Model. This works particularly well if you are actually wrapping that property Type
too.
You won't get automatic updates on the View Model property when the Model property changes, so you will have to keep things in sync yourself, but I consider that part of the View Model's job anyway, especially when you are wrapping a Model that you do not control.
public void MyViewModel(MyModel<MyModelPropertyType> model)
{
MyListProperty = new ObservableCollection<MyWrappedModelPropertyType>(model.ModelListProperty.Select(i => new MyWrappedModelPropertyType(i));
}
If you don't need to wrap the property type, you can omit the LINQ. You will end up with some overhead from copying the list as far as instantiation time and memory cost. If you need the property type wrapped, you would end up doing that any way though.
Upvotes: 6
Reputation: 169160
My question is if there is a way to make the PluginValues collection in the Model of type List and still have the features of ObservableCollection in the ViewModel.
No. An ObservableCollection<T>
implements the INotifyCollectionChanged
interface and a List<T>
doesn't.
So if you intend to dynamically add items to the PluginValues
collection at runtime, this should be an ObservableCollection<T>
or any other kind of collection type that implements the INotifyCollectionChanged
interface.
However, as pointed out in the comments, ObservableCollection<T>
is not really a WPF specific class since it is defined in the System.Collections.ObjectModel
namespace in System.dll
. So you might use the ObservableCollection<T>
as-is in your model or implement your own custom collection that raises notifications. The latter may seem a bit necessary though.
Upvotes: 1