2016-09-29 105 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