Ответ 1
Могу ли я предложить немного другой маршрут?
Это то, что я успешно выполнял в сценариях основных деталей. Скажем, у вас есть коллекция моделей детского просмотра. Я подготовлю интерфейс маркера для всех этих элементов, конечно, вы можете добавить свойства/методы, которые вам подходят, если есть такие методы, которые охватывают все модели детского вида:
public interface IMainScreenTabItem : IScreen
{
}
Вы можете быть уверены, что хотите, чтобы все ваши дочерние модели были Screen
(или, в случае вложенных сценариев, Conductor
s). Это заставляет их иметь полный цикл инициализации/активации/деактивации.
Затем модели child view:
public sealed class ChRemoteViewModel : Screen, IMainScreenTabItem
{
public ChRemoteViewModel()
{
DisplayName = "CH Remote";
}
}
public sealed class PcInfoViewModel : Screen, IMainScreenTabItem
{
public PcInfoViewModel()
{
DisplayName = "PC Info";
}
}
public sealed class RemoteToolsViewModel : Screen, IMainScreenTabItem
{
public RemoteToolsViewModel()
{
DisplayName = "Remote Tools";
}
}
DisplayName
будет отображаться как текст заголовка. Хорошая практика заключается в том, чтобы сделать эти классы запечатаны, потому что DisplayName
является виртуальным свойством, и большой нет-нет для вызова виртуальных методов в конструкторе класса, который не запечатан.
Затем вы можете добавить соответствующие представления и установить свой контейнер IoC для выбора регистраций - вам необходимо зарегистрировать все модели просмотра для детей как классы, реализующие IMainScreenTabItem
, а затем:
public class MainViewModel : Conductor<IMainScreenTabItem>.Collection.OneActive
{
public MainViewModel(IEnumerable<IMainScreenTabItem> tabs)
{
Items.AddRange(tabs);
}
}
Где MainView.xaml
справедливо:
<TabControl Name="Items"/>
И это просто работает. Это также очень приятное и удобное решение, если модели просмотра вашего ребенка принимают несколько зависимостей (например, доступ к базе данных, регистратор, механизм проверки и т.д.), Теперь вы можете использовать IoC для всего тяжелого лифтинга, а не для его создания вручную.
Одна вещь здесь: вкладки будут помещены в том же порядке, в который вводятся классы. Если вы хотите получить контроль над заказом, вы можете заказать их в конструкторе MainViewModel
путем передачи пользовательского IComparer<IMainScreenTabItem>
или добавления некоторого свойства, которое вы можете OrderBy
, или выберите интерфейс IMainScreenTabItem
. Выбранный по умолчанию элемент будет первым в списке Items
.
Другой вариант - сделать MainViewModel
тремя параметрами:
public MainViewModel(ChRemoteViewModel chRemoteViewModel, PcInfoViewModel pcInfo, RemoteToolsViewModel remoteTools)
{
// Add the view models above to the `Items` collection in any order you see fit
}
Хотя, если у вас более 2 - 3 моделей детского просмотра (и вы можете легко получить больше), это будет беспорядочно быстро.
О части "очистки". Модели представления, созданные IoC, переходят в обычный жизненный цикл: они инициализируются не более одного раза (OnInitialize
), затем деактивируются каждый раз, когда они перемещаются в сторону от OnDeactivate(bool)
и активируются, когда они перемещаются (OnActivate
). Параметр bool
в OnDeactivate
указывает, просто ли отключена модель просмотра или полностью закрыта (например, когда вы закрываете диалоговое окно и перемещаетесь). Если вы полностью закрываете модель представления, она будет повторно инициализирована в следующий раз, когда она будет показана.
Это означает, что любые связанные данные будут сохраняться между вызовами OnActivate
, и вам нужно будет явно очистить его в OnDeactivate
. Что еще, если вы сохраните сильную ссылку на модели просмотра вашего ребенка, то даже после вызова OnDeactivate(true)
данные будут по-прежнему присутствовать при следующей инициализации - что, поскольку модели ввода-вывода с IoC создаются один раз (если вы не введете factory в форме Func<YourViewModel>
), а затем инициализируется/активируется/деактивируется по требованию.
ИЗМЕНИТЬ
О загрузчике, я не совсем уверен, какой контейнер IoC вы используете. В моем примере используется SimpleInjector, но вы можете сделать то же самое, что и легко, например. Autofac:
public class AppBootstrapper : Bootstrapper<MainViewModel>
{
private Container container;
/// <summary>
/// Override to configure the framework and setup your IoC container.
/// </summary>
protected override void Configure()
{
container = new Container();
container.Register<IWindowManager, WindowManager>();
container.Register<IEventAggregator, EventAggregator>();
var viewModels =
Assembly.GetExecutingAssembly()
.DefinedTypes.Where(x => x.GetInterface(typeof(IMainScreenTabItem).Name) != null && !x.IsAbstract && x.IsClass);
container.RegisterAll(typeof(IMainScreenTabItem), viewModels);
container.Verify();
}
/// <summary>
/// Override this to provide an IoC specific implementation.
/// </summary>
/// <param name="service">The service to locate.</param><param name="key">The key to locate.</param>
/// <returns>
/// The located service.
/// </returns>
protected override object GetInstance(Type service, string key)
{
if (service == null)
{
var typeName = Assembly.GetExecutingAssembly().DefinedTypes.Where(x => x.Name.Contains(key)).Select(x => x.AssemblyQualifiedName).Single();
service = Type.GetType(typeName);
}
return container.GetInstance(service);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
container.InjectProperties(instance);
}
}
Обратите внимание на регистрацию viewModels
в Configure
.