我正在使用WPF TabControl,其最后一项始终是添加新标签的按钮,类似于Firefox:
TabControl的ItemSource绑定到ObservableCollection,并通过此“+”按钮向集合添加项目非常有效。我遇到的唯一问题是,在单击“+”选项卡后,我无法为我的生活设置新创建的(或任何其他现有选项卡)进行聚焦,因此当添加选项卡时,UI看起来像这样:
为了解释我是如何实现这种“特殊”选项卡行为,TabControl是模板化的,其NewButtonHeaderTemplate有一个控件(在我的情况下为Image),它在视图模型中调用AddListener命令(只有相关的代码是示出):
<Window x:Class="AIS2.PortListener.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ais="http://www.leica-geosystems.com/xaml"
xmlns:l="clr-namespace:AIS2.PortListener"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
DataContext="{Binding Source={StaticResource Locator}>
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="newTabButtonHeaderTemplate">
<Grid>
<Image Source="..\Images\add.png" Height="16" Width="16">
</Image>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand
Command="{Binding Source={StaticResource Locator},
Path=PortListenerVM.AddListenerCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>
<DataTemplate x:Key="newTabButtonContentTemplate"/>
<DataTemplate x:Key="itemHeaderTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<DataTemplate x:Key="itemContentTemplate">
<l:ListenerControl></l:ListenerControl>
</DataTemplate>
<l:ItemHeaderTemplateSelector x:Key="headerTemplateSelector"
NewButtonHeaderTemplate="{StaticResource newTabButtonHeaderTemplate}"
ItemHeaderTemplate="{StaticResource itemHeaderTemplate}"/>
<l:ItemContentTemplateSelector x:Key="contentTemplateSelector"
NewButtonContentTemplate="{StaticResource newTabButtonContentTemplate}"
ItemContentTemplate="{StaticResource itemContentTemplate}"/>
</ResourceDictionary>
</Window.Resources>
<TabControl Name="MainTab" Grid.Row="2" ItemsSource="{Binding Listeners}"
ItemTemplateSelector="{StaticResource headerTemplateSelector}"
ContentTemplateSelector="{StaticResource contentTemplateSelector}"
SelectedItem="{Binding SelectedListener}">
</TabControl>
AddListener命令只是向ObservableCollection添加一个项目,该项目有效更新TabControl的ItemSource并添加一个新选项卡:
private ObservableCollection<Listener> _Listeners;
public ObservableCollection<Listener> Listeners
{
get { return _Listeners; }
}
private object _SelectedListener;
public object SelectedListener
{
get { return _SelectedListener; }
set
{
_SelectedListener = value;
OnPropertyChanged("SelectedListener");
}
}
public PortListenerViewModel()
{
// Place the "+" tab at the end of the tab control
var itemsView = (IEditableCollectionView)CollectionViewSource.GetDefaultView(_Listeners);
itemsView.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtEnd;
}
private RelayCommand _AddListenerCommand;
public RelayCommand AddListenerCommand
{
get
{
if (_AddListenerCommand == null)
_AddListenerCommand = new RelayCommand(param => this.AddListener());
return _AddListenerCommand;
}
}
public void AddListener()
{
var newListener = new TCPListener(0, "New listener");
this.Listeners.Add(newListener);
// The following two lines update the property, but the focus does not change
//this.SelectedListener = newListener;
//this.SelectedListener = this.Listeners[0];
}
但是设置SelectedListener属性不起作用,即使TabControl的SelectedItem绑定到它。它必须与在WPF中更新内容的顺序有关,因为如果我在SelectedListener的set
中设置了断点,我可以看到以下情况:
this.Listeners.Add(newListener);
this.SelectedListener = newListener;
set
set
(根据调试器的类型为MS.Internal.NamedObject)有没有办法可以解决这个问题?我有错误的方法吗?
答案 0 :(得分:3)
我认为您在点击新标签时会触发两个事件:MouseLeftButtonDown
和TabControl.SelectionChanged
我认为他们都排队等候,然后一次处理一个。
因此,您的项目将被添加,设置为已选中,然后在重新绘制之前发生SelectionChanged事件,以将选择更改为[+]
选项卡。
也许尝试使用Dispatcher
来设置SelectedItem
,以便在TabControl
更改选择后发生SelectionChanged
。或者,如果用户尝试切换到NewTab,它会取消SelectedTab
事件,因此选定的选项卡实际上不会更改(当然,{Mouse}因为事件,Button
将是您的NewItem将会发生)
当我过去做过类似的事情时,我实际上覆盖了TabControl Template,将AddTab按钮创建为TabItem
,而不是NewItemPlaceholder
。我想建议这样做,而不是首先使用NewItemPlaceholder
,但我从未尝试使用Template
,所以不要真的知道它是否比覆盖{{更好或更差1}}。
答案 1 :(得分:1)
看一下有关哨兵对象的帖子:WPF Sentinel objects and how to check for an internal type 有几种方法可以解决它们的问题,该帖子提供了其中一个。