TabControl SelectionChanged多次触发

时间:2016-09-29 09:00:41

标签: c# wpf tabcontrol

我们拥有自己的UserRepository类,可以覆盖TabControl

相关代码是:

OnSelectionChanged

当我使用调试器时,我发现对于一个制表符切换,有时会多次触发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)); 。这是一个错误吗?我怎样才能解决这个问题?或者它是预期的行为,我可以使用另一个事件来检测标签交换机吗?

1 个答案:

答案 0 :(得分:-1)

如果在UpdateSelectedItem方法中设置TabControl的selectedValue,它将多次输入代码块。例如,如果设置

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

就像这个代码块一样,你会看到调试器两次输入OnSelectionChanged方法。

相关问题