处理 WPF MVVM 中内容控件之间导航的更好方法?



我目前正在开发一个C# WPF应用程序,并试图遵循MVVM设计模式。

它现在的工作方式是,我在主窗口中使用ContentControl并将其绑定到CurrentViewModel,并在App.xaml我的数据模板中声明。当我想更改主窗口中的当前视图时,我所要做的就是更改主窗口视图模型中的CurrentViewModel属性,效果很好。此外,为了不直接引用视图模型(通过在视图模型中执行new blablaViewModel()),我有一个在ICommand函数中调用的单例FlowManager类,并且实例化在该类而不是视图模型中完成。

这种方法的问题在于,对于我添加到应用程序中的每个视图,我必须在App.xaml中添加一个数据模板,在我的FlowManager类中添加一个enum条目,在我的switch()ChangePage()函数中添加一个新case,在我的MainViewModel中添加一个新的ICommand,除了为实际视图添加代码并创建自己的视图模型之外

。以下是我如何处理应用程序流的示例:

MainWindow.xaml中,我有以下布局:

<Window x:Class="EveExcelMineralUpdater.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:EveExcelMineralUpdater.ViewModels"
Title="MainWindow" Height="720" Width="1280">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="85*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button>MarketStat Request</Button>
<Button Command="{Binding ChangeToQuickLookCommand}">QuickLook Request</Button>
<Button>History Request</Button>
<Button>Route Request</Button>
<Button>Settings</Button>
</StackPanel>
<Separator Grid.Column="1" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<ContentControl Grid.Column="2" Content="{Binding CurrentViewModel}" />
</Grid>
</Window>

App.xaml 中.cs我通过创建主窗口并设置其DataContextMainViewModel属性来启动应用程序:

MainWindow mainWindow = new MainWindow();
MainViewModel mainViewModel = new MainViewModel();
mainWindow.DataContext = mainViewModel;
mainWindow.ViewModel = mainViewModel;
FlowManager.Instance.AppWindow = mainWindow;
mainWindow.Show();

MainViewModel.cs中,我处理一个按钮请求,以更改CurrentView属性,ICommand如下所示:

private void ChangeToQuickLook(object param)
{
FlowManager.Instance.ChangePage(FlowManager.Pages.QuickLook);
}
...
public ICommand ChangeToQuickLookCommand
{
get { return new RelayCommand(ChangeToQuickLook); }
}

FlowManager.cs中,我有一个enum,它列出了我应用程序中的所有页面(视图),以及将更改MainViewModelCurrentViewModel属性的实际ChangePage()函数:

// Only one view is implemented for now, the rest are empty for now
public void ChangePage(Pages page)
{
IViewModel newViewModel = null;
switch (page)
{
case Pages.MarketStat:
break;
case Pages.QuickLook:
newViewModel = new QuickLookRequestViewModel();
break;
case Pages.History:
break;
case Pages.Route:
break;
case Pages.Settings:
break;
}
AppWindow.ViewModel.CurrentViewModel = newViewModel;
}
...
public enum Pages
{
MarketStat,
QuickLook,
History,
Route,
Settings
}

最后,在App.xaml中,我有所有视图的所有数据模板的列表:

<Application x:Class="EveExcelMineralUpdater.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:EveExcelMineralUpdater.ViewModels"
xmlns:views="clr-namespace:EveExcelMineralUpdater.Views"
Startup="App_OnStartup">
<Application.Resources>
<!-- Pages DataTemplates -->
<DataTemplate DataType="{x:Type viewModels:QuickLookRequestViewModel}">
<views:QuickLookRequestView />
</DataTemplate>
</Application.Resources>
</Application>

就像我说的,这很好用,但我可以看到一些可扩展性问题,因为我必须修改代码的几个部分才能在应用程序中添加视图。有没有更好的方法可以在不使用任何框架的情况下做到这一点?

在查看@WojciechKulik评论后,我在FlowManager.cs类中提出了以下更改:

public class FlowManager
{
private static FlowManager _instance;
private MainWindow _mainWindow;
private ICollection<IViewModel> _viewModels; 
private FlowManager()
{
ViewModels = new List<IViewModel>();
}
public void ChangePage<TViewModel>() where TViewModel : IViewModel, new()
{
// If we are already on the same page as the button click, we don't change anything
if (AppWindow.ViewModel.CurrentViewModel == null || 
AppWindow.ViewModel.CurrentViewModel.GetType() != typeof(TViewModel))
{
foreach (IViewModel viewModel in ViewModels)
{
// If an instance of the viewmodel already exists, we switch to that one
if (viewModel.GetType() == typeof(TViewModel))
{
AppWindow.ViewModel.CurrentViewModel = viewModel;
return;
}
}
// Else, we create a new instance of the viewmodel
TViewModel newViewModel = new TViewModel();
AppWindow.ViewModel.CurrentViewModel = newViewModel;
ViewModels.Add(newViewModel);
}
}
public static FlowManager Instance
{
get
{
if (_instance == null)
{
_instance = new FlowManager();
}
return _instance;
}
}
public MainWindow AppWindow { get; set; }
public ICollection<IViewModel> ViewModels { get; private set; }
}

这样,我添加了对保持每个视图视图模型状态的支持,并且通过使用Generics和反射的强大功能,我摆脱了应用程序中每个视图的条目enum

如果我找到其他方法来减少要添加到应用程序中的每个视图必须修改的位置数量,我将更新此答案。

相关内容

  • 没有找到相关文章

最新更新