WPF TabControl 滚动选项卡

前言

我原本以为是很简单的事情,但是没想到实际做起来还是有很多的基础知识点的。

相关链接

WPF 控件库——可拖动选项卡的TabControl

TabControl是什么东西

我们平常写TabControl的时候,可能都很习惯了直接写TabControl+TabItem。但是TabControl负责了什么布局,TabItem负责了什么布局,我们都不知道。

在《深入浅出WPF》中,我们可以看到TabControl属于ItemsControl

在这里插入图片描述

通过模板副本查看样式

我们去看看控件模板样式副本。WPF的xaml的优点是每个控件都是单独的逻辑,耦合低。缺点是写起来麻烦,每次改动约等于重新写一个新的。通过增加自己的工作量来降低了耦合

在这里插入图片描述

    <SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
    <SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
    <Style x:Key="TabControlStyle1" TargetType="{x:Type TabControl}">
        <!--默认基础属性-->
        <Setter Property="Padding" Value="2"/>
        <Setter Property="HorizontalContentAlignment" Value="Center"/>
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
        <Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <!--控件模板-->
        <Setter Property="Template">
            <Setter.Value>
                <!--选择控件模板属性模板-->
                <ControlTemplate TargetType="{x:Type TabControl}">
                    <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition x:Name="ColumnDefinition0"/>
                            <ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
                            <RowDefinition x:Name="RowDefinition1" Height="*"/>
                        </Grid.RowDefinitions>
                        <!--设置TabItem选项卡的布局-->
                        <TabPanel x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
                        <!--设置TabItem的内容-->
                        <Border x:Name="contentPanel" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
                            <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </Grid>
                    <!--用于设置位置布局-->
                    <ControlTemplate.Triggers>
                        <Trigger Property="TabStripPlacement" Value="Bottom">
                            <Setter Property="Grid.Row" TargetName="headerPanel" Value="1"/>
                            <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                            <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                            <Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
                            <Setter Property="Margin" TargetName="headerPanel" Value="2,0,2,2"/>
                        </Trigger>
                        <Trigger Property="TabStripPlacement" Value="Left">
                            <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                            <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                            <Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
                            <Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
                            <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
                            <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
                            <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                            <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                            <Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
                        </Trigger>
                        <Trigger Property="TabStripPlacement" Value="Right">
                            <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                            <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                            <Setter Property="Grid.Column" TargetName="headerPanel" Value="1"/>
                            <Setter Property="Grid.Column" TargetName="contentPanel" Value="0"/>
                            <Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
                            <Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
                            <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                            <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                            <Setter Property="Margin" TargetName="headerPanel" Value="0,2,2,2"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

我们可以看到中间有个Gird分布,用于声明选项卡和内容之间布局关系。那么哪个代表选项卡,哪个代码【内容】呢?

在这里插入图片描述

IsItemsHost

其实选项卡布局还有一个要求,就是得声明【IsItemsHost】这个属性,我们可以看看微软官方文档是怎么说的。

Panel.IsItemsHost 属性

在这里插入图片描述

简单来说【IsItemsHost】就是一个可以自动绑定的ItemPanel的声明,ItemPanel是设置ItemControl的布局方式。

如何修改

其实改起来很简单,就是加个【ScrollViewer】就行了。原来的是Panel,我们改成StackPanel这样就不会自动换行了。

<Window x:Class="WpfApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>
        <SolidColorBrush x:Key="TabItem.Selected.Background"
                         Color="#FFFFFF" />
        <SolidColorBrush x:Key="TabItem.Selected.Border"
                         Color="#ACACAC" />
        <Style x:Key="TabControlStyle1"
               TargetType="{x:Type TabControl}">
            <!--默认基础属性-->
            <Setter Property="Padding"
                    Value="2" />
            <Setter Property="HorizontalContentAlignment"
                    Value="Center" />
            <Setter Property="VerticalContentAlignment"
                    Value="Center" />
            <Setter Property="Background"
                    Value="{StaticResource TabItem.Selected.Background}" />
            <Setter Property="BorderBrush"
                    Value="{StaticResource TabItem.Selected.Border}" />
            <Setter Property="BorderThickness"
                    Value="1" />
            <Setter Property="Foreground"
                    Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
            <!--控件模板-->
            <Setter Property="Template">
                <Setter.Value>
                    <!--选择控件模板属性模板-->
                    <ControlTemplate TargetType="{x:Type TabControl}">
                        <Grid x:Name="templateRoot"
                              ClipToBounds="true"
                              SnapsToDevicePixels="true"
                              KeyboardNavigation.TabNavigation="Local">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition x:Name="ColumnDefinition0" />
                                <ColumnDefinition x:Name="ColumnDefinition1"
                                                  Width="0" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition x:Name="RowDefinition0"
                                               Height="Auto" />
                                <RowDefinition x:Name="RowDefinition1"
                                               Height="*" />
                            </Grid.RowDefinitions>
                            <!--设置TabItem选项卡的布局-->
                            <ScrollViewer Height="{TemplateBinding Height}" >
                                <StackPanel x:Name="headerPanel"
                                            Background="Transparent"
                                            Grid.Column="0"
                                            IsItemsHost="True"
                                            Margin="2,2,2,0"
                                            Grid.Row="0"
                                            KeyboardNavigation.TabIndex="1"
                                            Panel.ZIndex="1" />
                            </ScrollViewer>
                         
                            <!--设置TabItem的内容-->
                            <Border x:Name="contentPanel"
                                    Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    Grid.Column="0"
                                    KeyboardNavigation.DirectionalNavigation="Contained"
                                    Grid.Row="1"
                                    KeyboardNavigation.TabIndex="2"
                                    KeyboardNavigation.TabNavigation="Local">
                                <ContentPresenter x:Name="PART_SelectedContentHost"
                                                  ContentSource="SelectedContent"
                                                  Margin="{TemplateBinding Padding}"
                                                  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </Border>
                        </Grid>
                        <!--用于设置位置布局-->
                        <ControlTemplate.Triggers>
                            <Trigger Property="TabStripPlacement"
                                     Value="Bottom">
                                <Setter Property="Grid.Row"
                                        TargetName="headerPanel"
                                        Value="1" />
                                <Setter Property="Grid.Row"
                                        TargetName="contentPanel"
                                        Value="0" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition0"
                                        Value="*" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition1"
                                        Value="Auto" />
                                <Setter Property="Margin"
                                        TargetName="headerPanel"
                                        Value="2,0,2,2" />
                            </Trigger>
                            <Trigger Property="TabStripPlacement"
                                     Value="Left">
                                <Setter Property="Grid.Row"
                                        TargetName="headerPanel"
                                        Value="0" />
                                <Setter Property="Grid.Row"
                                        TargetName="contentPanel"
                                        Value="0" />
                                <Setter Property="Grid.Column"
                                        TargetName="headerPanel"
                                        Value="0" />
                                <Setter Property="Grid.Column"
                                        TargetName="contentPanel"
                                        Value="1" />
                                <Setter Property="Width"
                                        TargetName="ColumnDefinition0"
                                        Value="Auto" />
                                <Setter Property="Width"
                                        TargetName="ColumnDefinition1"
                                        Value="*" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition0"
                                        Value="*" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition1"
                                        Value="0" />
                                <Setter Property="Margin"
                                        TargetName="headerPanel"
                                        Value="2,2,0,2" />
                            </Trigger>
                            <Trigger Property="TabStripPlacement"
                                     Value="Right">
                                <Setter Property="Grid.Row"
                                        TargetName="headerPanel"
                                        Value="0" />
                                <Setter Property="Grid.Row"
                                        TargetName="contentPanel"
                                        Value="0" />
                                <Setter Property="Grid.Column"
                                        TargetName="headerPanel"
                                        Value="1" />
                                <Setter Property="Grid.Column"
                                        TargetName="contentPanel"
                                        Value="0" />
                                <Setter Property="Width"
                                        TargetName="ColumnDefinition0"
                                        Value="*" />
                                <Setter Property="Width"
                                        TargetName="ColumnDefinition1"
                                        Value="Auto" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition0"
                                        Value="*" />
                                <Setter Property="Height"
                                        TargetName="RowDefinition1"
                                        Value="0" />
                                <Setter Property="Margin"
                                        TargetName="headerPanel"
                                        Value="0,2,2,2" />
                            </Trigger>
                            <Trigger Property="IsEnabled"
                                     Value="false">
                                <Setter Property="TextElement.Foreground"
                                        TargetName="templateRoot"
                                        Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="TabItem"
               x:Key="default">
            <Setter Property="FontSize"
                    Value="50" />
        </Style>
    </Window.Resources>
    <Grid>
        <TabControl Style="{DynamicResource TabControlStyle1}" TabStripPlacement="Left">
            <TabItem Header="标题1"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题2"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题3"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题4"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题5"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题6"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题7"
                     Style="{StaticResource default}">

            </TabItem>
            <TabItem Header="标题8"
                     Style="{StaticResource default}">

            </TabItem>
        </TabControl>
    </Grid>
</Window>

在这里插入图片描述

HandyControl的TabControl 滚动

我们看看HandyControl的代码

在这里插入图片描述
我们进到代码里面,可以看到他实现了ContentControl,这里我们就不展开说明了

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

总结

控件模板是必须要深入了解的。WPF我认为就3+1个难点。控件模板,依赖属性,Binding+WPF 动画。前三个掌握了,基本的静态页面就可以写了。动画属于附加需求,不用动画也可以写的很好看

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: WPF(Windows Presentation Foundation)是一种用于创建窗口应用程序的框架。TabControl选项卡控件)是WPF中可以用于组织和切换多个界面内容的重要控件。 TabControl通常由多个TabItem(选项卡项)组成,每个TabItem代表一个选项卡。可以通过给TabItem的Header属性设置文本或其他可视元素来定义选项卡的标题。当用户在界面上点击某个选项卡时,该选项卡的内容会显示在TabControl的内容区域中。 选项卡控件的优点之一是提供了一种简洁的界面布局方式,可以在有限的空间中展示大量的信息。通过在TabControl中添加多个选项卡,可以实现多个相关的界面内容的切换,使用户可以方便地在不同的内容之间进行导航。 TabControl还可以通过设置SelectedIndex或SelectedValue属性来指定默认显示的选项卡,或者通过代码动态地改变当前选中的选项卡。此外,还可以通过设置TabControl的样式和模板来自定义选项卡的外观和行为,以满足不同的需求。 总之,WPF中的TabControl选项卡控件可以用来组织和切换多个界面内容,具有简洁的界面布局方式和丰富的自定义功能,是开发窗口应用程序时常用的控件之一。 ### 回答2: WPF中的TabControl选项卡是一种常用的界面元素,用于在单个窗口或页面上显示多个相关内容的切换。 TabControl由标签项(TabItem)和内容项(Content)组成。每个标签项都有一个标题,用户可以通过单击标签项来切换到相应的内容项。通常情况下,标签项将显示在TabControl控件的顶部或侧边,内容项将显示在TabControl控件的主体部分。 可以通过ItemsSource属性将TabControl绑定到一个集合对象,每个集合项将作为一个标签项显示在TabControl中。这样可以方便地通过添加或删除集合项来动态地添加或移除标签项。另外,还可以通过SelectedIndex或SelectedValue属性来确定默认显示的标签项。 每个标签项都需要一个内容项与之对应,可以通过Content属性将标签项与内容项关联起来。内容项可以是一个单独的界面元素,也可以是一个完整的用户控件。 TabControl还提供了一些其他常用的属性和事件,用于设置和响应Tab项的选择状态、样式和行为。例如,可以通过IsEnabled属性来禁用某个标签项,可以通过SelectionChanged事件来处理用户选择不同标签项的操作。 总体而言,WPF中的TabControl选项卡是一种简洁、灵活且易用的界面元素,可以帮助开发者实现多页显示和内容切换功能,提高用户体验和界面交互的便利性。 ### 回答3: WPF中的TabControl是一个常用的控件,用于创建具有多个选项卡的用户界面。它允许用户在不同的选项卡之间切换,并显示当前选中选项卡的内容。 TabControl控件通常包含一个或多个TabItem子控件,每个TabItem代表一个选项卡。每个TabItem通常包含一个Header属性,用于显示在选项卡上的标题。当用户点击不同的选项卡时,TabControl会自动切换到对应的TabItem,显示该选项卡的内容。 TabControl还允许通过添加其他控件或面板作为TabItem的内容来定制每个选项卡的显示。这意味着可以在每个选项卡中添加任意的WPF控件,并根据需要进行布局和设计。 使用TabControl时,可以通过编程方式控制选项卡的切换。可以通过SelectedIndex属性设置当前选中的选项卡索引,或者通过SelectedContent属性直接设置选中的TabItem。此外,还可以通过SelectedIndexChanged事件来监测选项卡的切换。 TabControl还提供了许多附加功能,例如设置选项卡的可见性、禁用或启用选项卡等。可以通过样式和模板来自定义选项卡的外观和行为。 综上所述,WPFPTabControl选项卡是一个非常有用的控件,可以方便地创建多选项卡的用户界面,并且提供了丰富的功能和自定义选项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值