好的。这就是你应该如何处理TabControl
WPF 中的 a:
<Window x:Class="MiscSamples.MVVMTabControlSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MiscSamples"
Title="MVVMTabControlSample" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Tab1ViewModel}">
<!-- Here I just put UI elements and DataBinding -->
<!-- You may want to encapsulate these into separate UserControls or something -->
<StackPanel>
<TextBlock Text="This is Tab1ViewModel!!"/>
<TextBlock Text="Text1:"/>
<TextBox Text="{Binding Text1}"/>
<TextBlock Text="Text2:"/>
<TextBox Text="{Binding Text2}"/>
<CheckBox IsChecked="{Binding MyBoolean}"/>
<Button Command="{Binding MyCommand}" Content="My Command!"/>
</StackPanel>
</DataTemplate>
<!-- Here you would add additional DataTemplates for each different Tab type (where UI and logic is different from Tab 1) -->
</Window.Resources>
<DockPanel>
<Button Command="{Binding AddNewTabCommand}" Content="AddNewTab"
DockPanel.Dock="Bottom"/>
<TabControl ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTab}"
DisplayMemberPath="Title">
</TabControl>
</DockPanel>
</Window>
代码背后:
public partial class MVVMTabControlSample : Window
{
public MVVMTabControlSample()
{
InitializeComponent();
DataContext = new MVVMTabControlViewModel();
}
}
主视图模型:
public class MVVMTabControlViewModel: PropertyChangedBase
{
public ObservableCollection<MVVMTabItemViewModel> Tabs { get; set; }
private MVVMTabItemViewModel _selectedTab;
public MVVMTabItemViewModel SelectedTab
{
get { return _selectedTab; }
set
{
_selectedTab = value;
OnPropertyChanged("SelectedTab");
}
}
public Command AddNewTabCommand { get; set; }
public MVVMTabControlViewModel()
{
Tabs = new ObservableCollection<MVVMTabItemViewModel>();
AddNewTabCommand = new Command(AddNewTab);
}
private void AddNewTab()
{
//Here I just create a new instance of TabViewModel
//If you want to copy the **Data** from a previous tab or something you need to
//copy the property values from the previously selected ViewModel or whatever.
var newtab = new Tab1ViewModel {Title = "Tab #" + (Tabs.Count + 1)};
Tabs.Add(newtab);
SelectedTab = newtab;
}
}
Abstract TabItem ViewModel(你从中派生出每个不同的Tab“Widget”)
public abstract class MVVMTabItemViewModel: PropertyChangedBase
{
public string Title { get; set; }
//Here you may want to add additional properties and logic common to ALL tab types.
}
TabItem 1 视图模型:
public class Tab1ViewModel: MVVMTabItemViewModel
{
private string _text1;
private string _text2;
private bool _myBoolean;
public Tab1ViewModel()
{
MyCommand = new Command(MyMethod);
}
public string Text1
{
get { return _text1; }
set
{
_text1 = value;
OnPropertyChanged("Text1");
}
}
public bool MyBoolean
{
get { return _myBoolean; }
set
{
_myBoolean = value;
MyCommand.IsEnabled = !value;
}
}
public string Text2
{
get { return _text2; }
set
{
_text2 = value;
OnPropertyChanged("Text2");
}
}
public Command MyCommand { get; set; }
private void MyMethod()
{
Text1 = Text2;
}
}
编辑:我忘了发布命令类(虽然你肯定有你自己的)
public class Command : ICommand
{
public Action Action { get; set; }
public void Execute(object parameter)
{
if (Action != null)
Action();
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
public event EventHandler CanExecuteChanged;
public Command(Action action)
{
Action = action;
}
}
最后是 PropertyChangedBase(只是一个辅助类)
public class PropertyChangedBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
结果:
- 基本上,每个 Tab Item 类型都是一个
Widget
,它包含自己的逻辑和数据。
- 您在 ViewModel 或 Model 级别定义所有逻辑和数据,而不是在 UI 级别。
- 您可以操作在 ViewModel 或 Model 级别中定义的数据,并通过DataBinding更新 UI ,从不直接接触 UI。
- 请注意我如何利用DataTemplates为每个 Tab Item ViewModel 类提供特定的 UI。
- 复制新选项卡时,您只需创建所需的新实例
ViewModel
,并将其添加到ObservableCollection
. WPF 的 DataBinding 会根据 Collection 的更改通知自动更新 UI。
- 如果您想创建其他选项卡类型,只需从
MVVMTabItemViewModel
那里派生并添加您的逻辑和数据。DataTemplate
然后,您为该新 ViewModel创建一个,而 WPF 会负责其余的工作。
- 除非有真正的理由,否则您永远不会在WPF 的过程代码中操作 UI 元素。您不能“取消选中”或“禁用”UI 元素,因为 UI 元素必须反映 ViewModel 提供的数据的状态。因此,“检查/取消检查”状态或“启用/禁用”状态只是
bool
UI 绑定到的 ViewModel 中的一个属性。
- 请注意,这如何完全消除了对可怕的类似 winforms 的黑客攻击的需要,也消除了对
VisualTreeHelper.ComplicateMyCode()
某种东西的需要。
- 将我的代码复制并粘贴到 a 中
File -> New Project -> WPF Application
,然后自己查看结果。