I'm trying to learn MVVM but am finding it a nightmare trying to understand how to correctly navigate between views in an application using MVVM. After some time researching and trying to understand different techniques I have come across an approach from Rachel Lim's blog. This technique uses a ViewModel for the application itself and keeps track of the application state such as the current page. I feel this would be a nice approach to follow for my application.
我正在尝试学习MVVM,但我发现这是一个噩梦,试图理解如何在使用MVVM的应用程序中正确导航视图之间。经过一段时间的研究和尝试理解不同的技术后,我遇到了Rachel Lim博客的方法。此技术将ViewModel用于应用程序本身,并跟踪应用程序状态,例如当前页面。我觉得这对我的应用程序来说是一个很好的方法。
Now moving onto my problem..
现在转向我的问题..
What I want to achieve
我想要实现的目标
I want an application that has a one main application view that will store a LoginView and a HomeView as DataTemplates and have a content control that sets the LoginView as the view displayed when the application is started. The LoginView will have a button that when pressed will open another window that has a button. When the button in the pop up window is pressed I want to change the view in the main application window from LoginView to the HomeView.
我想要一个具有一个主应用程序视图的应用程序,该视图将LoginView和HomeView存储为DataTemplates,并具有一个内容控件,将LoginView设置为应用程序启动时显示的视图。 LoginView将有一个按钮,按下该按钮将打开另一个有按钮的窗口。当按下弹出窗口中的按钮时,我想将主应用程序窗口中的视图从LoginView更改为HomeView。
What I have so far
到目前为止我有什么
I have a set up the ApplicationView which works fine.
我有一个设置ApplicationView的工作正常。
<Window x:Class="WPF_Navigation_Practice.Views.ApplicationView"
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:ignore="http://www.galasoft.ch/ignore"
xmlns:vm="clr-namespace:WPF_Navigation_Practice.ViewModels"
xmlns:views="clr-namespace:WPF_Navigation_Practice.Views"
mc:Ignorable="d ignore"
DataContext="{StaticResource ApplicationViewModel}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:LoginViewModel}">
<views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</Grid>
</Window>
And have set up the ApplicationViewModel as follows. Setting the current page to the LoginViewModel.
并按如下方式设置了ApplicationViewModel。将当前页面设置为LoginViewModel。
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using WPF_Navigation_Practice.Interfaces;
namespace WPF_Navigation_Practice.ViewModels
{
/// <summary>
/// This class contains properties that a View can data bind to.
/// <para>
/// See http://www.galasoft.ch/mvvm
/// </para>
/// </summary>
public class ApplicationViewModel : ViewModelBase
{
#region Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
public ApplicationViewModel()
{
// Add available pages
PageViewModels.Add(new LoginViewModel());
PageViewModels.Add(new HomeViewModel());
PageViewModels.Add(new CodeViewModel());
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand<object>(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _changePageCommand;
}
}
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
_pageViewModels = new List<IPageViewModel>();
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _currentPageViewModel;
}
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
RaisePropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels
.FirstOrDefault(vm => vm == viewModel);
}
#endregion
}
}
When I run the application it will display my main Application window which displays the loginView which is a UserControl and is set as the currentPageViewModel with ContentPresenter.
当我运行应用程序时,它将显示我的主应用程序窗口,该窗口显示loginView,它是UserControl,并被设置为带有ContentPresenter的currentPageViewModel。
When the button in the LoginView UserControl is clicked it will open another window. As per the image below.
当单击LoginView UserControl中的按钮时,它将打开另一个窗口。如下图所示。
Here is the XAML for that window.
这是该窗口的XAML。
<Window x:Class="WPF_Navigation_Practice.Views.CodeView"
x:Name="CodeWindow"
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:ignore="http://www.galasoft.ch/ignore"
xmlns:z="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:viewModels="clr-namespace:WPF_Navigation_Practice.ViewModels"
mc:Ignorable="d ignore"
d:DesignWidth="623.224" d:DesignHeight="381.269"
DataContext="{Binding CodeViewModel, Source={StaticResource ApplicationViewModel}}">
<Grid>
<Button Content="Ok"
HorizontalAlignment="Left"
Margin="235,166,0,0"
VerticalAlignment="Top"
Width="138"
FontSize="20"
Height="67"/>
<Label Content="Second Window" HorizontalAlignment="Left" Margin="166,56,0,0" VerticalAlignment="Top" FontSize="36"/>
</Grid>
My Problem
我的问题
What I want to achieve is when the 'Ok' button in the secondView window is clicked, I want to change the currentPageViewModel in the ApplicationView Window from the LoginView to display the HomeView but am confused on how I would go about achieving this. Any help would be greatly appreciated.
我想要实现的是当单击第二个视图窗口中的“确定”按钮时,我想从LoginView更改ApplicationView窗口中的currentPageViewModel以显示HomeView,但我对如何实现这一点感到困惑。任何帮助将不胜感激。
1 个解决方案
#1
0
I see that you are already using MVVMLight. There is a Messenger class which can help you here. Register to the messenger in your ApplicationViewModel Constructor and in the code handling the button click in CodeViewModel use Send to send a message. In the action you pass on to register change the viewmodels as you wish.
我看到你已经在使用MVVMLight了。有一个Messenger课程可以帮助你。在ApplicationViewModel构造函数中注册到messenger,在处理按钮的代码中单击CodeViewModel,使用Send发送消息。在您传递给注册的操作中,您可以根据需要更改视图模型。
See http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htm and Use MVVM Light's Messenger to Pass Values Between View Model
请参阅http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htm并使用MVVM Light的Messenger在视图模型之间传递值
I don't have MVVMLight to write you a sample code. I've written a ViewModelMessenger from scratch and mine is like this:
我没有MVVMLight给你写一个示例代码。我从头开始编写ViewModelMessenger,我的是这样的:
public static void Register(string actionName, object registerer, Action<object, object> action)
{
var actionKey = new Tuple<string, object>(actionName, registerer);
if (!RegisteredActions.ContainsKey(actionKey))
{
RegisteredActions.Add(actionKey, action);
}
else
{
RegisteredActions[actionKey] = action;
}
}
Used like:
使用如下:
VMMessenger.Register("ChangeViewModel",this,ChangeViewModelAction)
and
和
public static void SendMessage(string messageName, object message, object sender)
{
var actionKeys = RegisteredActions.Keys.ToList();
foreach (Tuple<string, object> actionKey in actionKeys)
{
if (actionKey.Item1 == messageName)
{
Action<object, object> action;
if (RegisteredActions.TryGetValue(actionKey, out action))
{
action?.Invoke(message, sender);
}
}
}
}
Used like:
使用如下:
VMMessenger.SendMessage("ChangeViewModel","HomeViewModel",this);
and in ChangeViewModelAction
you can check for ViewModel names and change the CurrentPageViewModel to one with a matching name.
在ChangeViewModelAction中,您可以检查ViewModel名称并将CurrentPageViewModel更改为具有匹配名称的名称。
#1
0
I see that you are already using MVVMLight. There is a Messenger class which can help you here. Register to the messenger in your ApplicationViewModel Constructor and in the code handling the button click in CodeViewModel use Send to send a message. In the action you pass on to register change the viewmodels as you wish.
我看到你已经在使用MVVMLight了。有一个Messenger课程可以帮助你。在ApplicationViewModel构造函数中注册到messenger,在处理按钮的代码中单击CodeViewModel,使用Send发送消息。在您传递给注册的操作中,您可以根据需要更改视图模型。
See http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htm and Use MVVM Light's Messenger to Pass Values Between View Model
请参阅http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htm并使用MVVM Light的Messenger在视图模型之间传递值
I don't have MVVMLight to write you a sample code. I've written a ViewModelMessenger from scratch and mine is like this:
我没有MVVMLight给你写一个示例代码。我从头开始编写ViewModelMessenger,我的是这样的:
public static void Register(string actionName, object registerer, Action<object, object> action)
{
var actionKey = new Tuple<string, object>(actionName, registerer);
if (!RegisteredActions.ContainsKey(actionKey))
{
RegisteredActions.Add(actionKey, action);
}
else
{
RegisteredActions[actionKey] = action;
}
}
Used like:
使用如下:
VMMessenger.Register("ChangeViewModel",this,ChangeViewModelAction)
and
和
public static void SendMessage(string messageName, object message, object sender)
{
var actionKeys = RegisteredActions.Keys.ToList();
foreach (Tuple<string, object> actionKey in actionKeys)
{
if (actionKey.Item1 == messageName)
{
Action<object, object> action;
if (RegisteredActions.TryGetValue(actionKey, out action))
{
action?.Invoke(message, sender);
}
}
}
}
Used like:
使用如下:
VMMessenger.SendMessage("ChangeViewModel","HomeViewModel",this);
and in ChangeViewModelAction
you can check for ViewModel names and change the CurrentPageViewModel to one with a matching name.
在ChangeViewModelAction中,您可以检查ViewModel名称并将CurrentPageViewModel更改为具有匹配名称的名称。