D
That's what I finally got. public class VmList<TModel, TViewModel> : IList<TViewModel>, IEnumerator<TViewModel>, INotifyCollectionChanged, INotifyPropertyChanged where TModel : ModelBase where TViewModel : ViewModelBase, new()
{
IList<TModel> _list;
IEnumerator<TModel> _enumerator;
bool _modelListIsCollectionNotifier;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public VmList(IList<TModel> list)
{
_list = list;
{
var notifier = _list as INotifyCollectionChanged;
if (notifier != null)
{
_modelListIsCollectionNotifier = true;
notifier.CollectionChanged += Notifier_CollectionChanged;
}
}
{
var notifier = _list as INotifyPropertyChanged;
if (notifier != null)
notifier.PropertyChanged += Notifier_PropertyChanged;
}
_enumerator = _list.GetEnumerator();
}
private void Notifier_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
void Notifier_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, e);
}
public TViewModel this[int index]
{
get
{
return new TViewModel { Model = _list[index] };
}
set
{
_list[index] = (TModel)value.Model;
if(!_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, index));
}
}
public int Count
{
get
{
return _list.Count;
}
}
public TViewModel Current
{
get
{
return new TViewModel { Model = _enumerator.Current };
}
}
public bool IsReadOnly
{
get
{
return _list.IsReadOnly;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public void Add(TViewModel item)
{
_list.Add((TModel)item.Model);
if (!_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
public void Clear()
{
_list.Clear();
if (!_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public bool Contains(TViewModel item)
{
return _list.Contains((TModel)item.Model);
}
public void CopyTo(TViewModel[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public void Dispose()
{
_enumerator.Dispose();
}
public IEnumerator<TViewModel> GetEnumerator()
{
return this;
}
public int IndexOf(TViewModel item)
{
return _list.IndexOf((TModel)item.Model);
}
public void Insert(int index, TViewModel item)
{
_list.Insert(index, (TModel)item.Model);
if (!_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
public bool MoveNext()
{
return _enumerator.MoveNext();
}
public bool Remove(TViewModel item)
{
var res = _list.Remove((TModel)item.Model);
if (res && !_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
return res;
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
if (!_modelListIsCollectionNotifier)
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, index));
}
public void Reset()
{
_enumerator.Reset();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _list.GetEnumerator();
}
}
Sorry for not commenting on the code. It's about the next one. As you can see, this is a universal class dependent on two basic types: ModelBase and ViewModelBase♪ The entire collection is not duplicated from the model. When I ask for an element VmList'aclass takes the relevant element from the model collection ModelBase and makes it a new type element ViewModelBase♪ Here's a leaflet. ModelBase♪ Don't notice he's empty, his task is not logic, but in the presence. I mean, it's necessary to clearly distinguish the model and make the model. I'm going to give him a show-up and something, but it's not necessary to deal with the current task.[DataContract]
public class ModelBase: MvvmBase
{
}
Here's the leaflet. ViewModelBase♪public class ViewModelBase: MvvmBase
{
bool _initialized;
ModelBase _model;
Lookup<string, string> _relatives;
public ViewModelBase() { }
public ViewModelBase(ModelBase model)
{
Model = model;
}
void Initialize()
{
DependentAttribute attr;
var tmpRel = new List<KeyValuePair<string, string>>();
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(this))
{
attr = (DependentAttribute)pd.Attributes[typeof(DependentAttribute)];
if (attr != null)
tmpRel.Add(new KeyValuePair<string, string>(attr.PropertyName, pd.Name));
else
{
var modelProp = TypeDescriptor.GetProperties(Model)[pd.Name];
if (modelProp != null)
tmpRel.Add(new KeyValuePair<string, string>(modelProp.Name, pd.Name));
}
}
_relatives = (Lookup<string, string>)tmpRel.ToLookup(kv => kv.Key, kv => kv.Value);
Model.PropertyChanged += Model_PropertyChanged;
Initialized = Model != null;
}
protected virtual void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
foreach(var propName in _relatives[e.PropertyName])
OnPropertyChanged(propName);
}
internal protected ModelBase Model
{
get
{
return _model;
}
set
{
_model = value;
Initialize();
}
}
public bool Initialized
{
get
{
return _initialized;
}
set
{
_initialized = value;
}
}
}
public class ViewModelBase<TModel> : ViewModelBase where TModel: ModelBase
{
public ViewModelBase() { }
public ViewModelBase(TModel model) : base(model) { }
protected internal new TModel Model
{
get
{
return (TModel)base.Model;
}
set
{
base.Model = value;
}
}
}
As you can see, there are two classes. General and typified. There's another guy. DependentAttribute♪[AttributeUsage(AttributeTargets.Property,AllowMultiple = true, Inherited = true)]
public sealed class DependentAttribute: Attribute
{
public string PropertyName { get; set; }
public DependentAttribute(string propertyName)
{
PropertyName = propertyName;
}
}
It's about the next one. When the model has an event PropertyChanged, with a model watching if she has the same properties, and if there is, it's a big deal. PropertyChanged♪ Well, if the model uses your properties CircRadAnd in the model, you want to name it. Radius♪ you're just hanging onto your properties ♪ Radius Attribution [Dependent("CircRad")] and changes in the model CircRadWI will report changes in properties RadiusI mean, ideally, the nature of the WMD would look like that.public string SomeProperty { get; set; }
For public string SomeProperty
{
get
{
return _model.SomeProperty;
}
set
{
_model.SomeProperty = value;
OnPropertyChanged(nameof(SomeProperty));
}
}
Of course, if you need to provide for a logic of engagement, the properties will have to be revealed. Although I'm just inside the VM, signing up for my own. PropertyChanged and depending on the name of the properties that have been changed, performing the actions. I don't know how good it is, but it's easier for me.