如何使用 DataTemplates 通过 ViewModel-First 方法缓存动态切换的视图
How to cache dynamically switched views by ViewModel-First approach using DataTemplates
(我正在使用 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>
...
Control1ViewModel.cs
public class Control1ViewModel : ViewModelBase
{
...
}
MainWindow.xaml
使用以下技术在上述视图之间切换:
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
)来缩短问题代码部分。如上所述,假设要切换的视图不止一个。
每次 CurrentView
属性 设置一个新的 ViewModelBase
值(例如 Control1ViewModel
),WPF会构造绑定视图的新实例DataTemplate
可视化树对象,所以旧的会丢失
这意味着我无法在切换视图时缓存视图(例如 Control1
)。
我找到的唯一解决方案是 "hardcode" View 及其 ViewModel(使用 DataContext),但按照此解决方案会发生以下情况:
- 我打破了 ViewModel-First 方法。
- 为了不破坏完整的 MVVM,我必须更改
CurrentView
签名并将其移至我的 MainWindow.xaml
. 的代码后面
- 我没有切换
ViewModelBase
类型,而是切换具体控件。
我想知道是否有没有 "hardcoding" 带有视图模型的视图的解决方案,因此我可以保留当前的 ViewModelBase
开关和 ViewModel-First 方法。
你可以有以下方法:
- 而不是服用
ContentControl
服用 ItemsControl
<ItemsControl ItemsSource="{Binding Views}" SelectedItem="{Binding CurrentView}"/>
将 ItemsControl
的 ItemsPanel
作为网格,SelectedItem
设置 Z-Index
为 1,其余项目设置 Z-Index
到 0。这样,一次只能看到一个视图,这将覆盖其他视图。
取MainViewModel
中的两个属性。 ObservableCollection<ViewModelBase>
类型的 Views
和 ViewModelBase
类型的 CurrentView
并将其分别与 ItemsControls
的 ItemsSource
和 SelectedItem
绑定。
现在,当您想打开一个视图时,创建一个 ViewModel,将其添加到 Views 列表并将其设置为 CurrentView。如果它已经存在于列表中,只需将其设置为 CurrentView。
如果您希望它永远关闭,还提供一个关闭按钮。也就是说,如果您要关闭它,它将从列表中删除并且不会被缓存。
这就像您在 window 中打开了不同的视图一样,您可以在它们之间切换。如果您愿意,可以关闭视图。
编辑:
见以下代码:
<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
中,您必须有一个 属性 ZIndex
,它将用于在顶部显示当前视图。因此,无论何时您想要显示视图,只需将 ViewModel
的 ZIndex
属性 设置为 1 并将重置视图重置为 0.
(我正在使用 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>
...
Control1ViewModel.cs
public class Control1ViewModel : ViewModelBase
{
...
}
MainWindow.xaml
使用以下技术在上述视图之间切换:
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
)来缩短问题代码部分。如上所述,假设要切换的视图不止一个。
每次 CurrentView
属性 设置一个新的 ViewModelBase
值(例如 Control1ViewModel
),WPF会构造绑定视图的新实例DataTemplate
可视化树对象,所以旧的会丢失
这意味着我无法在切换视图时缓存视图(例如 Control1
)。
我找到的唯一解决方案是 "hardcode" View 及其 ViewModel(使用 DataContext),但按照此解决方案会发生以下情况:
- 我打破了 ViewModel-First 方法。
- 为了不破坏完整的 MVVM,我必须更改
CurrentView
签名并将其移至我的MainWindow.xaml
. 的代码后面
- 我没有切换
ViewModelBase
类型,而是切换具体控件。
我想知道是否有没有 "hardcoding" 带有视图模型的视图的解决方案,因此我可以保留当前的 ViewModelBase
开关和 ViewModel-First 方法。
你可以有以下方法:
- 而不是服用
ContentControl
服用ItemsControl
<ItemsControl ItemsSource="{Binding Views}" SelectedItem="{Binding CurrentView}"/>
将
ItemsControl
的ItemsPanel
作为网格,SelectedItem
设置Z-Index
为 1,其余项目设置Z-Index
到 0。这样,一次只能看到一个视图,这将覆盖其他视图。取
MainViewModel
中的两个属性。ObservableCollection<ViewModelBase>
类型的Views
和ViewModelBase
类型的CurrentView
并将其分别与ItemsControls
的ItemsSource
和SelectedItem
绑定。
现在,当您想打开一个视图时,创建一个 ViewModel,将其添加到 Views 列表并将其设置为 CurrentView。如果它已经存在于列表中,只需将其设置为 CurrentView。
如果您希望它永远关闭,还提供一个关闭按钮。也就是说,如果您要关闭它,它将从列表中删除并且不会被缓存。
这就像您在 window 中打开了不同的视图一样,您可以在它们之间切换。如果您愿意,可以关闭视图。
编辑: 见以下代码:
<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
中,您必须有一个 属性 ZIndex
,它将用于在顶部显示当前视图。因此,无论何时您想要显示视图,只需将 ViewModel
的 ZIndex
属性 设置为 1 并将重置视图重置为 0.