我想尽可能地遵循MVVM模式,但我不知道我的导航是否做得很好。请注意,我使用的是MasterDetail页面,并且我希望维护Masterpage,在导航时只更改Detail侧。
以下是我在ViewModel中导航的方式。在本例中,从ViewModelOne到ViewModelTwo:
public class ViewModelOne : ViewModelBase
{
private void GoToViewTwo()
{
var viewTwo = new ViewTwo(new ViewModelTwo());
((MasterView)Application.Current.MainPage).NavigateToPage(viewTwo);
}
}
MasterView实现:
public class MasterView : MasterDetailPage
{
public void NavigateToPage(Page page)
{
Detail = new NavigationPage(page);
IsPresented = false;
}
}
ViewTwo实现:
public partial class ViewTwo : PageBase
{
public MenuView(ViewModelTwo vm)
: base(vm)
{
InitializeComponent();
}
}
PageBase实现:
public class PageBase : ContentPage
{
public PageBase(ViewModelBase vmb)
{
this.BindingContext = vmb;
}
}
这是进行导航的最佳方法(和最佳性能(吗?当我做一些导航时,应用程序开始运行得更慢,也许是我做得不好。
这是导航显示MasterDetail页面的最佳方法吗?
谢谢。
我认为你肯定走在了正确的轨道上,但这里有一些问题:
首先,您不应该在视图模型中实例化视图。一旦您的视图模型意识到视图,那么您就基本上打破了这种模式。
var viewTwo = new ViewTwo(new ViewModelTwo());
您的视图创建应由主视图负责。事实上,您甚至不需要担心创建视图,因为您可以使用DataTemplate
。我稍后再解释。
首先,我们需要将View Models与Views 为了保持通用性,视图模型需要某种基础 当然,这是一个非常简单的基本视图模型,您可以稍后对此进行扩展,其主要原因是允许您创建一个主视图模型,该视图模型将负责显示您当前的页面。以下是主视图模型的一个简单示例: 请注意, 现在 好的,现在谈好东西。 您的详细信息与其父级有关系,因此,可以说管理它是父级的责任。在这种情况下,您的主详细信息视图模型看起来像这样: 所以,现在我们有了某种视图模型结构,我敢打赌你会问"视图呢?",这就是 在WPF中,可以将视图分配给 好的,太好了!,现在,要使Master View实际显示当前页面的视图,只需创建一个 为了进一步扩展,不仅 注意:使用布尔值到可见性转换器来确定是否显示子内容 使用这种方法,您不必担心实例化任何视图,因为 呜呜!这是一个很大的收获。 要从中吸取的要点是: 最后要注意的是,正如我刚才提到的,实现这一点的方法肯定不止一种,某种视图和视图模型管理器负责创建/删除视图和视图模式。class
或interface
,稍后您就会明白原因。让我们从一个简单的例子开始:public abstract class ViewModel : INotifyPropertyChanged
{
public event EventHandler OnClosed;
public event EventHandler OnOpened;
//Don't forget to implement INotifyPropertyChanged.
public bool IsDisplayed { get; private set; }
public void Open()
{
IsDisplayed = true;
//TODO: Raise the OnOpened event (Might be a better idea to put it in the IsDisplayed getter.
}
public void Close()
{
IsDisplayed = false;
//TODO: Raise the OnClosed event.
}
}
public class MasterViewModel : INotifyPropertyChanged
{
//Don't forget to implement INotifyPropertyChanged.
public ViewModel CurrentPage { get; private set; }
public MasterViewModel()
{
//This is just an example of how to set the current page.
//You might want to use a command instead.
CurrentPage = new MovieViewModel();
}
//TODO: Some other master view model functionality, like exiting the application.
}
INotifyPropertyChanged
在某种基类中可能会更好,而不必一遍又一遍地重新实现相同的代码MasterViewModel
非常简单,它只保存当前页面,但是拥有master的目的是允许执行应用程序级代码,就像关闭应用程序一样,这样你就可以将此逻辑与其他视图模型隔离开来。public class MovieViewModel : ViewModel
{
protected PickGenreViewModel ChildViewModel { get; private set; }
public MovieViewModel()
{
ChildViewModel = new PickGenreViewModel();
//TODO: Perhaps subscribe to the closed event?
}
//Just an example but an important thing to note is that
//this method is protected because it's the MovieViewModel's
//responsibility to manage it's child view model.
protected void PickAGenre()
{
ChildViewModel.Open();
}
//TODO: Other view model functionality.
}
DataTemplate
的作用所在Type
,例如,您可以在XAML中将MovieView
分配给MovieViewModel
,如下所示:xmlns:Views="clr-namespace:YourNamespace.Views"
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels"
...
<DataTemplate DataType="{x:Type ViewModels:MovieViewModel}">
<Views:MovieView/>
</DataTemplate>
ContentPresenter
,并将其Content
绑定到CurrentPage
。您的主视图将如下所示:<Window
...
xmlns:ViewModels="clr-namespace:YourNamespace.ViewModels">
<Window.DataContext>
<ViewModels:MasterViewModel/>
</Window.DataContext>
<Grid>
<ContentPresenter Content="{Binding CurrentPage}"/>
</Grid>
MasterView
需要为其子级包含一个ContentPresenter
,MovieView
也需要为其子级PickGenreViewModel
包含一个。你可以再次使用相同的方法:<Grid>
<!-- The main view code for the movie view -->
...
<Border Visibility="{Binding ChildViewModel.IsDisplayed, Converter=...">
<ContentPresenter Content="{Binding ChildViewModel}"/>
</Border>
</Grid>
DataTemplate
和ContentPresenter
为您处理了这一问题,您只需要将视图模型映射到适当的视图。