具有多个视图的Caliburn Event Aggregator

时间:2014-03-19 14:14:15

标签: c# wpf caliburn

我是第一次尝试使用Caliburn构建这个项目(以及MEF结构,我还没有完全理解)。

我需要同时使用Conductor和EventAggregator。 指挥,因为我有一个AppViewModel"显示" 3个按钮,用户将用户移动到3个不同的视图(AppView内的UserControls)。

我需要EventAggregator,因为其中一个视图中有一个按钮,它必须加载第四个视图(我认为必须是一个Window,而不是UserControl,因为它必须是全屏)。 所以我认为,当用户在3视图(AppView内的UserControl)中单击此按钮时,可以在侦听器(应该是AppViewModel)的顶部发送一条消息,这个应该是ActivateItem(第4个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个vm的请求的视图模型

 [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
    }

它没有到达AppViewModel的Handle方法。

这些视图模型的实例是否有问题?我无法从这里前进......

修改

问题是我每次激活一个新的PatientsManagerViewModel时都会传递一个新的EventAggregator对象吗?有什么提示吗?

1 个答案:

答案 0 :(得分:1)

您诊断出自己的问题。您自己创建了PatientsManagerViewModel并且每次都传入一个新的事件聚合器。我们的想法是事件聚合器必须是单例,即在所有视图模型和其他所有内容之间共享的一个实例。这是因为事件聚合器将订阅者存储在内存中,并且新实例将不知道谁将通知新事件,因为没有人会订阅。

我建议你的AppViewModel.GoToPatientsManager()应该是这样的:

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

IoC是一种(非常难看,难以测试)的方式,您可以访问您的容器。你不应该自己创建你的视图模型,你应该让你的校准容器为你做这件事。如果容器创建了视图模型,那么它也将修复它为您提供的任何依赖项,包括窗口管理器和事件聚合器。