在MVVM应用程序中的视图之间导航时,如何保留视图的完整状态? [英] How to preserve the full state of the View when navigating between Views in an MVVM application?

查看:75
本文介绍了在MVVM应用程序中的视图之间导航时,如何保留视图的完整状态?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MVVM应用程序,需要在屏幕之间进行基本的向后/向前导航.目前,我已经使用WorkspaceHostViewModel实现了此功能,该功能可跟踪当前工作区并公开必要的导航命令,如下所示.

I have an MVVM application that requires basic backward/forward navigation between screens. Currently, I have implemented this using a WorkspaceHostViewModel that tracks the current workspace and exposes the necessary navigation commands as follows.

public class WorkspaceHostViewModel : ViewModelBase
{
    private WorkspaceViewModel _currentWorkspace;
    public WorkspaceViewModel CurrentWorkspace
    {
        get { return this._currentWorkspace; }
        set
        {
            if (this._currentWorkspace == null
                || !this._currentWorkspace.Equals(value))
            {
                this._currentWorkspace = value;
                this.OnPropertyChanged(() => this.CurrentWorkspace);
            }
        }
    }

    private LinkedList<WorkspaceViewModel> _navigationHistory;

    public ICommand NavigateBackwardCommand { get; set; }
    public ICommand NavigateForwardCommand { get; set; }
}

我还有一个WorkspaceHostView,它绑定到WorkspaceHostViewModel,如下所示.

I also have a WorkspaceHostView that binds to the WorkspaceHostViewModel as follows.

<Window x:Class="MyNavigator.WorkspaceHostViewModel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Window.Resources>
    <ResourceDictionary Source="../../Resources/WorkspaceHostResources.xaml" />
  </Window.Resources>

  <Grid>
    <!-- Current Workspace -->
    <ContentControl Content="{Binding Path=CurrentWorkspace}"/>
  </Grid>

</Window>

在WorkspaceHostResources.xaml文件中,我关联了WPF应该用来使用DataTemplates渲染每个WorkspaceViewModel的视图.

In the WorkspaceHostResources.xaml file, I associate the View that WPF should use to render each WorkspaceViewModel using DataTemplates.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:MyNavigator">

  <DataTemplate DataType="{x:Type local:WorkspaceViewModel1}">
    <local:WorkspaceView1/>
  </DataTemplate>

  <DataTemplate DataType="{x:Type local:WorkspaceViewModel2}">
    <local:WorkspaceView2/>
  </DataTemplate>

</ResourceDictionary>

这很好用,但是一个缺点是由于DataTemplates的机制,在每个导航之间都重新创建了Views.如果视图包含复杂的控件(如DataGrids或TreeViews),则它们的内部状态将丢失.例如,如果我有一个具有可扩展行和可排序行的DataGrid,则当用户导航到下一个屏幕然后返回到DataGrid屏幕时,扩展/折叠状态和排序顺序将丢失.在大多数情况下,可以在导航之间跟踪需要保留的每条状态信息,但这似乎是一种非常不雅观的方法.

This works pretty well, but one disadvantage is that the Views are recreated between each navigation due to the mechanics of DataTemplates. If the view contains complex controls, like DataGrids or TreeViews, their internal state is lost. For example if I have a DataGrid with expandable and sortable rows, the expand/collapse state and sort order is lost when the user navigates to the next screen and then back to the DataGrid screen. In most cases it would be possible to track each piece of state information that needs to be preserved between navigations, but it seems like a very inelegant approach.

在改变整个屏幕的导航事件之间,是否有更好的方法来保留视图的整个状态?

Is there a better way to preserve the entire state of a view between navigation events that change the entire screen?

推荐答案

我最终将ActiveWorkspaces ObservableCollection属性添加到WorkspaceHostViewModel并将其绑定ItemsControl,如下所示.

I ended up adding an ActiveWorkspaces ObservableCollection property to the WorkspaceHostViewModel and binding an ItemsControl to it as follows.

<!-- Workspace -->
<ItemsControl ItemsSource="{Binding Path=ActiveWorkspaces}">
    <ItemsControl.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </ItemsControl.Resources>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>            
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="{x:Type ContentPresenter}">
            <Setter Property="Visibility" Value="{Binding Visible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

ActiveWorkspaces属性包含导航历史记录中的所有工作空间.它们都在UI中相互渲染,但是通过绑定各自的ContentPresenter的可见性,我一次只能显示一个.

The ActiveWorkspaces property contains all of the workspaces in the navigation history. They all get rendered on top of one another in the UI, but by binding the Visibility of their respective ContentPresenter I am able to show only one at a time.

向前/向后导航命令中存在操纵Visible属性(在Workspace本身是一个新属性)的逻辑.

The logic that manipulates the Visible property (which is a new property in the Workspace itself) exists in the navigate forward/backward commands.

这与雷切尔(Rachel)提出的解决方案非常相似,部分基于其网站上的ItemsControl教程;但是,我选择自己编写显示/隐藏逻辑,而不是依靠子类TabControl来替我完成.我仍然认为可以改善显示/隐藏逻辑.具体来说,我想从Workspace类中删除Visible属性,但是现在这已经足够好了.

This is a very similar approach to the solution proposed by Rachel and is in part based on the ItemsControl tutorial found on her web site; however, I opted to write the show/hide logic myself rather than rely on a subclassed TabControl to do it for me. I still feel that it would be possible to improve the show/hide logic. Specifically I would like to eliminate the Visible property from the Workspace class, but for now this works well enough.

成功使用上述解决方案几个月后,我选择将其替换为进行导航. Prism处理在指定区域中实例化,初始化和显示View的工作.此外,您可以控制View的生命周期,是否应在后续的导航请求中重复使用它,在与事件之间进行导航以及从事件进行导航时应执行什么逻辑,以及是否应中止导航(由于请注意,基于Prism视图的导航需要依赖注入容器(例如Unity或MEF),因此您可能需要将其合并到应用程序体系结构中,但是即使没有Prism导航,也要采用DI容器非常值得投资.

After using the above solution successfully for several months, I opted to replace it with the view-based navigation functionality provided by Prism. Although this approach requires much more overhead, the advantages greatly outweigh the effort involved. The general idea is to define Region's in your Views and then navigate by calling regionManager.RequestNavigate("RegionName", "navigationUri") in your ViewModel. Prism handles the legwork of instantiating, initializing and displaying your View in the specified Region. Additionally, you can control the lifetime of your View, whether or not it should be re-used upon subsequent navigation requests, what logic should be performed on navigation to and on navigation from events, and whether or not navigation should be aborted (due to unsaved changes in current View, etc.) Note that Prism view-based navigation requires a Dependency Injection Container (such as Unity or MEF) so you will likely need to incorporate this into your application architecture, but even without Prism navigation, adopting a DI container is well worth the investment.

这篇关于在MVVM应用程序中的视图之间导航时,如何保留视图的完整状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆