WPF自定义TabControl

发布于 2024-08-29 00:37:11 字数 5079 浏览 5 评论 0 原文

我必须开发一个自定义选项卡控件,并决定使用 WPF/XAML 创建它,因为我无论如何都打算学习它。完成后应该如下所示:

Target

到目前为止,我取得了良好的进展,但还存在两个问题:

  1. 只有第一个/最后一个选项卡项目应该有圆角的左上角/左下角。是否可以修改这些项目的样式,类似于我对所选选项卡项目所做的方式?

  2. 所选选项卡项目的右侧不应有边框。我尝试使用 z-index 和重叠来完成此任务,但结果相当令人失望。还有其他方法可以做到这一点吗?

当前

XAML:

<Window x:Class="MyProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="350" Width="500" Margin="5" Background="LightGray">
<Window.Resources>
    <LinearGradientBrush x:Key="SelectedBorderBrush" StartPoint="0,0" EndPoint="1,0">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="Gray" Offset="0.965"/>
                <GradientStop Color="WhiteSmoke" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>
    <Style TargetType="{x:Type TabControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabControl}">
                    <DockPanel>
                        <Border
                            Panel.ZIndex="50"
                            Margin="0,100,-1,0" 
                            Background="#FFAAAAAA"
                            BorderBrush="Gray"
                            CornerRadius="7,0,0,7"
                            BorderThickness="1">
                            <TabPanel
                                Margin="0,0,0,0"
                                IsItemsHost="True" />
                        </Border>
                        <Border
                            Background="WhiteSmoke"
                            BorderBrush="Gray"
                            BorderThickness="1"
                            CornerRadius="7,7,7,0" >
                            <ContentPresenter 
                                ContentSource="SelectedContent" />
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="{x:Type TabItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid>
                        <Border Name="Border" 
                            Background="#FFAAAAAA"
                            CornerRadius="7,0,0,0"
                            BorderBrush="Gray"
                            BorderThickness="0,0,0,1"
                            Panel.ZIndex="50"
                            Margin="0,0,0,0"
                                >

                            <ContentPresenter x:Name="ContentSite"             
                                VerticalAlignment="Center"
                                HorizontalAlignment="Left"
                                ContentSource="Header"
                                Margin="10,10,10,10"/>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Panel.ZIndex" Value="100" />
                            <Setter Property="Margin" Value="0,0,-2,0" />
                            <Setter TargetName="Border" 
                                    Property="BorderBrush" 
                                    Value="{StaticResource SelectedBorderBrush}"/>
                            <Setter TargetName="Border" 
                                Property="Background" 
                                Value="WhiteSmoke" />
                            <Setter TargetName="Border" 
                                Property="CornerRadius" 
                                Value="0,0,0,0" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <TabControl Name="_menuTabControl" TabStripPlacement="Left" Margin="5">
        <TabItem Name="_tabItem1" Header="First Tab Item" ></TabItem>

        <TabItem Name="_tabItem2" Header="Second Tab Item" >
            <Grid />
        </TabItem>
        <TabItem Name="_tabItem3" Header="Third Tab Item" >
            <Grid />
        </TabItem>
    </TabControl>
</Grid>

编辑:感谢 Vlad,我可以使用渐变边框画笔解决第二个问题。请参阅更新 XAML 以获取解决方案。

编辑:弗拉德解决了第一个问题。

I have to develop a customized tab control and decided to create it with WPF/XAML, because I planned to learn it anyway. It should look like this when it's finished:

Target

I made good progress so far, but there are two issues left:

  1. Only the first/last tab item should have a rounded upper-left/bottom-left corner. Is it possible to modify the style of these items, similar to the way I did with the selected tab item?

  2. The selected tab item should not have a border on its right side. I tried to accomplish this with z-index and overlapping, but the results were rather disappointing. Is there any other way to do this?

Current

XAML:

<Window x:Class="MyProject.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="350" Width="500" Margin="5" Background="LightGray">
<Window.Resources>
    <LinearGradientBrush x:Key="SelectedBorderBrush" StartPoint="0,0" EndPoint="1,0">
        <GradientBrush.GradientStops>
            <GradientStopCollection>
                <GradientStop Color="Gray" Offset="0.965"/>
                <GradientStop Color="WhiteSmoke" Offset="1.0"/>
            </GradientStopCollection>
        </GradientBrush.GradientStops>
    </LinearGradientBrush>
    <Style TargetType="{x:Type TabControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabControl}">
                    <DockPanel>
                        <Border
                            Panel.ZIndex="50"
                            Margin="0,100,-1,0" 
                            Background="#FFAAAAAA"
                            BorderBrush="Gray"
                            CornerRadius="7,0,0,7"
                            BorderThickness="1">
                            <TabPanel
                                Margin="0,0,0,0"
                                IsItemsHost="True" />
                        </Border>
                        <Border
                            Background="WhiteSmoke"
                            BorderBrush="Gray"
                            BorderThickness="1"
                            CornerRadius="7,7,7,0" >
                            <ContentPresenter 
                                ContentSource="SelectedContent" />
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="{x:Type TabItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TabItem}">
                    <Grid>
                        <Border Name="Border" 
                            Background="#FFAAAAAA"
                            CornerRadius="7,0,0,0"
                            BorderBrush="Gray"
                            BorderThickness="0,0,0,1"
                            Panel.ZIndex="50"
                            Margin="0,0,0,0"
                                >

                            <ContentPresenter x:Name="ContentSite"             
                                VerticalAlignment="Center"
                                HorizontalAlignment="Left"
                                ContentSource="Header"
                                Margin="10,10,10,10"/>
                        </Border>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Panel.ZIndex" Value="100" />
                            <Setter Property="Margin" Value="0,0,-2,0" />
                            <Setter TargetName="Border" 
                                    Property="BorderBrush" 
                                    Value="{StaticResource SelectedBorderBrush}"/>
                            <Setter TargetName="Border" 
                                Property="Background" 
                                Value="WhiteSmoke" />
                            <Setter TargetName="Border" 
                                Property="CornerRadius" 
                                Value="0,0,0,0" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <TabControl Name="_menuTabControl" TabStripPlacement="Left" Margin="5">
        <TabItem Name="_tabItem1" Header="First Tab Item" ></TabItem>

        <TabItem Name="_tabItem2" Header="Second Tab Item" >
            <Grid />
        </TabItem>
        <TabItem Name="_tabItem3" Header="Third Tab Item" >
            <Grid />
        </TabItem>
    </TabControl>
</Grid>

Edit: Thanks to Vlad, I could fix the second problem with a gradient border brush. See updates XAML for the solution.

Edit: Vlad fixed the first problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

阳光①夏 2024-09-05 00:37:11

对于第二个问题,您也许应该尝试删除剪辑?但请注意可能的问题

对于第一个问题,您应该尝试在属性 样式触发器 >已选定。 (编辑:我明白了,您正是这样做的。)看看这是如何在默认模板中实现的 MSDN。请注意,他们也使用 ZIndex

编辑
我找到了解决您的第一个/最后一个选项卡问题的方法。您需要使用附加属性来指定第一个/最后一个选项卡:

在 TestWindow 类中定义附加属性:

public static bool GetIsFirstTab(DependencyObject obj)
{
    return (bool)obj.GetValue(IsFirstTabProperty);
}

public static void SetIsFirstTab(DependencyObject obj, bool value)
{
    obj.SetValue(IsFirstTabProperty, value);
}

public static readonly DependencyProperty IsFirstTabProperty =
        DependencyProperty.RegisterAttached("IsFirstTab", typeof(bool),
               typeof(TestWindow), new UIPropertyMetadata(false));

然后,在第一个选项卡中设置此属性:

<Window x:Class="MyProject.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyProject"
        ...
/>
    ...
    <TabItem Name="_tabItem1" Header="First Tab Item"
             local:TestWindow.IsFirstTab="true">
    </TabItem>

然后,您应该为其定义一个触发器:

<Trigger Property="IsSelected" Value="True">
    <Setter TargetName="Border" 
            Property="Background" 
            Value="WhiteSmoke" />
</Trigger>
<Trigger Property="local:Window1.IsFirstTab" Value="True">
    <Setter TargetName="Border" 
            Property="Background" 
            Value="Red" />
</Trigger>

这一定有帮助。

同样的技巧也适用于最后一个选项卡。或者您可以使用数字而不是 bool 作为附加属性。

For the second problem, you should perhaps try to remove the clipping? Beware however of the possible issues.

For the first problem, you should try style trigger on property IsSelected. (Edit: I see, you are doing it exactly this way.) Have a look how this is implemented at the default template at MSDN. Note that they are using ZIndex, too.

Edit:
I found a workaround for your first/last tab problem. You need to use attached properties to designate the first/last tab:

In your TestWindow class you define attached property:

public static bool GetIsFirstTab(DependencyObject obj)
{
    return (bool)obj.GetValue(IsFirstTabProperty);
}

public static void SetIsFirstTab(DependencyObject obj, bool value)
{
    obj.SetValue(IsFirstTabProperty, value);
}

public static readonly DependencyProperty IsFirstTabProperty =
        DependencyProperty.RegisterAttached("IsFirstTab", typeof(bool),
               typeof(TestWindow), new UIPropertyMetadata(false));

Then, in your first tab you set this property:

<Window x:Class="MyProject.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyProject"
        ...
/>
    ...
    <TabItem Name="_tabItem1" Header="First Tab Item"
             local:TestWindow.IsFirstTab="true">
    </TabItem>

Then, you should define a trigger for it:

<Trigger Property="IsSelected" Value="True">
    <Setter TargetName="Border" 
            Property="Background" 
            Value="WhiteSmoke" />
</Trigger>
<Trigger Property="local:Window1.IsFirstTab" Value="True">
    <Setter TargetName="Border" 
            Property="Background" 
            Value="Red" />
</Trigger>

This must help.

The same trick would work with last tab. Or you can have a number instead of bool as attached property.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文