c# - 如何使用 DataTemplates 通过 ViewModel-First 方法缓存动态切换的 View

标签 c# wpf xaml mvvm

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

我在 MainWindow.xaml 中有一些 View ,我在运行时通过用户选择在它们之间动态切换。

这些 View 使用以下技术绑定(bind)到它们相应的 View 模型:

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

Control1ViewModel.cs

public class Control1ViewModel : ViewModelBase
{
    ...
}

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

MainWindow.xaml

...
<ContentControl Content="{Binding CurrentView}"/> // This is were the view appears.
...

MainViewModel.cs

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

为了方便起见,我没有添加更多的控件,只是放了一个(Control1)来缩短问题代码部分。如上所述,假设要切换的 View 不止一个。

每次 CurrentView 属性设置一个新的 ViewModelBase 值(例如 Control1ViewModel),WPF将构造绑定(bind) View DataTemplate 可视化树对象 的新实例,因此旧实例将丢失。

这意味着我无法在切换 View 时缓存 View (例如 Control1)。

我找到的唯一解决方案是用它的 ViewModel(使用 DataContext)“硬编码”View,但是按照这个解决方案会发生以下情况:

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

我想知道是否有不使用 View 模型“硬编码” View 的解决方案,这样我就可以保留当前的 ​​ViewModelBase 开关和 ViewModel-First 方法。

最佳答案

你可以有以下方法:

  1. 而不是服用 ContentControl拿个ItemsControl

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

  1. ItemsPanelItemsControl作为网格和 SelectedItem设置 Z-Index到 1 和其余项目集 Z-Index到 0。这样,一次只有一个 View 可见,它会覆盖其他 View 。

  2. MainViewModel 中的两个属性. Views类型 ObservableCollection<ViewModelBase>CurrentView类型 ViewModelBase并将其与 ItemsControls 绑定(bind)的 ItemsSourceSelectedItem分别。

现在,当您想要打开一个 View 时,创建一个 ViewModel,将其添加到 View 列表并将其设置为 CurrentView。如果它已经存在于列表中,只需将其设置为 CurrentView。

如果您希望它永远关闭,还可以提供一个关闭按钮。也就是说,如果您要关闭它,它将从列表中删除并且不会被缓存。

这就像您在窗口中打开了不同的 View 一样,您可以在它们之间切换。如果需要,您可以关闭 View 。

编辑: 见以下代码:

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

在这里您可以在 ViewModel 中看到你必须拥有一个属性(property) ZIndex ,它将用于在顶部显示当前 View 。所以每当你想显示 View 时,只需设置 ZIndex ViewModel的属性(property)为 1 并将重置 View 重置为 0。

关于c# - 如何使用 DataTemplates 通过 ViewModel-First 方法缓存动态切换的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30232697/

相关文章:

c# - Silverlight ReaderWriterLock 实现好/坏?

c# - 什么是 Xamarin.Forms 等效于 layoutSubviews?

c# XAML ProgressBar 正确设置渐变填充

c# - ELMAH - 它是否记录引用 URL

c# - 抽象属性的可访问性

c# - 选中数据网格标题中的所有复选框及其在 wpf mvvm 中的绑定(bind)

c# - 创建TextBlock时我可以知道它的宽度吗?

c# - WPF/XAML 绑定(bind) : Work with real DataContext

c# - WPF 无法访问嵌套控件

WPF:无法设置焦点