C# 在WPF MVVM中处理ContentControl之间导航的更好方法?
我目前正在开发一个C#WPF应用程序,并试图遵循MVVM设计模式 它现在的工作方式是,我在主窗口中使用C# 在WPF MVVM中处理ContentControl之间导航的更好方法?,c#,wpf,mvvm,navigation,datatemplate,C#,Wpf,Mvvm,Navigation,Datatemplate,我目前正在开发一个C#WPF应用程序,并试图遵循MVVM设计模式 它现在的工作方式是,我在主窗口中使用ContentControl,并将其绑定到CurrentViewModel,并在App.xaml我的数据模板中声明。当我想在主窗口中更改当前视图时,我所要做的就是在主窗口的视图模型中更改CurrentViewModel属性,这很有效。另外,为了不直接引用视图模型(通过在视图模型中执行new blablaViewModel()),我有一个singletonFlowManager类,我在IComma
ContentControl
,并将其绑定到CurrentViewModel
,并在App.xaml
我的数据模板中声明。当我想在主窗口中更改当前视图时,我所要做的就是在主窗口的视图模型中更改CurrentViewModel
属性,这很有效。另外,为了不直接引用视图模型(通过在视图模型中执行new blablaViewModel()
),我有一个singletonFlowManager
类,我在ICommand
函数中调用该类,并在该类而不是视图模型中进行实例化
这种方法的问题是,对于我添加到应用程序中的每个视图,我都必须在App.xaml
中添加一个数据模板,在我的FlowManager
类中添加一个enum
条目,在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>
在MainViewModel.cs中,我处理一个按钮请求,用ICommand
更改CurrentView
属性,如下所示:
private void ChangeToQuickLook(object param)
{
FlowManager.Instance.ChangePage(FlowManager.Pages.QuickLook);
}
...
public ICommand ChangeToQuickLookCommand
{
get { return new RelayCommand(ChangeToQuickLook); }
}
在FlowManager.cs中,我有一个列出应用程序中所有页面(视图)的enum,以及实际的ChangePage()
函数,该函数将更改我的MainViewModel
中的CurrentViewModel
属性:
// 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; }
}
公共类FlowManager
{
私有静态FlowManager_实例;
专用主窗口MainWindow;
私有ICollection\u视图模型;
私人FlowManager()
{
ViewModels=新列表();
}
public void ChangePage(),其中TViewModel:IViewModel,new()
{
//如果我们已经在按钮单击的同一页面上,我们不会更改任何内容
如果(AppWindow.ViewModel.CurrentViewModel==null | |
AppWindow.ViewModel.CurrentViewModel.GetType()!=typeof(TViewModel))
{
foreach(ViewModels中的IViewModel viewModel)
{
//如果viewmodel的实例已经存在,我们将切换到该实例
if(viewModel.GetType()==typeof(TViewModel))
{
AppWindow.ViewModel.CurrentViewModel=ViewModel;
返回;
}
}
//否则,我们将创建viewmodel的新实例
TViewModel newViewModel=新TViewModel();
AppWindow.ViewModel.CurrentViewModel=newViewModel;
添加(newViewModel);
}
}
公共静态FlowManager实例
{
得到
{
if(_instance==null)
{
_实例=新的FlowManager();
}
返回_实例;
}
}
公共主窗口AppWindow{get;set;}
公共ICollection视图模型{get;private set;}
}
通过这种方式,我添加了对保持每个视图的viewmodel状态的支持,并且通过使用泛型和反射的功能,为应用程序中的每个视图添加了一个条目,从而摆脱了enum
如果我找到其他方法来减少我要添加到应用程序中的每个视图的修改位置,我将更新此答案。您可以查看我的导航源代码:@WojciechKulik谢谢您的建议,我会研究它!
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; }
}