我正在尝试学习MVVM,但我发现这是一个噩梦,试图了解如何在使用MVVM的应用程序中正确导航视图之间。经过一段时间研究和尝试理解不同的技术后,我遇到了Rachel Lim's blog的方法。此技术将ViewModel用于应用程序本身,并跟踪应用程序状态,例如当前页面。我觉得这对我的申请来说是一个很好的方法。
现在转移到我的问题..
我想要实现的目标
我想要一个具有一个主应用程序视图的应用程序,该视图将LoginView和HomeView存储为DataTemplates,并具有一个内容控件,将LoginView设置为应用程序启动时显示的视图。 LoginView将有一个按钮,按下该按钮将打开另一个有按钮的窗口。当按下弹出窗口中的按钮时,我想将主应用程序窗口中的视图从LoginView更改为HomeView。
到目前为止我有什么
我有一个设置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>
并按如下方式设置了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
}
}
当我运行应用程序时,它将显示我的主应用程序窗口,该窗口显示loginView,它是一个UserControl,并被设置为带有ContentPresenter的currentPageViewModel。
单击LoginView UserControl中的按钮时,将打开另一个窗口。如下图所示。
这是该窗口的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>
我的问题
我想要实现的是当单击第二个视图窗口中的“确定”按钮时,我想从LoginView更改ApplicationView窗口中的currentPageViewModel以显示HomeView,但我对如何实现此目标感到困惑。任何帮助将不胜感激。
答案 0 :(得分:0)
我看到你已经在使用MVVMLight了。有一个Messenger课程可以帮助你。在ApplicationViewModel构造函数中注册到messenger,在处理按钮的代码中单击CodeViewModel,使用Send发送消息。在传递给注册的操作中,根据需要更改视图模型。
参见http://www.mvvmlight.net/help/WP8/html/9fb9c53a-943a-11d7-9517-c550440c3664.htm 和Use MVVM Light's Messenger to Pass Values Between View Model
我没有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;
}
}
用过:
VMMessenger.Register("ChangeViewModel",this,ChangeViewModelAction)
和
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);
}
}
}
}
用过:
VMMessenger.SendMessage("ChangeViewModel","HomeViewModel",this);
并在ChangeViewModelAction
中,您可以检查ViewModel名称并将CurrentPageViewModel更改为具有匹配名称的名称。