我們有我們自己的TabControl
類,它可以覆蓋OnSelectionChanged
。TabControl 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
是一個選項卡切換髮射多次。這是一個錯誤?我怎樣才能解決這個問題?還是它的行爲意向,我可以使用另一個事件來檢測標籤開關?
當您從一個標籤切換到另一個標籤時,兩個事件都會被觸發。 –
@NawedNabiZada不,它不是。至少這不是默認行爲。然而,從這個問題來看,它不清楚什麼'UpdateSelectedItem()'正在做什麼。 – Clemens
@NawedNabiZada這是爲什麼?我將如何檢測tabswitches(哪個事件)? – Sybren