MVVM Light noteの最新リリース では、MVVM Lightが「ナビゲーションサービス」を提供することが示されています。
しかし、私と私の友人であるグーグルは、それを使用する方法を見つけることができません。
ServiceLocatorにINavigationService
を要求できることがわかりますので、別のページに移動するように要求する方法はわかりますが、
INavigationService
に与えられるパラメーターの形式は何ですかこのライブラリの公式ドキュメントはありますか?現在、うまくコード化されており、正常に動作していることがわかりますが、使用方法を検索するときに、方法を示すドキュメント/サンプルが見つかりません。彼のブログにはいくつかのエントリがあります。これは非常にイライラします。私が見つけた唯一のドキュメントは this です、私はPluralsightにあまり精通していませんが、毎月のサブスクリプションを取ることが必須のようです(個人として、私のアプリケーションを作成しようとしています)自由時間、できません)。
はい、MvvmLight
はその最後のバージョン でNavigationService
を導入しましたが、Wpf
(WP、Metroapps、..でImplemented NavigationService
を使用できます)に関する実装は提供しませんでしたが、残念ながらWpf
、あなたは自分でそれを実装する必要があります、ここで私は現在それをどのようにやっているのですか( credit )
firstMvvmLight
INavigationService
を実装するナビゲーションインターフェイスを作成します
_public interface IFrameNavigationService:INavigationService
{
object Parameter { get; }
}
_
Parameter
は、ViewModels
の間でオブジェクトを渡すために使用され、INavigationService
は_GalaSoft.MvvmLight.Views
_名前空間の一部です
そのようにそのインターフェースを実装します
_class FrameNavigationService : IFrameNavigationService,INotifyPropertyChanged
{
#region Fields
private readonly Dictionary<string, Uri> _pagesByKey;
private readonly List<string> _historic;
private string _currentPageKey;
#endregion
#region Properties
public string CurrentPageKey
{
get
{
return _currentPageKey;
}
private set
{
if (_currentPageKey == value)
{
return;
}
_currentPageKey = value;
OnPropertyChanged("CurrentPageKey");
}
}
public object Parameter { get; private set; }
#endregion
#region Ctors and Methods
public FrameNavigationService()
{
_pagesByKey = new Dictionary<string, Uri>();
_historic = new List<string>();
}
public void GoBack()
{
if (_historic.Count > 1)
{
_historic.RemoveAt(_historic.Count - 1);
NavigateTo(_historic.Last(), null);
}
}
public void NavigateTo(string pageKey)
{
NavigateTo(pageKey, null);
}
public virtual void NavigateTo(string pageKey, object parameter)
{
lock (_pagesByKey)
{
if (!_pagesByKey.ContainsKey(pageKey))
{
throw new ArgumentException(string.Format("No such page: {0} ", pageKey), "pageKey");
}
var frame = GetDescendantFromName(Application.Current.MainWindow, "MainFrame") as Frame;
if (frame != null)
{
frame.Source = _pagesByKey[pageKey];
}
Parameter = parameter;
_historic.Add(pageKey);
CurrentPageKey = pageKey;
}
}
public void Configure(string key, Uri pageType)
{
lock (_pagesByKey)
{
if (_pagesByKey.ContainsKey(key))
{
_pagesByKey[key] = pageType;
}
else
{
_pagesByKey.Add(key, pageType);
}
}
}
private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count < 1)
{
return null;
}
for (var i = 0; i < count; i++)
{
var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (frameworkElement != null)
{
if (frameworkElement.Name == name)
{
return frameworkElement;
}
frameworkElement = GetDescendantFromName(frameworkElement, name);
if (frameworkElement != null)
{
return frameworkElement;
}
}
}
return null;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
_
上記のコードのMainFrame
は、ページ間を移動するために使用されるFrame
で定義された単純なXaml
コントロールのx:Nameです(ニーズに基づいてカスタマイズします)
Second:viewmodellocator
で、ナビゲーションサービス(SetupNavigation()
)を初期化して、ビューモデルで使用できるようにします。
_static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SetupNavigation();
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<LoginViewModel>();
SimpleIoc.Default.Register<NoteViewModel>();
}
private static void SetupNavigation()
{
var navigationService = new FrameNavigationService();
navigationService.Configure("LoginView", new Uri("../Views/LoginView.xaml",UriKind.Relative));
navigationService.Configure("Notes", new Uri("../Views/NotesView.xaml", UriKind.Relative));
SimpleIoc.Default.Register<IFrameNavigationService>(() => navigationService);
}
_
3番目:最後に、たとえばサービスを使用します
_ public LoginViewModel(IFrameNavigationService navigationService)
{
_navigationService = navigationService;
...
_navigationService.NavigateTo("Notes",data);
..
_
[〜#〜] edit [〜#〜]
明示的なサンプルは、この repo にあります。
私はむしろViewModelFirst Navigation Serviceに行きたいです。
私の考えでは、View/ViewModelの新しいペアを作成するときに使用するコードの方が簡単で、追加するコードの量も少なくなります。
このためには、いくつかのものが必要です:
最初に、両方の方法でナビゲーションを処理するいくつかのメソッドを持つNavigableViewModel抽象クラス。すべてのviewModelはこのクラスから継承します:
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>
そして、ViewModelsの変更を処理するコードビハインド(各ページにviewModelを通知するように許可します):
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
((MainViewModel)this.DataContext).ShowFirstView(); // we need to have our view loaded to start navigating
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>();
//To navigate wherever you want you just need to call this method, replacing LoginViewModel with YourViewModel
}
}
この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>);
// so whenever you want to add a new navigabel View Model just add these lines here
// SimpleIoc.Default.Register<YourViewModel>();
// navService.AddNavigableElement(SimpleIoc.Default.GetInstance<YourViewModel>);
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()
{
// this part is a bit tricky. We use it to find all xaml pages in the current project.
// so you need to be sure that all your pages you want to use with your viewmodles need to end with page.xaml
// Example : LoginPage.xaml will work fine. Parameters.xaml won't.
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); //obviously for this to work you need to have your views in a Views folder at the root of the project. But you are alowed yo reat sub folders in it
return new Uri(cleanedPath, UriKind.Relative);
}
public void AddNavigableElement(Func<NavigableViewModel> viewModelSingletonGetter)
{
//Where the magic happens !
//If your are wondering why a Func, it's because we want our viewmodels to be instantiated only when we need them via IOC.
//First we ge the type of our viewmodel to register for the Func.
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のみが含まれる)を示してみましょう。
LoginPage.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>
そしてそのビューモデル:
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)
{
// whatever you want to happen when you enter this page/viewModel
}
public override void OnNavigatingTo(object parameter = null)
{
// whatever you want to happen when you leave this page/viewmodel
}
}
最初にいくつかのコードが必要であることを認めます。しかし、すべてが機能していると、非常に使いやすいシステムになります。
いくつかのviewModelに移動したいですか? myNavigationService.NavigateTo(someParam);を使用します。
新しいペアのView/ViewModelを追加しますか? viewModelをいくつかのIOCコンテナに追加するだけです(私のプロジェクトでは、独自のIOCを使用しています。必要に応じてビューモデルをアンロードして、優れたナビゲーションスタックを提供できます)。 。
ナビゲーション機能がmvvm lightで利用可能かどうかわかりません。 contentControlバインディングで実装しました:
<xcad:LayoutDocumentPane>
<xcad:LayoutDocument x:Name="DetailDoc" CanClose="False">
<ContentControl Content="{Binding DisplayedDetailViewModel}"/>
</xcad:LayoutDocument>
</xcad:LayoutDocumentPane>
そして、viewmodelプロパティ。 mvvm light ViewModelBaseクラスを継承します。
public ViewModelBase DisplayedDetailViewModel
{
get
{
return displayedDetailViewModel;
}
set
{
if (displayedDetailViewModel == value)
{
return;
}
displayedDetailViewModel = value;
RaisePropertyChanged("DisplayedDetailViewModel");
}
コンテンツコントロールがどのユーザーコントロールを使用する必要があるかを知るには、app.xamlでDataTemplatesを定義します。
<Application.Resources>
<ResourceDictionary>
<!--
We define the data templates here so we can apply them across the
entire application.
The data template just says that if our data type is of a particular
view-model type, then render the appropriate view. The framework
takes care of this dynamically. Note that the DataContext for
the underlying view is already set at this point, so the
view (UserControl), doesn't need to have it's DataContext set
directly.
-->
<DataTemplate DataType="{x:Type viewModel:LoggerViewModel}">
<views:LogView />
</DataTemplate>
LogViewはUserControlです。 LoggerViewModelをDisplayedDetailViewModelに割り当てるだけで、フレームワークが作業を行います。