代码之家  ›  专栏  ›  技术社区  ›  LokleLama

caliburn.micro冷凝器不工作

  •  2
  • LokleLama  · 技术社区  · 12 年前

    我们正在使用caliburn.micro进行我们的一个项目,目前我遇到了一个令人困惑的问题:

    我们有以下课程:

    ToolViewerViewModel : Conductor<Screen>.Collection.OneActive
    DocViewerViewModel : Conductor<DocumentViewModel>
    

    以及各种文档视图,所有这些视图都具有此基类:

    DocumentViewModel : Screen
    

    这个 ToolViewerViewModel 是管理允许用户控制程序的不同方面的多个可停靠工具视图。

    这个 DocViewerViewModel 是向用户显示他正在处理/使用的数据。这里是为了呈现众多 DocumentViewModel 给用户。并且被实现为特殊的可停靠视图 ToolViewerView 。对于数据的每个方面 文档视图模型 DocViewerViewModel 并呈现给用户。

    这个 文档视图模型 是数据的所有表示方面的基类。一个可以将数据显示为表格,另一个可以显示为图表,依此类推。。。

    我们现在遇到的问题是 OnActivate() OnDeactivate() 当我们期望它们被调用时,它们不会被调用。

    第一个问题: 系统已启动并正在运行;这个 文档视图模型 显示在 DocViewerViewModel 它嵌入在 工具查看器视图模型 以及一个或两个其他可停靠视图。当前选定的可停靠视图是 DocViewerViewModel 。当用户现在选择其他停靠视图之一时 启用停用() 方法 文档视图模型 正在调用。这对我来说毫无意义。我希望 DocViewerViewModel.OnDeactivate() 将被调用。

    第二个问题: 系统已启动并正在运行;这个 文档视图模型 显示在 DocViewerViewModel 它嵌入在 工具查看器视图模型 以及一个或两个其他可停靠视图。当前选定的可停靠视图是允许用户更改 文档视图模型 DocViewerViewModel 。当用户现在选择另一个 文档视图模型 以下代码正在 DocViewerViewModel :

    DocViewerViewModel.DeactivateItem(oldDocumentViewModel, true);
    DocViewerViewModel.ActivateItem(new DocumentViewModel());
    

    我希望 DocumentViewModel.OnDeactivate() 被传唤 DocViewerViewModel.DeactivateItem(oldDocumentViewModel, true) 呼叫但这永远不会发生。

    结论: 唯一正常工作的导体是 工具查看器视图模型 它在管理一切。但这种行为并不是我们想要或期望发生的:我们希望 工具查看器视图模型 仅执行可停靠视图和 DocViewerViewModel 进行 文档视图模型 。这很重要,因为有两种不同的用例:一种是同时管理多个实例,另一种是只有一个实例处于活动状态并被使用时,旧的实例将被丢弃。

    希望这里的任何人都能帮助我获得我想要的行为。

    我现在有一个示例代码给您:

    public class ToolViewerViewModel : Conductor<Screen>.Collection.OneActive
    {
        private readonly IDockManager _dockManager;
        private readonly DocViewerViewModel _docViewerViewModel;
        private readonly IList<DockableViewModel> _toolViews = new List<DockableViewModel>();
    
        public ToolViewerViewModel(IViewModelFactory viewModelFactory, DocViewerViewModel docViewerViewModel, IDockManager dockManager)
        {
            _dockManager = dockManager;
            _viewModelFactory = viewModelFactory;
            _docViewerViewModel = docViewerViewModel;
        }
    
        protected override void OnViewLoaded(object view)
        {
            _dockManager.Link(this);
            _dockManager.CreateSpecialPaneFor(_docViewerViewModel);
            ActivateItem(_docViewerViewModel);
    
            ShowToolView<ProjectExplorerViewModel>();
            base.OnViewLoaded(view);
        }
    
        public void ShowToolView<T>() where T : DockableViewModel
        {
            if (!IsToolViewOpen<T>())
            {
                var viewModel = _viewModelFactory.Create<T>();
                ActivateItem(viewModel);
                RefreshMenu(typeof(T));
            }
        }
    }
    

    下一节课:

    public class DocViewerViewModel : Conductor<DocumentViewModel>
    {
        private readonly IViewModelFactory _viewModelFactory;
    
        public DocViewerViewModel(IViewModelFactory viewModelFactory)
        {
            _viewModelFactory = viewModelFactory;
        }
    
        public bool ShowInMainView<T>() where T : DocumentViewModel
        {
            return ShowInMainView(typeof(T));
        }
    
        private bool ShowInMainView(Type viewModelType)
        {
            var ret = false;
    
            // close the current view
            if (ActiveItem != null)
            {
                DeactivateItem(ActiveItem, true); //The close flag is on true since we want to remove the current instance from the memory
            }
    
            // check whether the current viewModel has been closed successfully
            if (ActiveItem == null)
            {
                try
                {
                    var viewModel = _viewModelFactory.Create(viewModelType) as DocumentViewModel;
                    if (viewModel != null)
                    {
                        ActivateItem(viewModel);
                        ret = true;
                    }
                    else
                    {
                        ActivateItem(_viewModelFactory.Create<NoDataViewModel>());
                    }
                }
                catch (Exception ex)
                {
                    ActivateItem(_viewModelFactory.Create<NoDataViewModel>());
                }
            }
    
            return ret;
        }
    }
    

    最后一个:

    public abstract class DocumentViewModel : Screen
    {
        private bool _isDirty;
    
        protected IViewModelFactory ViewModelFactory { get; private set; }
        protected IEventAggregator EventAggregator { get; private set; }
    
        public bool IsDirty
        {
            get
            {
                return _isDirty;
            }
            protected set
            {
                if (value.Equals(_isDirty))
                {
                    return;
                }
                _isDirty = value;
                NotifyOfPropertyChange(() => IsDirty);
            }
        }
    
        protected DocumentViewModel(IViewModelFactory viewModelFactory, IEventAggregator eventAggregator)
        {
            ViewModelFactory = viewModelFactory;
            EventAggregator = eventAggregator;
        }
    
        protected override void OnDeactivate(bool close)
        {
            if (close)
            {
                if (EventAggregator != null)
                {
                    EventAggregator.Unsubscribe(this);
                }
            }
            base.OnDeactivate(close);
        }
    
        protected override void OnActivate()
        {
            if (EventAggregator != null)
            {
                EventAggregator.Subscribe(this);
            }
            base.OnActivate();
        }
    
        public override void CanClose(Action<bool> callback)
        {
            var ret = true;
            if (IsDirty && (ViewModelFactory != null))
            {
                var saveDialog = ViewModelFactory.Create<SaveDialogViewModel>();
                saveDialog.Show();
                if (saveDialog.DialogResult == DialogResult.Cancel)
                {
                    ret = false;
                }
                else
                {
                    if (saveDialog.DialogResult == DialogResult.Yes)
                    {
                        Save();
                    }
                    else
                    {
                        Discard();
                    }
                    IsDirty = false;
                }
            }
            callback(ret);
        }
    
        public abstract void Save();
    
        public virtual void Discard()
        {
        }
    }
    

    使用此代码 DocumentViewModel.OnDeactivate() 当用户在 DocViewerViewModel 是焦点所在。这不应该发生!

    当用户在可停靠视图之间更改焦点时 DocumentViewModel.OnDeactivate() 不应该接到电话。但当方法 DocViewerViewModel.ShowInMainView<SomeDocumentViewModel>() 正在调用。目前情况并非如此。

    4 回复  |  直到 12 年前
        1
  •  1
  •   Chui Tey    12 年前

    据我所知,代码的编写方式没有任何问题。既然您使用的是MVVM,我建议您设计一个我提供的测试用例 here .

    下面是测试用例的片段

    // TestHarness.cs
    
    [TestMethod]
    public void CheckDeactivation()
    {
        // We'd like to have the ToolViewerViewModel only Conduct the dock-able views 
        // and the DocViewerViewModel to conduct the DocumentViewModel.
    
        IViewModelFactory factory = new ViewModelFactory();
        DocViewerViewModel docViewer = new DocViewerViewModel(factory);
        IDockManager dockManager = null;
    
        var toolViewer = new ToolViewerViewModel(factory, docViewer, dockManager);
        var mockToolView = new UserControl();           
        (toolViewer as IViewAware).AttachView(mockToolView);
    
        DocumentViewModel docView1 = new NoDataViewModel();
        DocumentViewModel docView2 = new NoDataViewModel();
        docViewer.ActivateItem(docView1);
        docViewer.ActivateItem(docView2);
    
        Assert.AreEqual(0, docViewer.CountDeactivated());
    }
    
        2
  •  1
  •   Samuel Neugber    11 年前

    我遇到了与您完全相同的问题,最终使用了PropertyChangedBase而不是Screen,问题消失了。 稍后,在阅读屏幕和导线上的文档后 here ,我意识到我并没有在视图层次结构中进一步激活导体本身!

    因此,请查看您使用ToolViewerViewModel的位置,并确保激活该实例!

        3
  •  0
  •   LokleLama    12 年前

    非常感谢您的测试。即使认为这是非常好的代码,它也测试了错误的代码部分。您的代码只是测试方法 ActivateItem() DeactivateItem() 正在调用:

    public override void ActivateItem(DocumentViewModel item)
    {
        _countActivated++;
        base.ActivateItem(item);
    }
    
    public override void DeactivateItem(DocumentViewModel item, bool close)
    {
         _countDeactivated++;
         base.DeactivateItem(item, close);
    }
    

    但由于这些方法是显式调用的,我们不需要为此进行测试。。。

    真正的问题是列车长没有给 OnActivate() OnDeactivate() DocumentViewModel 班为了增强您的测试,我使用了以下代码:

    public class DummyViewModelFactory : IViewModelFactory
    {
        private readonly Dictionary<Type, Func<object>> _registredCreators = new Dictionary<Type, Func<object>>();
    
        public T Create<T>() where T : PropertyChangedBase
        {
            return Create(typeof(T)) as T;
        }
    
        public object Create(Type type)
        {
            if (type == null)
            {
                return null;
            }
    
            if (_registredCreators.ContainsKey(type))
            {
                return _registredCreators[type]();
            }
    
            return null;
        }
    
        public void Release(object instance)
        {
        }
    
        public void RegisterCreatorFor<T>(Func<T> creatorFunction)
        {
            _registredCreators.Add(typeof(T), () => creatorFunction());
        }
    }
    

    作为混凝土 文档视图模型 我实施了:

    public class NoDataViewModel : DocumentViewModel
    {
        public NoDataViewModel(IEventAggregator eventAggregator,
            IViewModelFactory viewModelFactory)
            : base(viewModelFactory, eventAggregator, )
        {
        }
    
        public override void Save()
        {
            // nothing to do
        }
    
        public override void Reload()
        {
            // nothing to do
        }
    }
    
    public class NoDataViewModelMock : NoDataViewModel
    {
        private static int activationCounterForTesting = 0;
        private static int deactivationCounterForTesting = 0;
    
        public static int ActivationCounterForTesting
        {
            get
            {
                return activationCounterForTesting;
            }
        }
    
        public static int DeactivationCounterForTesting
        {
            get
            {
                return deactivationCounterForTesting;
            }
        }
    
        public NoDataViewModelMock()
            : base(null, null)
        {
        }
    
        protected override void OnActivate()
        {
            activationCounterForTesting++;
            base.OnActivate();
        }
    
        protected override void OnDeactivate(bool close)
        {
            deactivationCounterForTesting++;
            base.OnDeactivate(close);
        }
    }
    

    我把你的测试方法改成了:

    [TestMethod]
    public void CheckDeactivation()
    {
        var viewModelFactory = new DummyViewModelFactory();
    
        viewModelFactory.RegisterCreatorFor<NoDataViewModel>(() => new NoDataViewModelMock());
    
        var docViewer = new DocViewerViewModel(viewModelFactory);
        IDockManager dockManager = null;
    
        var toolViewer = new ToolViewerViewModel(viewModelFactory, docViewer, dockManager);
        var mockToolView = new UserControl();
        (toolViewer as IViewAware).AttachView(mockToolView);
    
        docViewerViewModel.ShowInMainView<NoDataViewModel>();
        docViewerViewModel.ShowInMainView<NoDataViewModel>();
        docViewerViewModel.ShowInMainView<NoDataViewModel>();
    
        Assert.AreEqual(3, NoDataViewModelMock.ActivationCounterForTesting);
        Assert.AreEqual(2, NoDataViewModelMock.DeactivationCounterForTesting);
    }
    

    然后你会看到 OnActivate() 启用停用() 方法从未被调用。

    通过更高级的测试,您还可以看到他们正在被调用,但从 ToolViewerViewModel 直接地我想知道为什么以及如何改变这种行为以满足我的需求:

    • 这个 DocumentViewModel.OnActivate() DocViewerViewModel.ShowInMainView<T>() 方法。
    • 这个 DocumentViewModel.OnDeactivate() 方法应在旧 文档视图模型 调用 DocViewerViewModel.ShowInMainView<T>()
        4
  •  0
  •   LokleLama    12 年前

    我们解决这个问题的方法是取消使用 Screen 作为BaseClass DocViewerViewModel 实现我们自己的导体逻辑。

    推荐文章