在 WPF Tabcontrol 标题模板中显示 SelectedIndex

Posted

技术标签:

【中文标题】在 WPF Tabcontrol 标题模板中显示 SelectedIndex【英文标题】:Show SelectedIndex in WPF Tabcontrol header template 【发布时间】:2013-03-11 07:12:02 【问题描述】:

我的应用程序中有 1...n 个选项卡控件,具有以下 XAML 设置:

<TabControl Name="ordersTabControl" ItemsSource="Binding CoilItems">
  <TabControl.ItemTemplate>
    <DataTemplate DataType="models:Coil">
      <StackPanel>
        <TextBlock Text="Binding CoilCode, StringFormat='Coil: 0'" />
        <TextBlock Text="Binding ArticleCode, StringFormat='Auftrag: 0'" />
        <TextBlock Text="Binding RestWeight, StringFormat='Restgewicht: 0 kg'" />
      </StackPanel>
    </DataTemplate>
  </TabControl.ItemTemplate>
  <TabControl.ContentTemplate>
  [...]
  </TabControl.ContentTemplate>
</TabControl>

打开标签的数量在运行时会发生变化。现在我想在每个选项卡中显示一个索引(即第一个选项卡显示“订单 1”,第二个“订单 2”等等)除了每个标题中已经存在的信息。

AFAIK 使用 DataTemplate 时我无法通过代码隐藏访问选项卡属性,那么 XAML 中是否有任何方法可以在选项卡标题内绑定文本块以在选项卡控件中显示该特定选项卡的索引?

我认为RelativeSource 和FindAncestors 应该可以吗?唉,我真的找不到任何关于这些设置的清晰教程(而且我 2 天前才开始使用 WPF)。

【问题讨论】:

如果您可以将models:Coil 对象包装在视图模型中,例如CoilViewModel,那么您可以添加一个名为CoilIndex 的附加属性,这将允许您添加一个绑定到该对象的TextBlock索引,例如&lt;TextBlock Text="Binding CoilIndex, StringFormat='Order 0'" /&gt; 【参考方案1】:

我将为您提供一个使用附加属性的解决方案。检查代码:

附加属性

public static class IndexAttachedProperty



    #region TabItemIndex

    public static int GetTabItemIndex(DependencyObject obj)
    
        return (int) obj.GetValue(TabItemIndexProperty);
    

    public static void SetTabItemIndex(DependencyObject obj, int value)
    
        obj.SetValue(TabItemIndexProperty, value);
    

    // Using a DependencyProperty as the backing store for TabItemIndex.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TabItemIndexProperty =
        DependencyProperty.RegisterAttached("TabItemIndex", typeof (int), typeof (IndexAttachedProperty),
                                            new PropertyMetadata(-1));



    #endregion

    #region TrackTabItemIndex

    public static bool GetTrackTabItemIndex(DependencyObject obj)
    
        return (bool) obj.GetValue(TrackTabItemIndexProperty);
    

    public static void SetTrackTabItemIndex(DependencyObject obj, bool value)
    
        obj.SetValue(TrackTabItemIndexProperty, value);
    

    // Using a DependencyProperty as the backing store for TrackTabItemIndex.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TrackTabItemIndexProperty =
        DependencyProperty.RegisterAttached("TrackTabItemIndex", typeof (bool), typeof (IndexAttachedProperty),
                                            new PropertyMetadata(false, TrackTabItemIndexOnPropertyChanged));

    private static void TrackTabItemIndexOnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    
        var tabControl = GetParent(d, p => p is TabControl) as TabControl;
        var tabItem = GetParent(d, p => p is TabItem) as TabItem;
        if (tabControl == null || tabItem == null)
            return;
        if (!(bool)e.NewValue)
            return;
        int index = tabControl.Items.IndexOf(tabItem.DataContext == null ? tabItem : tabItem.DataContext);
        SetTabItemIndex(d, index);
    
    #endregion





    public static DependencyObject GetParent(DependencyObject item, Func<DependencyObject, bool> condition)
    
        if (item == null)
            return null;
        return condition(item) ? item : GetParent(VisualTreeHelper.GetParent(item), condition);
    

这段代码定义了两个附加属性,第一个是设置一个项目是否跟踪它所包含的选项卡项目索引。第二个是索引属性。

XAML 示例代码:

        <TabControl.ItemTemplate>
            <DataTemplate DataType="x:Type WpfApplication3:A">
                <StackPanel x:Name="tabItemRoot" WpfApplication3:IndexAttachedProperty.TrackTabItemIndex ="True">
                    <TextBlock Text="Binding Text"/>
                    <TextBlock Text="Binding Path=(WpfApplication3:IndexAttachedProperty.TabItemIndex), ElementName=tabItemRoot"/>

                </StackPanel>
            </DataTemplate>
        </TabControl.ItemTemplate>

以上代码是使用附加属性的示例。您可以轻松适应您的代码。

结果:

希望此代码对您有用...

【讨论】:

【参考方案2】:

如果您没有将AlternationCount 属性用于其他目的,您可以破解它以获得更简单的解决方案。

像这样绑定AlternationCount

<TabControl AlternationCount="Binding Path=Items.Count, RelativeSource=RelativeSource Self">

然后在您的 ItemTemplate 中将您希望的 TextBlock 或其他控件绑定到 AlternationIndex 像这样,

<TextBlock Text="Binding Path=(ItemsControl.AlternationIndex), RelativeSource=RelativeSource  FindAncestor, AncestorType=TabItem" />

通过将自定义转换器插入上述绑定,您可以显示任何您想要的内容。

【讨论】:

【参考方案3】:

即使您可以访问代码隐藏中的 TabItem 属性,它也无济于事,因为选项卡不知道它自己在 TabControl 集合中的索引。这对所有ItemsControls 都是正确的,并且看起来很烦人,但这是有道理的,因为任何对象何时可以告诉您它自己在集合中的位置?

但是,只要您可以访问控件的ItemsCollection 和选项卡的内容,就可以使用 IndexOf 来完成。我们可以在 MultiValueConverter 中执行此操作,以便可以在 DataTemplate 中完成。

转换器代码:

   public class ItemsControlIndexConverter : IMultiValueConverter
   
      public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
      
         ItemCollection itemCollection = (ItemCollection)values[0];
         return (itemCollection.IndexOf(values[1]) + 1).ToString();
      

      public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
      
         throw new NotImplementedException();
      
   

TabControl XAML:

<TabControl ItemsSource="Binding CoilItems">
    <TabControl.Resources>
        <local:ItemsControlIndexConverter x:Key="IndexConverter"/>
    </TabControl.Resources>
    <TabControl.ItemTemplate>
         <DataTemplate>
             <StackPanel Orientation="Horizontal">
                 <TextBlock>
                     <TextBlock.Text>
                         <MultiBinding Converter="StaticResource IndexConverter" StringFormat="Order 0" Mode="OneWay">
                             <Binding RelativeSource="RelativeSource AncestorType=TabControl" Path="Items"/> <!-- First converter index is the ItemsCollection -->
                             <Binding /> <!-- Second index is the content of this tab -->
                         </MultiBinding>
                     </TextBlock.Text>
                 </TextBlock>
                 <!-- Fill in the rest of the header template -->
             </StackPanel>
         </DataTemplate>
     </TabControl.ItemTemplate>
 </TabControl>

【讨论】:

以上是关于在 WPF Tabcontrol 标题模板中显示 SelectedIndex的主要内容,如果未能解决你的问题,请参考以下文章

WPF TabControl 标头问题

WPF TabControl在开始时没有选择的项目

WPF TabControl Unload俩次的解决方案

TabControl 怎么每点击一个TabItem 就在同一个Tabcontrol中显示不同的页面?

WPF 自定义控件样式:如tabcontrol等!越多越好!

是否可以在 WPF TabControl 中左对齐标题?