在WPF MVVM中处理ContentControls之间导航的更好方法?

时间:2015-05-11 16:53:10

标签: c# wpf mvvm navigation datatemplate

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

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

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

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

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列出了我的应用程序中的所有页面(视图),以及将更改{{ChangePage()的实际CurrentViewModel函数我的MainViewModel中的1}}属性:

// 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>

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

1 个答案:

答案 0 :(得分:0)

在查看@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; }
}

这样,我添加了对保持我的每个视图的视图模型的状态的支持,并且我通过使用电源为我的应用程序中的每个视图删除了我的enum Generics和反思。

如果我找到其他方法来减少我想要添加到应用程序的每个视图的地方数量,我会更新这个答案。