我更愿意使用
ViewModelFirst导航服务。
在我看来,这更容易使用,并且在创建新的View / ViewModel时需要添加的代码要少得多。
为此,您需要一些东西:
首先是NavigableViewModel抽象类,其中包含一些方法来处理双向导航。您的所有viewModels都将继承自此类:
NavigableViewModel.cs
public abstract class NavigableViewModel : ViewModelBase
{
public abstract void OnNavigatedTo(object parameter = null);
public abstract void OnNavigatingTo(object parameter = null);
}
一个包含导航帧的主窗口,只需使用
NavigationUIVisibility="Hidden"隐藏默认的导航控件:
MainWindow.xaml
<Window x:Class="YourProject.Views.MainWindow"
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:local="clr-namespace:SS3DViewModelFirstMvvmLightProject"
mc:Ignorable="d"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="MainWindow" Height="350" Width="525">
<-- Just remeber to replace x:Class="YourProject.Views.MainWindow" with your actual project path-->
<Frame x:Name="Frame" NavigationUIVisibility="Hidden">
</Frame>
</Window>
一些处理ViewModel更改的代码(允许我们通知每个页面其viewModel):
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
((MainViewModel)this.DataContext).ShowFirstView();
Frame.LoadCompleted += (s, e) => UpdateFrameDataContext();
Frame.DataContextChanged += (s, e) => UpdateFrameDataContext();
}
private void UpdateFrameDataContext()
{
Page view = (Page)Frame.Content;
if (view != null)
{
view.DataContext = Frame.DataContext;
}
}
}
在你的MainViewModel中,有一个小方法用于导航到你的第一个ViewModel(这里是LoginViewModel):
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
public void ShowFirstView()
{
ServiceLocator.Current.GetInstance<ViewModelFirstNavigationService>().NavigateTo<LoginViewModel>();
}
}
为了使这个 ServiceLocator 调用起作用,我们需要在 ViewModelLocator 中添加一些东西:
ViewModelLocator.cs
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
ViewModelFirstNavigationService navService = new ViewModelFirstNavigationService(Main);
SimpleIoc.Default.Register<LoginViewModel>();
navService.AddNavigableElement(SimpleIoc.Default.GetInstance<LoginViewModel>);
SimpleIoc.Default.Register<ViewModelFirstNavigationService>(() => navService);
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
}
}
现在,您已经准备好了所有的东西,让我们添加系统的核心——导航服务(这是棘手的部分):
ViewModelFirstNavigationService
public class ViewModelFirstNavigationService
{
private Dictionary<Type, Uri> _registeredViews;
private Dictionary<Type, Func<NavigableViewModel>> _registeredViewModels;
private List<string> _allXamlPages;
private MainViewModel _mainContainerViewModel;
public NavigableViewModel CurrentViewModel;
public ViewModelFirstNavigationService(MainViewModel mainContainerViewModel)
{
_mainContainerViewModel = mainContainerViewModel;
_registeredViews = new Dictionary<Type, Uri>();
_registeredViewModels = new Dictionary<Type, Func<NavigableViewModel>>();
_allXamlPages = GetAllXamlPages();
}
private List<string> GetAllXamlPages()
{
System.Reflection.Assembly viewModelFirstProjectAssembly;
viewModelFirstProjectAssembly = System.Reflection.Assembly.GetExecutingAssembly();
var stream = viewModelFirstProjectAssembly.GetManifestResourceStream(viewModelFirstProjectAssembly.GetName().Name + ".g.resources");
var resourceReader = new ResourceReader(stream);
List<string> pages = new List<string>();
foreach (DictionaryEntry resource in resourceReader)
{
Console.WriteLine(resource.Key);
string s = resource.Key.ToString();
if (s.Contains("page.baml"))
{
pages.Add(s.Remove(s.IndexOf(".baml")));
}
}
return pages;
}
private Type ResolveViewModelTypeFromSingletonGetterFunc<T>(Func<T> viewModelSingletonGetterFunc)
{
MethodInfo methodInfo = viewModelSingletonGetterFunc.Method;
return methodInfo.ReturnParameter.ParameterType;
}
private Uri ResolvePageUriFromViewModelType(Type viewModelType)
{
string pageName = String.Empty;
int index = viewModelType.Name.IndexOf("ViewModel");
pageName = viewModelType.Name.Remove(index);
string pagePath = String.Format("{0}.xaml", _allXamlPages.Where(page => page.Contains(pageName.ToLower())).FirstOrDefault());
string cleanedPath = pagePath.Remove(0, "views/".Length);
return new Uri(cleanedPath, UriKind.Relative);
}
public void AddNavigableElement(Func<NavigableViewModel> viewModelSingletonGetter)
{
Type vmType = ResolveViewModelTypeFromSingletonGetterFunc(viewModelSingletonGetter);
Uri uriPage = ResolvePageUriFromViewModelType(vmType);
_registeredViews.Add(vmType, uriPage);
_registeredViewModels.Add(vmType, viewModelSingletonGetter);
}
public void NavigateTo<GenericNavigableViewModelType>(object parameter = null)
{
Type key = typeof(GenericNavigableViewModelType);
NavigateTo(key, parameter);
}
public void NavigateTo(Type key, object parameter = null)
{
CurrentViewModel?.OnNavigatingTo(parameter);
CurrentViewModel = _registeredViewModels[key].Invoke();
Uri uri = _registeredViews[key];
((MainWindow)Application.Current.MainWindow).Frame.Source = uri;
((MainWindow)Application.Current.MainWindow).Frame.DataContext = CurrentViewModel;
CurrentViewModel.OnNavigatedTo(parameter);
}
}
现在,一切都正常工作了!万岁!让我们用我们的示例LoginViewModel来演示(它只包含一个漂亮的黑色正方形中的helloworld):
Login页面.xaml
<Page x:Class="YourProject.Views.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SS3DViewModelFirstMvvmLightProject.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="LoginPage">
<Grid Background="Gray">
<Label Content="{Binding HelloWorld}" Foreground="White" Background="Black" Width="150" Height="150"></Label>
</Grid>
</Page>
其 ViewModel 如下:
LoginViewModel.cs
public class LoginViewModel : NavigableViewModel
{
private string _helloWorld;
public string HelloWorld
{
get
{
return _helloWorld;
}
set
{
_helloWorld = value;
RaisePropertyChanged(() => HelloWorld);
}
}
public LoginViewModel()
{
HelloWorld = "Hello World";
}
public override void OnNavigatedTo(object parameter = null)
{
}
public override void OnNavigatingTo(object parameter = null)
{
}
}
我承认你需要一些代码来开始。但是当一切正常工作时,你就会得到一个非常易于使用的系统。
想要导航到某个viewModel吗?
只需使用我的导航服务.NavigateTo(someParam)。
想要添加新的视图/视图模型对吗?
只需将您的视图模型添加到某个IOC容器(在我的项目中,我使用自己的IOC,它允许我在需要时卸载我的视图模型并提供一些细粒度的导航堆栈),然后将其赋予您的导航服务即可。
new NavigationService()
,这是一个内部构造函数,对我不可用。 - J4N