我确实有3个用户控件(TIShowNames,TIEnterCode,TIShowFactor)。 他们有自己的观点和相应的viewModel。
所有这3个,都在mainwindowView中。
这是我的mainwindowView Xaml:
<Controls:TransitionPresenter Name="transContainer" Grid.Row="2" RestDuration="0:0:1" IsLooped="False" Transition="{StaticResource SlideTransition}">
<TabControl Name="TCMain" Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabItem Name="TIShowNames" Visibility="Collapsed">
<views:NameView x:Name="NameViewElement" />
</TabItem>
<TabItem Name="TIEnterCode" Visibility="Collapsed">
<views:CodeView x:Name="CodeViewElement" />
</TabItem>
<TabItem Name="TIShowFactor" Visibility="Collapsed">
<views:FactorDetailView x:Name="FactorDetailViewElement" />
</TabItem>
</TabControl>
</Controls:TransitionPresenter>
在我以前的编程风格中,我习惯使用这行代码来浏览标签项(没有任何模式):
private void ChangeTabItemTo(TabItem TI)
{
transContainer.ApplyTransition("TCMain", "TCMain");
TCMain.SelectedItem = TI;
}
我在“TIShowNames”中有一个btn节目,所以当我点击它时,它必须转到“TIShowFactor”。 在MVVM中,ViewModel不知道有关视图的任何事情(此项目选项卡位于其父视图中!!!)。那么他如何在不违反MVVM的情况下更改选定的Tab项?
另一个尝试: 由于此错误,更改Selectedindex不会起作用:
“System.Windows.Data错误:40:BindingExpression路径错误:'索引' 'object'''MainWindowViewModel'找不到属性 (的HashCode = 22018304)”。 BindingExpression:路径= AAA; DataItem ='MainWindowViewModel'(HashCode = 22018304);目标元素是 'TabControl'(Name =''); target属性为'IsSelected'(类型 '布尔')“
更新:
控制:TransitionPresenter来自Fluid DLL
更新:
我想要隐藏标签项的标题,这样就没有人可以点击标题了,只有通过用户控件中的btns才能通过标题导航导航
答案 0 :(得分:2)
您可以在视图中为每个视图模型类型定义DataTemplate
:
<TabControl Name="TCMain"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding ViewModel}"
Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 ">
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:NameViewViewModel}">
<views:NameView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:CodeViewViewModel}">
<views:CodeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:FactorDetailViewModel}">
<views:FactorDetailView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
...并将SelectedItem
属性绑定到您在视图模型中设置的源属性,例如:
public object ViewModel
{
get { return _vm; }
set { _vm = value; NotifyPropertyChanged(); }
}
...
ViewModel = new CodeViewViewModel(); //displays the CodeView
答案 1 :(得分:2)
扩展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";
}
}
}
您会对其他视图模型执行相同的操作,只更改每个视图模型的“title”属性。
现在我将创建应用程序的MainView及其相应的视图模型。
MainViewModel将包含一组BaseViewModel和一个“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设置为你的MainViewModel)
<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”中的按钮将绑定到此命令,该命令是使用RelativeSource绑定的“MainViewModel”的属性:
<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>