TabItem内容的样式

时间:2016-11-01 18:14:34

标签: c# wpf xaml

我有TabControl用作我应用的主要元素: enter image description here

每个标签内容都有标题(与菜单名称重复),分隔符和内容。我必须与此视图对应的XAML代码应如下所示:

<TabControl Style="{StaticResource MyCustomStyle}">
    <TabItem Header="Menu 1">
        <TabItem.Content>
            ...
        </TabItem.Content>
    </TabItem>
    <TabItem Header="Menu 2">
        <TabItem.Content>
            <TextBlock>Content</TextBlock>
        </TabItem.Content>
    </TabItem>
    ...
</TabControl>

为避免重复部分代码,我决定以自定义样式设置视图。 MyCustomStyle为此:

<Style x:Key="MyCustomStyle" TargetType="{x:Type TabControl}">
    <Style.Resources>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Padding" Value="10"/>
            <Setter Property="Width" Value="120"/>
            <Setter Property="Content">
                <Setter.Value>
                    <StackPanel Margin="10">
                        <Label FontSize="20" Content="..."/>
                        <Separator/>
                        <ContentPresenter ContentSource="..."/>
                    </StackPanel>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
    <Setter Property="TabStripPlacement" Value="Left"/>
</Style>

修改内容时出现唯一的问题。标签不希望将值绑定到TabItem标头。 ContentPresenter

的相同故事

我尝试使用RelativeSource,但这不起作用:

<Label FontSize="20" Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}, Path=Header}"/>

1 个答案:

答案 0 :(得分:2)

如果我理解正确,你就非常接近 - 你只想给它一个ContentTemplate,而不是在风格中设置Content

<Style x:Key="MyCustomStyle" TargetType="{x:Type TabControl}">
    <Style.Resources>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="Padding" Value="10"/>
            <Setter Property="Width" Value="120"/>
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Margin="10">
                            <Label FontSize="20" Content="..."/>
                            <Separator/>
                            <ContentControl 
                                Content="{Binding}"
                                />
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Style.Resources>
    <Setter Property="TabStripPlacement" Value="Left"/>
</Style>

现在,对于Label内容。你不能通过RelativeSource AncestorType得到它,因为TabItem不在可视化树中:如果你写了一个VisualTreeHelper.GetParent()循环,你会发现父链碰到一堆随机的东西,比如网格等等,然后突然出现在TabControl上。

所以我们做的是,我们编写一个多值转换器。我们为DataContext提供DataTemplate - ContentTabItem为ContentTemplated - 和TabControl。然后,我们会查看TabControl的{​​{1}},找到具有相同Items的{​​{1}}。

我尝试将其作为常规值转换器,只需从TabItem传入Content,在转换器内部移动可视树以找到{RelativeSource Self},然后使用Label在转换器内部TabControl标识我想要的Label。由于(我认为)虚拟化,这不起作用:DataContext被实例化一次,并且这些控件被重用。由于TabItem的值每次都相同,而我没有告诉DataTemplate有关{RelativeSource Self}的任何内容,因此仅为第一个Binding调用了值转换器曾被选中。多值转换器通过显式绑定到DataContext TabItem来解决该问题。

如果您使用.填充DataContext,则会中断此操作。事实上,如果你像ItemsSource那样用TabControl填充TabControl,就会破坏它。但是,你不能设置他们的TabItems属性,如果你使用Header,你就会有各种各样的好东西要绑定,你不会考虑坚果的权宜之计像这个。

TabItemHeaderConverter.cs

ItemsSource

新的XAML:

public class TabItemHeaderConverter : IMultiValueConverter
{
    //  This is pretty awful, but nobody promised life would be perfect. 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var tc = values[0] as TabControl;
        var tabItemContent = values[1];

        var tabItem = tc.Items.Cast<TabItem>().FirstOrDefault(ti => ti.Content == tabItemContent);

        if (null != tabItem)
        {
            return tabItem.Header;
        }

        return "Unknown";
    }

    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}