使用 MVVM 将新项目添加到 TabControl 的 ItemSource 时选择最后一个 TabItem
Selecting the last TabItem when new items are added to a TabControl's ItemSource using MVVM
我通过将 ItemsSource
绑定到 MyUnicornsViewModel
创建了一个动态生成的 TabControl
。
随着新项目添加到 MyUnicornsViewModel
... 创建了新的选项卡项目。但是,TabControl
中不会自动选择新添加的选项卡。
如何在添加新标签时选择它们?
<TabControl ItemsSource="{Binding MyUnicornsViewModel}" SelectedItem="{Binding SelectedItem}">
<TabControl.ItemTemplate>
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- body template-->
<DataTemplate>
<TextBlock Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
起初,我希望 TabControl
中有“ItemsChanged”或“ItemAdded”事件,这样我就可以在添加新项目时在代码隐藏中设置 SelectedIndex。
我尝试的另一件事是将 TabControl.SelectedItem
绑定到 MyUnicornsViewModel
中的 SelectedItem
属性。可悲的是,这也没有用。
MyUnicornsViewModel:
public class MyUnicornsViewModel : ObservableCollection<UnicornViewModel>
{
...
private void AddNewUnicorn()
{
var awesomeUnicorn = new UnicornViewModel();
Add(awesomeUnicorn);
SelectedItem = awesomeUnicorn; //I expected my TabControl to have 'awesomeUnicorn' selected.
}
public UnicornViewModel SelectedItem { get; set; }
}
这里有几个问题:
- 从
ObservableCollection
推导出“视图模型”很奇怪。一个视图模型应该包含一个可观察的集合。
- 视图模型需要实现
INotifyPropertyChanged
接口;从提供的代码中不清楚 UnicornViewModel
是否实现了此接口,但是,MyUnicornsViewModel
绝对没有。
这里有一些建议:
- 实现
INotifyPropertyChanged
接口的视图模型基础 class 将真正帮助您完成大部分工作。您可以使用 INotifyPropertyChanged documentation or look for an MVVM framework that fits well with your project (e.g. Prism, MVVM Light, ReactiveUI) 编写自己的代码。其中每一个都将提供一个基础 class 用于视图模型 - BindableBase
、ViewModelBase
、ReactiveObject
分别用于上述每个框架。
MyUnicornsViewModel
应该有:
- 独角兽合集
ObservableCollection
;这将绑定到 TabControl
. 上的 ItemsSource
属性
SelectedItem
属性 必须在设置时触发 PropertyChanged
事件。
这是一个使用 Prism 的快速示例:
public sealed class UnicornViewModel : BindableBase
{
public UnicornViewModel(string name, string content)
{
Name = name;
Content = content;
}
// these properties don't change and therefore don't need to raise property changed
public string Name { get; }
public string Content { get; }
}
public sealed class UnicornsViewModel : BindableBase
{
private UnicornViewModel _selectedUnicorn;
public UnicornsViewModel()
{
AddUnicornCommand = new DelegateCommand(AddUnicorn);
ClearUnicornsCommand = new DelegateCommand(ClearUnicorns, () => HasUnicorns).ObservesProperty(() => HasUnicorns);
}
public ObservableCollection<UnicornViewModel> Unicorns { get; } = new ObservableCollection<UnicornViewModel>();
public UnicornViewModel SelectedUnicorn
{
get => _selectedUnicorn;
set => SetProperty(ref _selectedUnicorn, value, () => RaisePropertyChanged(nameof(HasUnicorns)));
}
public DelegateCommand AddUnicornCommand { get; }
public DelegateCommand ClearUnicornsCommand { get; }
private bool HasUnicorns => Unicorns.Any(); // helper property for the clear command's can execute
private void AddUnicorn()
{
Unicorns.Add(new UnicornViewModel($"Unicorn {Unicorns.Count + 1}", Guid.NewGuid().ToString()));
SelectedUnicorn = Unicorns.Last();
}
private void ClearUnicorns()
{
SelectedUnicorn = null;
Unicorns.Clear();
}
}
我通过将 ItemsSource
绑定到 MyUnicornsViewModel
创建了一个动态生成的 TabControl
。
随着新项目添加到 MyUnicornsViewModel
... 创建了新的选项卡项目。但是,TabControl
中不会自动选择新添加的选项卡。
如何在添加新标签时选择它们?
<TabControl ItemsSource="{Binding MyUnicornsViewModel}" SelectedItem="{Binding SelectedItem}">
<TabControl.ItemTemplate>
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- body template-->
<DataTemplate>
<TextBlock Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
起初,我希望 TabControl
中有“ItemsChanged”或“ItemAdded”事件,这样我就可以在添加新项目时在代码隐藏中设置 SelectedIndex。
我尝试的另一件事是将 TabControl.SelectedItem
绑定到 MyUnicornsViewModel
中的 SelectedItem
属性。可悲的是,这也没有用。
MyUnicornsViewModel:
public class MyUnicornsViewModel : ObservableCollection<UnicornViewModel>
{
...
private void AddNewUnicorn()
{
var awesomeUnicorn = new UnicornViewModel();
Add(awesomeUnicorn);
SelectedItem = awesomeUnicorn; //I expected my TabControl to have 'awesomeUnicorn' selected.
}
public UnicornViewModel SelectedItem { get; set; }
}
这里有几个问题:
- 从
ObservableCollection
推导出“视图模型”很奇怪。一个视图模型应该包含一个可观察的集合。 - 视图模型需要实现
INotifyPropertyChanged
接口;从提供的代码中不清楚UnicornViewModel
是否实现了此接口,但是,MyUnicornsViewModel
绝对没有。
这里有一些建议:
- 实现
INotifyPropertyChanged
接口的视图模型基础 class 将真正帮助您完成大部分工作。您可以使用 INotifyPropertyChanged documentation or look for an MVVM framework that fits well with your project (e.g. Prism, MVVM Light, ReactiveUI) 编写自己的代码。其中每一个都将提供一个基础 class 用于视图模型 -BindableBase
、ViewModelBase
、ReactiveObject
分别用于上述每个框架。 MyUnicornsViewModel
应该有:- 独角兽合集
ObservableCollection
;这将绑定到TabControl
. 上的 SelectedItem
属性 必须在设置时触发PropertyChanged
事件。
ItemsSource
属性- 独角兽合集
这是一个使用 Prism 的快速示例:
public sealed class UnicornViewModel : BindableBase
{
public UnicornViewModel(string name, string content)
{
Name = name;
Content = content;
}
// these properties don't change and therefore don't need to raise property changed
public string Name { get; }
public string Content { get; }
}
public sealed class UnicornsViewModel : BindableBase
{
private UnicornViewModel _selectedUnicorn;
public UnicornsViewModel()
{
AddUnicornCommand = new DelegateCommand(AddUnicorn);
ClearUnicornsCommand = new DelegateCommand(ClearUnicorns, () => HasUnicorns).ObservesProperty(() => HasUnicorns);
}
public ObservableCollection<UnicornViewModel> Unicorns { get; } = new ObservableCollection<UnicornViewModel>();
public UnicornViewModel SelectedUnicorn
{
get => _selectedUnicorn;
set => SetProperty(ref _selectedUnicorn, value, () => RaisePropertyChanged(nameof(HasUnicorns)));
}
public DelegateCommand AddUnicornCommand { get; }
public DelegateCommand ClearUnicornsCommand { get; }
private bool HasUnicorns => Unicorns.Any(); // helper property for the clear command's can execute
private void AddUnicorn()
{
Unicorns.Add(new UnicornViewModel($"Unicorn {Unicorns.Count + 1}", Guid.NewGuid().ToString()));
SelectedUnicorn = Unicorns.Last();
}
private void ClearUnicorns()
{
SelectedUnicorn = null;
Unicorns.Clear();
}
}