首先感谢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
{
}
}
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
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
{
}
}
答案 0 :(得分:1)
实际问题不是真正的答案,而是更多警告:IoC容器存在其他一些问题以及您如何使用它
您正在为视图模型中的每个命令创建一个新的导航服务实例...它不遵循IoC或服务方法。
导航服务应由容器解析,以便您的viewmodel构造函数应包含INavigationService
参数
e.g。 MainMenuViewModel
的构造函数应如下所示:
private INavigationService _navigationService;
public MainMenuViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
......和用法:
public void Page1Checked()
{
_navigationService.Navigate(typeof(IPage1ViewModel), null);
}
这是因为容器会自动将INavigationService
实现注入您的VM。您不需要引用IEventAggregator
实现(除非您的VM依赖于它,它似乎不是),您不应该手动实例化NavigationService
实例,因为这是容器的工作
这是您第一次使用IoC还是MVVM?你能发布一些关于你正在经历的和你期望的更多信息(可能有截图)吗?
编辑:
好的,这是我能给你的全部,直到我知道你发送给我的项目你做了什么(棱镜或转移到CM?)
Caliburn.Micro使用IConductor<T>
作为可以进行/管理活动屏幕/项目的所有窗口的基础。 Conductor<T>
是此界面的标准实现,通常用于管理基于IScreen
的具体结果。
IConductor接口
和实施
默认导体管理1个屏幕。有几个嵌套类实现多个屏幕 - Conductor<T>.Collection.OneActive
和Conductor<T>.Collection.AllActive
允许其中一个或所有项目一次处于活动状态 - (例如OneActive
的示例是带有选项卡的Internet Explorer窗口和AllActive
的示例是Visual Studio 2010工具窗口)
为了使用此导体实现,理想情况下应使用IScreen
(或具体的Screen
类)
IScreen界面
和实施
基本上,通过这些实现,如果主窗口被停用,很容易将“停用”消息传递给所有孩子及其子女等等。这也意味着屏幕会收到一条已激活的通知(OnActivate等)
e.g。
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文档很好:
如果您仍在使用Prism而不是Caliburn.Micro并且没有Prism等效,那么至少Caliburn.Micro实现将为您提供一些指导