首先感谢 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
{
}
}