导航与微卡利 [英] Navigation with Caliburn Micro

查看:213
本文介绍了导航与微卡利的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我与Caliburn.Micro玩弄,现在有一个非常简单的应用程序。

I'm playing around with Caliburn.Micro and have a very simple application now.

它有一个APPVIEW,这实际上有一个导航栏一ContentControl中,一个InnerView和状态栏。

It has an AppView, which actually has a ContentControl for a NavigationBar, an InnerView and a StatusBar.

现在我想不同的内部视图之间处理导航。

Now I want to handle Navigation between different inner views.

现在我用在eventaggregator发布一个NavigationEvent,这应该
切换主窗口到另一个视图的innerview。

Right now I use the eventaggregator to publish a NavigationEvent, which should switch the innerview of the mainwindow to another view.

下面是我的呼吁发布(所有InnerViews具有相同基类有一个IEventAggregator)

Here is my call to Publish (all InnerViews have the same baseclass which has an IEventAggregator)

public void NavigateOverview()
{
    base._eventAggregator.Publish(new NavigateEvent("OverviewViewModel"));
}



现在我通过一个字符串AppViewModel,它处理NavigateEvent:

Right now I pass a string to the AppViewModel, which handles the NavigateEvent:

        public void Handle(NavigateEvent navigate)
        {
            InnerViewModel target;

            switch (navigate.TargetViewModel)
            {
                case "SelectProjectViewModel":
                {
                    target = new SelectProjectViewModel(_eventAggregator);
                    break;
                }
                case "OverviewViewModel":
                {
                    target = new OverviewViewModel(_eventAggregator);
                    break;
                }
                default:
                {
                    throw new InvalidOperationException("no target type found");
                }
            }

            this.CurrentInnerViewModel = target;
        }



字符串传递工作,但容易出错,不是很干净。

Passing strings works, but is errorprone and not very clean.

什么是处理的方式卡利?
是什么指挥应该怎么办?

What is the Caliburn way of handling that? Is that what a Conductor should do?

推荐答案

为什么不只是传递一种类型呢?这样,有没有神奇的字符串

Why not just pass a type instead? That way there are no magic strings

例如

public void NavigateOverview()
{
    base._eventAggregator.Publish(new NavigateEvent(typeof(OverviewViewModel)));
}



然后:

then:

    public void Handle(NavigateEvent navigate)
    {
        InnerViewModel target;

        // EDIT: Remove the case (only works with integral types so you can't use typeof etc)
        // but you could do this with standard conditional logic

        this.CurrentInnerViewModel = target;
    }



编辑2:

Edit 2:

既然你没事问关于建立内容管理系统的IoC,这里是使用与温莎城堡国际奥委会和传递额外的参数导航解决方案的例子(从借款 EventAggregator

Ok since you asked about building into CMs IoC, here is an example for using the IoC with Castle Windsor and a solution for passing additional parameters to navigation (borrowing from EventAggregator)

引导程序只需要几点点滴滴配置容器:

The bootstrapper just needs a few bits and pieces to config the container:

public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
    // The Castle Windsor container
    private IWindsorContainer _container;

    protected override void Configure()
    {
        base.Configure();

        // Create the container, install from the current assembly (installer code shown in next section below)
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
    }

    // Matches up with Windsors ResolveAll nicely
    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        return (IEnumerable<object>)_container.ResolveAll(service);
    }

    // Matches up with Windsors Resolve
    protected override object GetInstance(Type service, string key)
    {
        return string.IsNullOrEmpty(key) ? _container.Resolve(service) : _container.Resolve(key, service);
    }

    // Windsor doesn't do property injection by default, but it's easy enough to get working:
    protected override void BuildUp(object instance)
    {
        // Get all writable public properties on the instance we will inject into
        instance.GetType().GetProperties().Where(property => property.CanWrite && property.PropertyType.IsPublic)
        // Make sure we have a matching service type to inject by looking at what's registered in the container
                                          .Where(property => _container.Kernel.HasComponent(property.PropertyType))
        // ...and for each one inject the instance
                                          .ForEach(property => property.SetValue(instance, _container.Resolve(property.PropertyType), null));
    }
}



温莎安装程序为CM将可能是那样简单

The Windsor Installer for CM will probably be as simple as:

public class CaliburnMicroInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        // Register the window manager
        container.Register(Component.For<IWindowManager>().ImplementedBy<WindowManager>());

        // Register the event aggregator
        container.Register(Component.For<IEventAggregator>().ImplementedBy<EventAggregator>());
    }
}



我也有一个导航服务接口,应用程序,以帮助导航:

I also have a navigation service interface to aid with application navigation:

public interface INavigationService
{
    void Navigate(Type viewModelType, object modelParams);
}



这是由的NavigationService (告诉你,在几秒钟之内)

Which is implemented by NavigationService (show you that in a sec)

这需要一个温莎安装过:

That needs a Windsor installer too:

public class NavigationInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<INavigationService>().ImplementedBy<NavigationService>());
    }
}



的NavigationService 很像 EventAggregator 在暴露导航参数应实现的每个参数类,它可以接收...

The NavigationService works much like EventAggregator in that the type that exposes navigation arguments should implement a generic interface for each argument class that it can receive...

界面看起来像这样(从EventAggregator大量举债):

The interface looks like this (borrowing heavily from EventAggregator):

// This is just to help with some reflection stuff
public interface IViewModelParams { }

public interface IViewModelParams<T> : IViewModelParams        
{
    // It contains a single method which will pass arguments to the viewmodel after the nav service has instantiated it from the container
    void ProcessParameters(T modelParams);
}



例如:

example:

public class ExampleViewModel : Screen, 
    // We can navigate to this using DefaultNavigationArgs...
    IViewModelParams<DefaultNavigationArgs>, 
    // or SomeNavigationArgs, both of which are nested classes...
    IViewModelParams<SomeOtherNavigationArgs>
{
    public class DefaultNavigationArgs
    {
        public string Value { get; private set; }

        public DefaultNavigationArgs(string value)
        {
            Value = value;
        }
    }

    public class OtherNavigationArgs
    {
        public int Value { get; private set; }

        public DefaultNavigationArgs(int value)
        {
            Value = value;
        }
    }

    public void ProcessParameters(DefaultNavigationArgs modelParams)
    {            
        // Do something with args
        DisplayName = modelParams.Value;
    }

    public void ProcessParameters(OtherNavigationArgs modelParams)
    {            
        // Do something with args. this time they are int!
        DisplayName = modelParams.Value.ToString();
    }
}

这会导致一些强类型的导航(如重构友好!)

This leads to some strongly typed navigation (e.g. refactor friendly!)

NavigationService.Navigate(typeof(ExampleViewModel), new ExampleViewModel.DefaultNavigationArgs("hello"));

NavigationService.Navigate(typeof(ExampleViewModel), new ExampleViewModel.OtherNavigationArgs(15));



这也意味着,在ViewModel仍然在它自己的导航参数

It also means that the ViewModel is still in control of it's own navigation parameters

确定回到温莎为秒;很明显,我们需要从我们的观点命名空间中安装任何意见 - 温莎公爵夫妇一口流利的API使这很简单:

Ok back to Windsor for a sec; obviously we need to install any views from our views namespace - Windsors fluent API makes this pretty easy:

public class ViewInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        // The 'true' here on the InSameNamespaceAs causes windsor to look in all sub namespaces too
        container.Register(Classes.FromThisAssembly().InSameNamespaceAs<ShellViewModel>(true));
    }
}



好了现在的NavigationService 实施

public class NavigationService : INavigationService
{
    // Depends on the aggregator - this is how the shell or any interested VMs will receive
    // notifications that the user wants to navigate to someplace else
    private IEventAggregator _aggregator;

    public NavigationService(IEventAggregator aggregator)
    {
        _aggregator = aggregator;
    }

    // And the navigate method goes:
    public void Navigate(Type viewModelType, object modelParams)
    {
        // Resolve the viewmodel type from the container
        var viewModel = IoC.GetInstance(viewModelType, null);

        // Inject any props by passing through IoC buildup
        IoC.BuildUp(viewModel);

        // Check if the viewmodel implements IViewModelParams and call accordingly
        var interfaces = viewModel.GetType().GetInterfaces()
               .Where(x => typeof(IViewModelParams).IsAssignableFrom(x) && x.IsGenericType);

        // Loop through interfaces and find one that matches the generic signature based on modelParams...
        foreach (var @interface in interfaces)
        {
            var type = @interface.GetGenericArguments()[0];
            var method = @interface.GetMethod("ProcessParameters");

            if (type.IsAssignableFrom(modelParams.GetType()))
            {
                // If we found one, invoke the method to run ProcessParameters(modelParams)
                method.Invoke(viewModel, new object[] { modelParams });
            }
        }

        // Publish an aggregator event to let the shell/other VMs know to change their active view
        _aggregator.Publish(new NavigationEventMessage(viewModel));
    }
}

现在外壳可以只处理聚合信息并激活新注入的,另外配置的VM

Now the shell can just handle the aggregator message and activate the new injected and additionally configured VM

public class ShellViewModel : Conductor<IScreen>, IHandle<NavigationEventMessage>
{
    private IEventAggregator _aggregator;
    private INavigationService _navigationService;

    public ShellViewModel(IEventAggregator aggregator, INavigationService _navigationService)
    {
        _aggregator = aggregator;
        _aggregator.Subscribe(this);

        _navigationService.Navigate(typeof (OneSubViewModel), null);
    }

    public void Handle(NavigationEventMessage message)
    {
        ActivateItem(message.ViewModel);
    }
}



其实我约束导航只是IScreen实现,所以我NavigationEventMessage实际上看起来是这样的:

Actually I constrain the navigation to just IScreen implementations so my NavigationEventMessage actually looks like this:

public class NavigationEventMessage
{
    public IScreen ViewModel { get; private set; }

    public NavigationEventMessage(IScreen viewModel)
    {
        ViewModel = viewModel;
    }
}

这是因为我一直想生命周期为我的孩子的ViewModels

This is because I always want lifecycle for my child viewmodels

这篇关于导航与微卡利的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆