如何使用DataTemplates通过ViewModel-First方法缓存动态切换的视图

Eid*_*o95 3 c# wpf xaml mvvm

(我正在使用GalaSoft.MvvmLight框架)

我有一些观点,我MainWindow.xaml通过用户选择在运行时动态切换它们.

这些视图使用以下技术与其对应的视图模型绑定:

MainWindow.xaml

...
<Window.Resources>
        <DataTemplate DataType="{x:Type vm:Control1ViewModel}">
            <v:Control1/>
        </DataTemplate>
    ... // Assume there is more then one DataTemplate. Every view has a unique view-model.
</Window.Resources>
...
Run Code Online (Sandbox Code Playgroud)

Control1ViewModel.cs

public class Control1ViewModel : ViewModelBase
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

MainWindow.xaml使用以下技术上述视图之间切换:

MainWindow.xaml

...
<ContentControl Content="{Binding CurrentView}"/> // This is were the view appears.
...
Run Code Online (Sandbox Code Playgroud)

MainViewModel.cs

public class MainViewModel : ViewModelBase
{
    ...
    private ViewModelBase _currentView;
    public ViewModelBase CurrentView
    {
        get { return _currentView; }
        private set
        {
            _currentView = value;
            base.RaisePropertyChanged("CurrentView");
        }
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

为方便起见,我没有添加更多控件,只需添加一个(Control1)来缩短问题代码部分.如上所述,假设有多个视图可以切换.

每次CurrentView一个新的设置属性ViewModelBase值(例如Control1ViewModel),WPF将构建绑定的视图的一个新实例DataTemplate视觉树对象,所以旧将会丢失.

这意味着我无法Control1在它们之间切换时缓存视图(例如).

我在回答中找到的唯一解决方案是使用ViewModel(使用DataContext)对视图进行"硬编码",但遵循此解决方案会发生以下情况:

  • 我打破了ViewModel-First方法.
  • 为了不打破完整的MVVM,我必须更改CurrentView签名并将其移动到我后面的代码中MainWindow.xaml.
  • ViewModelBase我没有切换类型,而是切换具体的控件.

我想知道是否有任何解决方案没有用视图模型"硬编码"视图,所以我可以保持当前的ViewModelBase开关和ViewModel-First方法.

Nit*_*shi 5

您可以采用以下方法:

  1. 而不是ContentControl采取一个ItemsControl

<ItemsControl ItemsSource="{Binding Views}" SelectedItem="{Binding CurrentView}"/>

  1. ItemsPanelItemsControl是一个网格和SelectedItem设置Z-Index为1和设置项目的其余部分Z-Index为0.这样只有一个视图会在同一时间,这将是比其他的看法是可见的.

  2. 拿两个属性MainViewModel.Views类型ObservableCollection<ViewModelBase>CurrentView类型ViewModelBase,并分别与ItemsControls's ItemsSourceSelectedItem.

现在,当您要打开视图时,创建一个ViewModel,将其添加到Views列表并将其设置为CurrentView.如果它已经在列表中,只需将其设置为CurrentView.

如果您希望永久关闭按钮,也可以提供关闭按钮.即如果你将它关闭,它将从列表中删除,不会被缓存.

这可以像在窗口中打开不同的视图一样工作,并且可以在它们之间切换.如果你想要,你可以关闭一个视图.

编辑: 见下面的代码:

            <ItemsControl ItemsSource="{Binding Views}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Grid Margin="10,10,0,10">
                        </Grid>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Grid.Opacity" Value="{Binding ZIndex}"/>
                        <Setter Property="Grid.ZIndex" Value="{Binding ZIndex}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ContentControl Content="{Binding}"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
Run Code Online (Sandbox Code Playgroud)

在这里,您可以看到,您ViewModel必须拥有一个属性ZIndex,该属性将用于显示顶部的当前视图.因此,只要您想显示视图,只需将ZIndexproperty属性设置ViewModel为1并将重置视图重置为0即可.