1

首先感谢 Charleh 的 caliburn.micro 导航解决方案。如果导航其他页面,如何激活 SubMenu 导航到 Page2 和 Deactive?

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

    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

<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

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

子菜单视图.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>

子菜单视图模型.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
    {
    }
}
4

1 回答 1

1

不是对实际问题的真正答案,而是更多警告:您对 IoC 容器以及如何使用它还有一些其他问题

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

导航服务应由容器解析,因此您的视图模型构造函数应包含一个INavigationService参数

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

private INavigationService _navigationService;

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

...和用法:

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

这是因为容器会自动将INavigationService实现注入到您的虚拟机中。您不需要对IEventAggregator实现的引用(除非您的 VM 依赖于它,但它似乎并不依赖)并且您不应该手动实例化NavigationService实例,因为这是容器的工作

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

编辑:

好的,这就是我能告诉你的所有内容,直到我知道你对你发给我的项目做了什么(棱镜或转移到 CM?)

Caliburn.MicroIConductor<T>用作所有可以执行/管理活动屏幕/项目的窗口的基础。这Conductor<T>是此接口的标准实现,通常用于管理IScreen基于混凝土的。

IConductor接口

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

和实施

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

默认指挥管理 1 个屏幕。有几个嵌套类实现了多个屏幕 -Conductor<T>.Collection.OneActiveConductor<T>.Collection.AllActive 允许其一个或所有项目一次处于活动状态 - (例如,OneActive带有选项卡的 Internet Explorer 窗口的示例和AllActiveVisual Studio 2010 工具窗口的示例)

为了使用此导体实现,您应该理想地使用IScreen(或具体Screen类)

屏幕界面

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 等)

例如

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,因此可以有子导体和孙导体,它们都将遵守生命周期。

我不确定是否有 Prism 等价物

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

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

如果您仍在使用 Prism 而不是 Caliburn.Micro 并且没有 Prism 等效项,那么至少 Caliburn.Micro 实现将为您提供一些指导

于 2013-04-17T10:58:10.667 回答