2012-01-20 17 views
4

我工作的一個WPF TabControl的,其最後一個項目始終是一個按鈕添加新的選項卡,類似於Firefox時,覆蓋ObservableCollection,並通過此「+」按鈕將項目添加到集合中效果很好。我遇到的唯一問題是,點擊「+」選項卡後,我無法將新創建的(或任何其他現有選項卡)設置爲焦點,因此添加選項卡時,UI看起來是這樣的:的TabControl的的SelectedItem得到由NewItemPlaceholder添加標籤

screenshot 2

要解釋一下我是如何實現這一「特殊」標籤的行爲,該TabControl的是模板及其NewButtonHeaderTemplate具有(在我的情況下,圖像)調用的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我可以看到以下情況發生:

  1. this.Listeners.Add(newListener);
  2. this.SelectedListener = newListener;
  3. SelectedListener set使用正確的偵聽器對象調用
  4. SelectedListener set使用NewItemPlaceholder對象(根據調試器類型爲MS.Internal.NamedObject)調用

有沒有辦法解決這個問題?我有錯誤的方法嗎?

+0

+1優秀的問題。有興趣聽到答案 –

+0

你試過擴展TabControl來實現這個功能嗎? ..或者可能是附屬物? –

+0

@jberger:我對WPF相當陌生,我真的不知道從哪裏開始。歡迎您在下面發佈解決方案。 – Fueled

回答

3

我認爲你是觸發,當您點擊新標籤兩個事件:MouseLeftButtonDownTabControl.SelectionChanged

我覺得他們都得到排隊,然後處理一次一個。

因此,您的項目正在添加,設置爲選定狀態,然後在重新繪製之前發生SelectionChanged事件以將所選內容更改爲[+]選項卡。

也許嘗試使用Dispatcher來設置SelectedItem,因此在TabControl更改它的選擇後發生。或使其所以如果用戶嘗試切換到NEWTAB,它將取消SelectionChanged事件,因此所選擇的選項卡並沒有真正改變(當然SelectedTab將是你的newitem,因爲會發生MouseDown事件)

當我在過去做過這樣的事情時,我實際上覆蓋了TabControl Template以創建AddTab按鈕,作爲Button而不是TabItem。我想建議這樣做,而不是首先使用NewItemPlaceholder,但我從來沒有嘗試過使用NewItemPlaceholder,所以不知道它是否比覆蓋Template更好或更差。

+0

最後一段的+1,應該足以將佔位符的模板更改爲按鈕。 –

+0

這看起來很有前途。當我週一回去工作時,我會看看用一個按鈕替換TabItem。 – Fueled

+0

我沒有走完整路,只是改變了NewButtonHeaderTemplate來使用一個按鈕,效果很好。我可能必須這樣做,因爲用戶仍然可以「錯過」按鈕並單擊選項卡標題(因爲按鈕不佔用整個寬度和高度),但同時它是一個足夠好的解決方案。謝謝! – Fueled

1

看看這個帖子關於哨兵對象:WPF Sentinel objects and how to check for an internal type 有幾種方法可以解決他們的問題,該帖子提供了其中之一。

+0

在發佈之前,我正要做類似的事情,但無法弄清楚如何檢查MS.Internal.NameObject。我會嘗試在您發佈的鏈接中提供的建議,只是爲了嘗試。但我確實覺得這個解決方案只是解決真正的問題。 – Fueled

+0

我似乎無法使該選項正常工作。在接受的答案中運行代碼,確實可以得到「DisconnectedItem」對象,但對於NewItemPlaceholder,「var t = typeof(BindingExpressionBase).GetField(」NewItemPlaceholder「,BindingFlags.Static | BindingFlags.NonPublic);」總是返回null。 – Fueled

+0

另一種方法是檢查我的SelectedListener屬性的類型是Listener,如果是,則只調用OnPropertyChanged。無論如何,當我移除標籤時,我發現自己必須使用它。 – Fueled

相關問題