2016-09-29 110 views
-2

我們有我們自己的TabControl類,它可以覆蓋OnSelectionChangedTabControl SelectionChanged多次觸發

相關的代碼是:

protected override void OnSelectionChanged(SelectionChangedEventArgs e) 
    { 
     base.OnSelectionChanged(e); 
     UpdateSelectedItem(); 
    } 


    internal Grid ItemsHolder { get; set; } 

    public TabControl() 
     : base() 
    { 
     ItemsHolder = new Grid(); 
     // this is necessary so that we get the initial databound selected item 
     this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
    } 


    /// <summary> 
    /// if containers are done, generate the selected item 
    /// </summary> 
    /// <param name="sender"></param> 
    /// <param name="e"></param> 
    void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) 
    { 
     if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 
     { 
      this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; 
      UpdateSelectedItem(); 
     } 
    } 

    private bool _isTemplateApplied = false; 
    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 
     var itemsHolderParent = GetTemplateChild("PART_ItemsHolderParent") as Panel; 
     if (itemsHolderParent != null) 
     { 
      var parent = ItemsHolder.Parent as Panel; 
      if (parent != null) 
       parent.Children.Remove(ItemsHolder); 
      itemsHolderParent.Children.Add(ItemsHolder); 
      if (parent != null) 
       ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; 
      UpdateSelectedItem(); 
     } 
     _isTemplateApplied = true; 
    } 


    internal void ClearChildren() 
    { 
     foreach (var cp in ItemsHolder.Children.OfType<ContentPresenter>()) 
      cp.Content = null; 
     ItemsHolder.Children.Clear(); 
    } 

    /// <summary> 
    /// when the items change we remove any generated panel children and add any new ones as necessary 
    /// </summary> 
    /// <param name="e"></param> 
    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) 
    { 
     base.OnItemsChanged(e); 

     switch (e.Action) 
     { 
      case NotifyCollectionChangedAction.Reset: 
       var removeList = new List<ContentPresenter>(); 
       foreach (var presenter in ItemsHolder.Children.OfType<ContentPresenter>()) 
        removeList.Add(presenter); 
       var oldItemsCount = removeList.Count; 

       foreach (var item in Items) 
       { 
        var itemPresenter = FindChildContentPresenter(item); 
        if (removeList.Contains(itemPresenter)) 
         removeList.Remove(itemPresenter); 
       } 

       foreach (var removePresenter in removeList) 
        ItemsHolder.Children.Remove(removePresenter); 

       //If there were old items, the SelectionChanged in the Selector will force a new selected item 
       //If there are no items we can't update the selected item (there is nothing to select) 
       //If the tempalte is not yet applied, applying the template will select the tabitem 
       if (oldItemsCount == 0 && Items != null && Items.Count > 0 && _isTemplateApplied) 
        UpdateSelectedItem(); 
       break; 

      case NotifyCollectionChangedAction.Add: 
      case NotifyCollectionChangedAction.Remove: 
       if (e.OldItems != null) 
       { 
        foreach (var item in e.OldItems) 
        { 
         ContentPresenter cp = FindChildContentPresenter(item); 
         if (cp != null) 
         { 
          ItemsHolder.Children.Remove(cp); 
         } 
        } 
       } 

       // don't do anything with new items because we don't want to 
       // create visuals that aren't being shown 

       UpdateSelectedItem(); 
       break; 

      case NotifyCollectionChangedAction.Replace: 
       throw new NotImplementedException("Replace not implemented yet"); 
     } 
    } 

    /// <summary> 
    /// generate a ContentPresenter for the selected item 
    /// </summary> 
    internal void UpdateSelectedItem() 
    { 
     if (SelectedIndex == -1 && Items != null && Items.Count > 0) 
      SelectedIndex = 0; 
     if (SelectedIndex == -1) 
      return; 

     // generate a ContentPresenter if necessary 
     TabItem item = GetSelectedTabItem(); 
     if (item != null) 
     { 
      FindOrElseCreateChildContentPresenter(item); 
     } 

     // show the right child 
     foreach (ContentPresenter child in ItemsHolder.Children.OfType<ContentPresenter>()) 
     { 
      if ((child.Tag as TabItem).IsSelected) 
       SelectedTabItem = child.Tag as TabItem; 
      child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed; 
     } 
    } 


    /// <summary> 
    /// create the child ContentPresenter for the given item (could be data or a TabItem) 
    /// </summary> 
    /// <param name="item"></param> 
    /// <returns></returns> 
    internal ContentPresenter FindOrElseCreateChildContentPresenter(object item) 
    { 
     if (item == null) 
     { 
      return null; 
     } 

     ContentPresenter cp = FindChildContentPresenter(item); 

     if (cp != null) 
     { 
      cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); 
      return cp; 
     } 

     // the actual child to be added. cp.Tag is a reference to the TabItem 
     cp = new ContentPresenter(); 
     cp.Content = (item is TabItem) ? (item as TabItem).Content : item; 

     Dispatcher.BeginInvoke(new Action(() => 
     { 
      cp.ContentTemplate = this.SelectedContentTemplate; 
      cp.ContentTemplateSelector = this.SelectedContentTemplateSelector; 
      cp.ContentStringFormat = this.SelectedContentStringFormat; 
     }), System.Windows.Threading.DispatcherPriority.Send); 


     cp.Visibility = Visibility.Collapsed; 
     cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item)); 
     ItemsHolder.Children.Add(cp); 
     return cp; 
    } 

    /// <summary> 
    /// Find the CP for the given object. data could be a TabItem or a piece of data 
    /// </summary> 
    /// <param name="data"></param> 
    /// <returns></returns> 
    public ContentPresenter FindChildContentPresenter(object data) 
    { 
     if (data is TabItem) 
     { 
      data = (data as TabItem).Content; 
     } 

     if (data == null) 
     { 
      return null; 
     } 

     foreach (ContentPresenter cp in ItemsHolder.Children.OfType<ContentPresenter>()) 
     { 
      if (cp.Content == data) 
      { 
       return cp; 
      } 
     } 

     return null; 
    } 

    /// <summary> 
    /// copied from TabControl; wish it were protected in that class instead of private 
    /// </summary> 
    /// <returns></returns> 
    protected TabItem GetSelectedTabItem() 
    { 
     object selectedItem = base.SelectedItem; 
     if (selectedItem == null) 
     { 
      return null; 
     } 
     TabItem item = selectedItem as TabItem; 
     if (item == null) 
     { 
      item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem; 
     } 
     return item; 


    } 



    internal TabItem SelectedTabItem 
    { 
     get { return (TabItem)GetValue(SelectedTabItemProperty); } 
     set { SetValue(SelectedTabItemProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for SelectedTabItem. This enables animation, styling, binding, etc... 
    internal static readonly DependencyProperty SelectedTabItemProperty = 
     DependencyProperty.Register("SelectedTabItem", typeof(TabItem), typeof(TabControl), new UIPropertyMetadata(null)); 

當我使用調試器我看到,有時OnSelectionChanged是一個選項卡切換髮射多次。這是一個錯誤?我怎樣才能解決這個問題?還是它的行爲意向,我可以使用另一個事件來檢測標籤開關?

+0

當您從一個標籤切換到另一個標籤時,兩個事件都會被觸發。 –

+0

@NawedNabiZada不,它不是。至少這不是默認行爲。然而,從這個問題來看,它不清楚什麼'UpdateSelectedItem()'正在做什麼。 – Clemens

+0

@NawedNabiZada這是爲什麼?我將如何檢測tabswitches(哪個事件)? – Sybren

回答

-1

如果您在UpdateSelectedItem方法設置的TabControl的selectedValue,則這是要更進入代碼塊比once.For例如,如果你設置

private void UpdateSelectedItem() 
    { 
     this.SelectedValue = 0; // set to a value 
    } 

這樣的代碼塊,你會看到調試器兩次輸入OnSelectionChanged方法。

+0

'SelectedValue'永遠不會在'UpdateSelectedItem()'中設置。你什麼意思? – Sybren

+0

@Sybren :)我不知道你在UpdateSelectedItem()中寫入沒有任何信息,是嗎? – FreeMan

+0

@Sybren但沒有UpdateSelectedItem()方法,它對我很好。我的意思是你設置了一些與你的tabcontrol相關的東西,我的朋友。那清楚了嗎? – FreeMan