扩展 mm8 的答案,这就是我的做法:
首先,我将创建一个 BaseViewModel 类,供每个视图模型继承,它代表 TabControl 的每个选项卡。
我喜欢将它实现为一个带有名为“Title”的抽象字符串属性的抽象类,因此我可以动态创建选项卡并显示它们的名称(或标题)。该类还将实现 NotifyPropertyChanged 接口。
public abstract class BaseViewModel : INotifyPropertyChanged
{
public abstract string Title { get; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
然后我将创建从这个基本视图模型继承的每个视图模型。例如:
public class NameViewModel : BaseViewModel
{
public override string Title
{
get
{
return "Name";
}
}
}
您可以对其他视图模型执行相同的操作,只需更改每个视图模型的“标题”属性。
现在我将创建应用程序的 MainView 及其对应的视图模型。
MainViewModel 将有一个 BaseViewModels 的集合和一个“CurrentViewModel”(BaseViewModel 类型),并将您想要的所有视图模型添加到其构造函数的集合中,如下所示:
public class MainViewModel : BaseViewModel
{
public override string Title
{
get
{
return "Main";
}
}
private ObservableCollection<BaseViewModel> _viewModels;
public ObservableCollection<BaseViewModel> ViewModels
{
get { return _viewModels; }
set
{
if (value != _viewModels)
{
_viewModels = value;
OnPropertyChanged();
}
}
}
private BaseViewModel _currentViewModel;
public BaseViewModel CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (value != _currentViewModel)
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
public MainViewModel()
{
ViewModels = new ObservableCollection<BaseViewModel>();
ViewModels.Add(new NameViewModel());
ViewModels.Add(new CodeViewModel());
ViewModels.Add(new FactorDetailViewModel());
}
}
最后,您的主视图将类似于 mm8 发布的内容:
(请注意我的代码与 mm8 的代码的区别:(1)您需要将 TabControl 的 DisplayMemberPath 设置为 BaseViewModels 的“Title”属性,并且(2)您需要将 Window 的 DataContext 设置为您的主视图模型)
<Window ...>
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<TabControl Name="TCMain"
ItemsSource="{Binding ViewModels}"
DisplayMemberPath="Title"
SelectedItem="{Binding CurrentViewModel}"
Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:NameViewModel}">
<local:NameView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:CodeViewModel}">
<local:CodeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:FactorDetailViewModel}">
<local:FactorDetailView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
现在它应该可以按预期工作了。每次更改 TabControl 的活动选项卡时,控件的 SelectedItem 属性都会更改为相应的视图模型,该视图模型将被模板化为其对应的视图。
顺便说一下,这种方法称为“View Model First”(而不是 View First)。
编辑
如果您想在具有更改当前视图模型命令的视图模型之一上有一个按钮,您可以这样做:
我想您应该熟悉 Josh Smith 的 RelayCommand。如果不是,只需在网络上搜索它的实现即可。
您需要在 MainViewModel 上创建一个 ICommand 属性,该属性将负责更改“CurrentViewModel”属性:
private ICommand _showFactorDetailCommand;
public ICommand ShowFactorDetailCommand
{
get
{
if (_showFactorDetailCommand == null)
{
_showFactorDetailCommand = new RelayCommand(p => true, p => show());
}
return _showFactorDetailCommand;
}
}
private void show()
{
CurrentViewModel = ViewModels.Single(s => s.Title == "Factor");
}
上面的 show() 方法只是搜索标题为“Factor”的视图模型集合并将其设置为 CurrentViewModel,而 CurrentViewModel 又将是 ContentControl 的内容,它充当您内部 TabControl 的 ContentTemplate主视图。
请记住,您的 FactorDetailViewModel 应按如下方式实现:
public class FactorDetailViewModel : ViewModelBase
{
public override string Title
{
get
{
return "Factor";
}
}
}
“NameView”内的按钮将绑定到此命令,该命令是“MainViewModel”使用RelativeSource 绑定的属性:
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowFactorDetailCommand}" Content="Show Factor" Height="20" Width="60"/>
您可以使该命令更通用,将您想要导航到的视图模型的标题作为命令参数传递:
private ICommand _showCommand;
public ICommand ShowCommand
{
get
{
if (_showCommand == null)
{
_showCommand = new RelayCommand(p => true, p => show(p));
}
return _showCommand;
}
}
private void show(p)
{
var vm = (string)p;
CurrentViewModel = ViewModels.Single(s => s.Title == vm);
}
然后在你的视图上,也传递命令参数:
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowCommand}" Content="Show Factor" CommandParameter="Factor" Height="20" Width="60"/>
最后,要完全隐藏您的 TabItems,您需要设置 TabControl 的 ItemContainerStyle,以便 TabItems 的 Visibility 具有“Collapsed”的值。
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</TabControl.ItemContainerStyle>