擴展在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」屬性。
現在我將創建應用程序的主視圖及其相應的視圖模型。
的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設置爲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()方法搜索視圖模型的集合,具有標題「因子」並將其設置爲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的可見性的價值「坍塌」。
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</TabControl.ItemContainerStyle>
您應該也許應該提到,這需要將tab view-models添加到主視圖模型的list屬性中,並且該TabControl.ItemsSource綁定到該屬性。以防萬一您的示例代碼不明顯。 –
雖然它是:) – mm8
TabControl.ItemsSource應設置爲ObservableCollection。什麼可以替代T而不是T?視圖模型不一樣。 –
Farshad