带有MainMenu和SubMenu的Caliburn Micro导航 [英] Caliburn Micro Navigation with MainMenu and SubMenu

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

问题描述

首先感谢Charleh的caliburn.micro导航解决方案。
如何激活子菜单如何导航到Page2,如果导航到其他页面则如何去激活?

The first thanks Charleh for caliburn.micro navigation solution. How to Active SubMenu navigate to Page2 and Deactive if navigate other page?

ShellView.xaml

ShellView.xaml

    <Window x:Class="Navigation.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="600" WindowStartupLocation="CenterScreen" Width="800" Height="600">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <!--Header-->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="40"/>
                <RowDefinition Height="40"/>
            </Grid.RowDefinitions>
            <ContentControl Grid.Column="1" Grid.Row="0" x:Name="MainMenuRegion" HorizontalContentAlignment="Stretch" HorizontalAlignment="Right" Margin="0,9,17,0" />
            <ContentControl Grid.Column="1" Grid.Row="1" x:Name="SubMenuRegion" HorizontalContentAlignment="Stretch" HorizontalAlignment="Right" Margin="0,0,17,0" />
        </Grid>    

        <!--Content-->
        <ContentControl Grid.Row="2" x:Name="ActiveItem"
                        HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
        </ContentControl>
    </Grid>
</Window>

ShellViewModel.cs

ShellViewModel.cs

    namespace Navigation
    {
        public class ShellViewModel : Conductor<object>.Collection.OneActive, IShellViewModel, IHandle<NavigationEventMessage>
        {
            public ShellViewModel(IEventAggregator eventAggregator, INavigationService navigationService, IPage1ViewModel page1ViewModel, IPage2ViewModel page2ViewModel,IMainMenuViewModel mainMenuViewModel, ISubMenuViewModel subMenuViewModel)
            {
                _eventAggregator = eventAggregator;
                _eventAggregator.Subscribe(this);

                navigationService.Navigate(typeof(IPage1ViewModel), null);

                _page1ViewModel = page1ViewModel;
                _page2ViewModel = page2ViewModel;

                Items.Add(_page1ViewModel);
                Items.Add(_page2ViewModel);
                ActiveItem = _page1ViewModel;            
            }

            private readonly IEventAggregator _eventAggregator;
            private readonly IPage1ViewModel _page1ViewModel;
            private readonly IPage2ViewModel _paage2ViewModel;

            public IMainMenuViewModel MainMenuRegion { get; set; }
            public ISubMenuViewModel SubMenuRegion { get; set; }         

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

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

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

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

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 as IScreen));
    }
}

// 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);
}

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(Component.For<IShellViewModel>().ImplementedBy<ShellViewModel>().LifestyleSingleton());

        container.Register(Component.For<IPage1ViewModel>().ImplementedBy<Page1ViewModel>().LifestyleSingleton());

        container.Register(Component.For<IPage2ViewModel>().ImplementedBy<Page2ViewModel>().LifestyleSingleton());

        container.Register(Component.For<IMainMenuViewModel>().ImplementedBy<MainMenuViewModel>().LifestyleSingleton());

        container.Register(Component.For<ISubMenuViewModel>().ImplementedBy<SubMenuViewModel>().LifestyleSingleton());

    }
}

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

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>());
    }
}

MainMenuView.xaml

MainMenuView.xaml

<UserControl x:Class="Navigation.Views.MainMenuView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
             xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
             mc:Ignorable="d">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
            <RadioButton IsChecked="True" Content="Page1" cal:Message.Attach="[Event Checked]=[Action Page1Checked]"/>
            <RadioButton Content="Page2" cal:Message.Attach="[Event Checked]=[Action Page2Checked]"/>
        </StackPanel>    
    </Grid>

</UserControl>

MainMenuViewModel.cs

MainMenuViewModel.cs

namespace Navigation.ViewModels
{
    public class MainMenuViewModel : Conductor<object>.Collection.OneActive, IMainMenuViewModel
    {
        private readonly IEventAggregator _eventAggregator;
        private bool _isAssemblyManagementModule;

        public MainMenuViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator; 
        }      

        public void Page1Checked()
        {
            NavigationService navigationService = new NavigationService(_eventAggregator);
            navigationService.Navigate(typeof(IPage1ViewModel), null);

        }

        public void Page2Checked()
        {    
            NavigationService navigationService = new NavigationService(_eventAggregator);
            navigationService.Navigate(typeof(IPage2ViewModel), null);

        }
    }

    public interface IMainMenuViewModel
    {
    }
}

SubMenuView.xaml

SubMenuView.xaml

<UserControl x:Class="Navigation.Views.SubMenuView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   
             xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
             mc:Ignorable="d">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Top">
            <RadioButton IsChecked="True" Content="SubPage1" cal:Message.Attach="[Event Checked]=[Action SubPage1Checked]"/>
            <RadioButton Content="SubPage2" cal:Message.Attach="[Event Checked]=[Action SubPage2Checked]"/>
        </StackPanel>    
    </Grid>     
</UserControl>

SubMenuViewModel.cs

SubMenuViewModel.cs

namespace Navigation.ViewModels
{
    public class SubMenuViewModel : Conductor<object>.Collection.OneActive, ISubMenuViewModel
    {
        private readonly IEventAggregator _eventAggregator;
        private bool _isAssemblyManagementModule;

        public SubMenuViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator; 
        }      

        public void SubPage1Checked()
        {
            NavigationService navigationService = new NavigationService(_eventAggregator);
            navigationService.Navigate(typeof(ISubPage1ViewModel), null);

        }

        public void SubPage2Checked()
        {    
            NavigationService navigationService = new NavigationService(_eventAggregator);
            navigationService.Navigate(typeof(ISubPage2ViewModel), null);

        }
    }

    public interface ISubMenuViewModel
    {
    }
}


推荐答案

不是实际问题的答案,而是更多警告:您的IoC还有其他问题容器及其使用方式

Not really an answer to the actual question but more a warning: You have some other issues with the IoC container and how you are using it

您正在为视图模型中的每个命令创建一个新的导航服务实例...这不遵循IoC或服务方法。

You are creating a new navigation service instance for each command in your viewmodels...that's not following the IoC or service methodology.

导航服务应由容器解析,因此您的viewmodel构造函数应包含 INavigationService 参数

The navigation service should be resolved by the container so your viewmodel constructor should contain an INavigationService parameter

例如您的 MainMenuViewModel 的构造函数应如下所示:

e.g. your constructor for MainMenuViewModel should look like this:

private INavigationService _navigationService;

public MainMenuViewModel(INavigationService navigationService)
{
    _navigationService = navigationService;
}      

...和用法:

public void Page1Checked()
{
    _navigationService.Navigate(typeof(IPage1ViewModel), null);
}

这是因为容器将自动注入 INavigationService 实施到您的VM中。您不需要引用 IEventAggregator 的实现(除非您的VM依赖于它,而且看起来好像不是),并且您不应该手动实例化 NavigationService 实例,因为这是容器的工作

This is because the container will automatically inject the INavigationService implementation into your VM. You don't need a reference to IEventAggregator implementation (unless your VM is dependent on it, which it doesn't appear to be) and you should not be manually instantiating the NavigationService instance since this is the job of the container

这是您第一次使用IoC还是MVVM?您是否可以发布有关您正在经历和期望的更多信息(可能带有屏幕截图)?

Is this your first time using IoC or MVVM? Can you post some more information (maybe with screenshots) on what you are experiencing and what you expect?

编辑:

好吧,这就是我能给你的,直到我知道你对我发送给我的项目正在做什么(棱镜还是搬到CM?)

Ok this is all I can give you until I know what you are doing with the project you sent me (prism or move to CM?)

Caliburn.Micro将 IConductor< T> 用作可能执行/管理活动屏幕/项目的所有窗口的基础。 Conductor< T> 是此接口的标准实现,通常用于管理 IScreen 的浇筑物。

Caliburn.Micro uses IConductor<T> as the base for all windows which may conduct/manage an active screen/item. The Conductor<T> is the standard implementation of this interface and is usually used to manage IScreen based concretions.

IConductor接口

IConductor interface

http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffbonc.Sca

和实现

http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c654c654c0c .Silverlight / Conductor.cs

默认导体管理1个屏幕。有几个实现多个屏幕的嵌套类- Conductor .Collection.OneActive Conductor .Collection.AllActive 允许一次激活其一个或所有项目-(例如 OneActive 的示例是带有选项卡的Internet Explorer窗口,以及 AllActive 是Visual Studio 2010工具窗口)

The default conductor manages 1 screen. There are a couple of nested classes which implement multiple screens - Conductor<T>.Collection.OneActive and Conductor<T>.Collection.AllActive allowing either one or all of its items to be active at a time - (e.g. an example of OneActive is the Internet Explorer window with tabs and an example of AllActive is Visual Studio 2010 tool windows)

要使用此导体实现,最好使用 IScreen (或具体的 Screen 类)

In order to work with this conductor implementation you should ideally use IScreen (or the concrete Screen class)

IScreen接口

http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/IScreen.cs

和实施

http://caliburnmicro.codeplex.com/SourceControl /changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08#src/Caliburn.Micro.Silverlight/Screen.cs

基本上,通过这些实现,如果停用了主窗口,很容易将停用消息传播给所有孩子及其孩子,依此类推。这也意味着屏幕会收到有关已被激活的通知(例如OnActivate等)

Basically, with these implementations, if the main window is deactivated, it is easy to bubble the 'deactivate' message down to all the children and their children and so on. It also means that a screen gets a notification that it has been activated (OnActivate etc)

例如

class ParentWindow : Conductor<IScreen>
{

    void DoSomething() 
    {
        ActivateItem(ItemToActivate); // Previous item is automatically deactivated etc
    }

    override OnActivate() 
    {
        // Activate children or whatever

    }
}

class SomeChildWindow : Screen
{
}

重要的是要注意, Conductor< T> 的子类是 Screen ,因此可以有子导体和

It's important to note that Conductor<T> subclasses Screen so it's possible to have child conductors and grandchild conductors that will all obey lifecycle.

我不确定是否有棱镜等价物

I'm not sure if there are Prism equivalents

有一些有关屏幕和生命周期的Caliburn Micro文档:

There is some good Caliburn Micro documentation regarding screens and lifecycle:

http://caliburnmicro.codeplex.com/wikipage?title=Screens%2c%20Conductors%20and%20Composition&referringTitle=Documentation

如果您仍在使用Prism而不是Caliburn.Micro,那么我没有棱镜,那么至少是Caliburn.Micro实现会为您提供一些指导

If you are still using Prism and not Caliburn.Micro and there is no Prism equivalent, then at least the Caliburn.Micro implementations will give you some guidance

这篇关于带有MainMenu和SubMenu的Caliburn Micro导航的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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