Агрегатор событий Caliburn с несколькими представлениями

Я пытаюсь построить этот проект, используя Caliburn в первый раз (а также структуру MEF, которую я не совсем понял).

Мне нужно использовать и Проводник, и EventAggregator. Проводник, потому что у меня есть AppViewModel, который «отображает» 3 кнопки, которые перемещают пользователя в 3 разных представления (UserControls внутри AppView).

И мне нужен EventAggregator, потому что в одном из этих трех представлений есть кнопка внутри, которая должна загружать 4-е представление (я думаю, это должно быть окно, а не пользовательский элемент управления, потому что он должен быть полноэкранным). Итак, я подумал, что когда пользователь нажимает эту кнопку внутри 3-го представления (UserControl внутри AppView), сообщение может быть отправлено поверх прослушивателя (это должна быть AppViewModel), а это должно быть ActivateItem (4th vm).

Не знаю почему, но даже по примерам проектов Caliburn мое сообщение не доходит до AppViewModel.

Это мой загрузчик:

public class AppBootstrapper : Bootstrapper<AppViewModel>
    {
        private CompositionContainer container;

        protected override void Configure()
        {
            container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x =>
                new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));

            CompositionBatch batch = new CompositionBatch();

            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(container);

            container.Compose(batch);
        }

        protected override object GetInstance(Type serviceType, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
            var exports = container.GetExportedValues<object>(contract);

            if (exports.Any())
                return exports.First();

            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }

        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        protected override void BuildUp(object instance)
        {
            container.SatisfyImportsOnce(instance);
        }

        protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<AppViewModel>();
        }
    }

Это AppViewModel:

[Export (typeof(AppViewModel))]
    public class AppViewModel : Conductor<object>, IHandle<ChangeViewEvent>
    {
        [ImportingConstructor]
        public AppViewModel(IEventAggregator events)
        {
            events.Subscribe(this);
            ActivateItem(new MainViewModel());            
        }

        public void GoToPatientsManager()
        {
            ActivateItem(new PatientsManagerViewModel(new WindowManager(), new EventAggregator()));
        }

        public void GoToTestManager()
        {
            ActivateItem(new TestManagerViewModel(new WindowManager()));
        }

        public void GoToResultsManager()
        {
            ActivateItem(new MainViewModel());
        }

        public void Handle(ChangeViewEvent message)
        {
            switch (message.ViewName)
            {
                case "TestManager" :
                    GoToTestManager();
                    break;
            }
        }
    }

А это модель представления, которая должна запускать запрос на загрузку 4-й вм

 [Export(typeof(PatientsManagerViewModel))]
    public class PatientsManagerViewModel : Screen
    {
        private readonly IWindowManager _windowManager;
        private readonly IEventAggregator eventAggregator;

        [ImportingConstructor]
        public PatientsManagerViewModel(IWindowManager windowManager, IEventAggregator eventAggregator)
        {
            _windowManager = windowManager;
            this.eventAggregator = eventAggregator;
        }

        #region Methods

        public void ShowFakeMessage()
        {
            dynamic settings = new ExpandoObject();
            settings.Placement = PlacementMode.Center;
            settings.PlacementTarget = GetView(null);

            var res = _windowManager.ShowDialog(new DeletePersonViewModel(), null, settings);

            if (res)
            {
                // The result of the dialog men. In this true case we'll use Linq to delete the entry from the database
                // using the dbContext
            }
        }

        public void GoToTestManager()
        {
            eventAggregator.Publish(new ChangeViewEvent("TestManager"));
        }

        #endregion
    }

Он не достигает метода Handle модели AppViewModel.

Это что-то не так с экземплярами моделей представления? Я не могу двигаться дальше отсюда...

РЕДАКТИРОВАТЬ

Может быть, проблема в том, что я передаю новый объект EventAggregator каждый раз, когда активирую новую модель PatientManagerViewModel? Какие-нибудь советы?


person Ivan_nn2    schedule 19.03.2014    source источник


Ответы (1)


Вы сами диагностировали свою проблему. Вы сами создаете PatientManagerViewModel и каждый раз передаете новый агрегатор событий. Идея состоит в том, что агрегатор событий должен быть одноэлементным, то есть одним экземпляром, общим для всех моделей представлений и всего остального. Это связано с тем, что агрегатор событий хранит подписчиков в памяти, и новый экземпляр не будет знать, кому сообщать о новых событиях, потому что никто не подписался.

Я бы посоветовал, чтобы ваш AppViewModel.GoToPatientsManager() выглядел примерно так:

public void GoToPatientsManager()
{
    var patientManagerViewModel = IoC.Get<PatientsManagerViewModel>();
    ActivateItem(patientManagerViewModel);
}

IoC - это (довольно уродливый, трудно тестируемый) способ доступа к вашему контейнеру. Вы не должны создавать свои модели представления самостоятельно, вместо этого вы должны заставить контейнер caliburn сделать это за вас. Если контейнер создает ваши модели представления, то он также исправит любые зависимости, которые у него есть для вас, включая оконный менеджер и агрегатор событий.

person Jack Hughes    schedule 15.01.2015