У меня есть классы и интерфейсы:
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();
}
Теперь мне нужен такой класс:
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);
}
}
Естественно, компилятор требует реализации RangeObservableCollection.Items
Я бы мог сделать так:
public interface IEntityListViewModel<T> where T : IEntity
{
RangeObservableCollection<T> Items { get; set; }
T SelectedItem { get; set; }
void LoadItems();
}
Но у меня есть еще один класс:
public abstract class UserControlBase : UserControl
{
public IEntityListViewModel VM { get; set; }
public virtual void Page_Loaded(object sender, RoutedEventArgs e)
{
VM.LoadItems();
}
}
А дальше уже конкретные UserControl'ы наследуются от UserControlBase. Для чего это мне? В UserControl'ах куча повторяющегося кода (в основном привязки событий ViewModel). Поэтому это все я хочу сделать в UserControlBase. Я бы мог объявить в UserControlBase так:
public IEntityListViewModel<T> VM { get; set; }
но тогда мне T нужно поднимать в UserControlBase, а это как-то криво.
Как мне наследовать EntityListViewModel от обычного IEntityListViewModel? Или есть какое-то другое решение?
UPD 1: Явно реализовал, как посоветовали в комментариях:
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);
}
}
Но теперь во View вызов VM.Items обращается к IEntityListViewModel.Items
В текущем виде самым простым для вас было бы действительно объявить UserControlBase
обобщенным. Если же этот способ вам по какой-то причине не подойдет - надо выносить хранение из базового класса в наследники.
В вашем случае, если базовый класс подписывается на события - ему нужен интерфейс INotifyCollectionChanged
. Его и надо запрашивать у наследника:
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;
}
Если при таком подходе в базовом классе оказывается слишком много полей - надо выделять в интерфейсе ковариантную часть.
Например, для ваш интерфейс IEntityListViewModel
можно разбить следующим образом:
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();
}
Реализовать такой "усложненный" интерфейс не сильно сложнее чем обычный:
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;
}
После этого, ковариантную часть можно "пробросить" в базовый класс через абстрактное свойство:
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;
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Могу ли я каким то образом, запустив httpListener по префиксу "http://+:8080", после этого послать на него запрос? Или же символы юникода нельзя использовать...