I have classes and interfaces:

public interface IEntity { int ID { get; set; } } public class Entity : IEntity { public int ID { get; set; } } public interface IEntityListViewModel { RangeObservableCollection<IEntity> Items { get; set; } IEntity SelectedItem { get; set; } void LoadItems(); } 

Now I need this class:

 public abstract class EntityListViewModel<T> : IEntityListViewModel where T : IEntity { public RangeObservableCollection<T> Items { get; set; } public T SelectedItem { get; set; } public EntityListViewModel() { Items = new RangeObservableCollection<T>(); } protected abstract List<T> GetEntities(); public void LoadItems() { var lst = GetEntities(); Items.ReplaceRange(lst); } } 

Naturally, the compiler requires the implementation of RangeObservableCollection.Items

I could do this:

 public interface IEntityListViewModel<T> where T : IEntity { RangeObservableCollection<T> Items { get; set; } T SelectedItem { get; set; } void LoadItems(); } 

But I have another class:

 public abstract class UserControlBase : UserControl { public IEntityListViewModel VM { get; set; } public virtual void Page_Loaded(object sender, RoutedEventArgs e) { VM.LoadItems(); } } 

And then specific UserControls are inherited from UserControlBase. What is it for me? In UserControls, a bunch of duplicate code (mostly binding to ViewModel events). Therefore, all I want to do in UserControlBase. I could declare in UserControlBase like this:

 public IEntityListViewModel<T> VM { get; set; } 

but then I need to raise T in UserControlBase, but this is somehow crooked.

How do I inherit an EntityListViewModel from a regular IEntityListViewModel? Or is there some other solution?

UPD 1: Explicitly implemented, as advised in the comments:

 public abstract class EntityListViewModel<T> : IEntityListViewModel where T : IEntity { public RangeObservableCollection<T> Items { get; set; } public T SelectedItem { get; set; } RangeObservableCollection<IEntity> IEntityListViewModel.Items { get; set; } IEntity IEntityListViewModel.SelectedItem { get; set; } public EntityListViewModel() { Items = new RangeObservableCollection<T>(); } protected abstract List<T> GetEntities(); public void LoadItems() { var lst = GetEntities(); Items.ReplaceRange(lst); } } 

But now in View, the VM.Items call is called IEntityListViewModel.Items

  • what prevents to explicitly implement this property? - Grundy
  • Is this your WinForms or WPF? It is not clear why you suddenly needed to become attached to VM events. - VladD
  • WPF. And how without a binding to events in VM? For example, there is the ErrorOccured Action <string> event, it happens in the ViewModel, and the View should react somehow (show a MessageBox, for example). - Andrew K
  • @AndrewK: No, no, you're doing something wrong. VM usually exposes INPC, View responds via Binding. You should try not to know what types in the VM are in View. - VladD
  • @AndrewK: And starting a dialogue with a user is not at all a VM, it’s part of business logic. - VladD

1 answer 1

In the current form, the easiest for you would be to declare UserControlBase generic. If this method does not suit you for some reason, you must take storage from the base class to the heirs.

In your case, if the base class subscribes to events, it needs the INotifyCollectionChanged interface. He should be requested from the heir:

 public abstract class UserControlBase : UserControl { protected abstract INotifyCollectionChanged AbstractItems { get; } } public class FooListControl : UserControlBase { public IEntityListViewModel<Foo> VM { get; set; } protected virtual INotifyCollectionChanged AbstractItems => VM.Items; } 

If with this approach there are too many fields in the base class, then the covariant part should be selected in the interface.

For example, for your interface, IEntityListViewModel can be broken down like this:

 public interface IEntityListViewModel<T> : IEntityListViewModelOut<T> where T : IEntity { RangeObservableCollection<T> Items { get; set; } T SelectedItem { get; set; } } public interface IEntityListViewModelOut<out T> where T : IEntity { IReadOnlyList<T> Items { get; } INotifyCollectionChanged ItemsEvents { get; } T SelectedItem { get; } void LoadItems(); } 

Implementing such a "complicated" interface is not much more complicated than the usual one:

 public class EntityListViewModel<T> : IEntityListViewModel<T> where T : IEntity { public RangeObservableCollection<T> Items { get; set; } = new RangeObservableCollection<T>(); public T SelectedItem { get; set; } IEntityListViewModelOut<T>.Items => Items; IEntityListViewModelOut<T>.ItemsEvents => Items; IEntityListViewModelOut<T>.SelectedItem => SelectedItem; } 

After that, the covariant part can be "forwarded" to the base class through an abstract property:

 public abstract class UserControlBase : UserControl { protected abstract IEntityListViewModelOut<IEntity> AbstractVM { get; } } public class FooListControl : UserControlBase { public IEntityListViewModel<Foo> VM { get; set; } protected virtual IEntityListViewModelOut<IEntity> AbstractVM => VM; }