一、查看TabControl原样式
<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> <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"/> <Border x:Name="contentPanel" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabNavigation="Local" KeyboardNavigation.TabIndex="2"> <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>
分析原有样式
由上图可知TabControl由TabPanel和ContentPresenter两部分组成,并被一个两行两列的Grid包裹
分析上图触发器部分,TabStripPlacement控制着TabPanel和ContentPresenter 在网格中的位置
接着查看TabItem原有样式
<ControlTemplate TargetType="{x:Type TabItem}"> <Grid x:Name="templateRoot" SnapsToDevicePixels="true"> <Border x:Name="mainBorder" Background="{TemplateBinding Background}" BorderThickness="1,1,1,0" BorderBrush="{TemplateBinding BorderBrush}" Margin="0"> <Border x:Name="innerBorder" Background="{StaticResource TabItem.Selected.Background}" BorderThickness="1,1,1,0" BorderBrush="{StaticResource TabItem.Selected.Border}" Margin="-1" Opacity="0"/> </Border> <ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> </Grid> <ControlTemplate.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/> </MultiDataTrigger.Conditions> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.MouseOver.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/> </MultiDataTrigger.Conditions> <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/> </MultiDataTrigger.Conditions> <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/> </MultiDataTrigger.Conditions> <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/> </MultiDataTrigger.Conditions> <Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/> <Setter Property="Background" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Background}"/> <Setter Property="BorderBrush" TargetName="mainBorder" Value="{StaticResource TabItem.Disabled.Border}"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/> </MultiDataTrigger.Conditions> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/> </MultiDataTrigger.Conditions> <Setter Property="Panel.ZIndex" Value="1"/> <Setter Property="Margin" Value="-2,-2,0,-2"/> <Setter Property="Opacity" TargetName="innerBorder" Value="1"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/> </MultiDataTrigger.Conditions> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/> </MultiDataTrigger.Conditions> <Setter Property="Panel.ZIndex" Value="1"/> <Setter Property="Margin" Value="-2,0,-2,-2"/> <Setter Property="Opacity" TargetName="innerBorder" Value="1"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/> </MultiDataTrigger.Conditions> <Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/> </MultiDataTrigger.Conditions> <Setter Property="Panel.ZIndex" Value="1"/> <Setter Property="Margin" Value="0,-2,-2,-2"/> <Setter Property="Opacity" TargetName="innerBorder" Value="1"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/> </MultiDataTrigger.Conditions> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/> <Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/> </MultiDataTrigger.Conditions> <Setter Property="Panel.ZIndex" Value="1"/> <Setter Property="Margin" Value="-2,-2,-2,0"/> <Setter Property="Opacity" TargetName="innerBorder" Value="1"/> <Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/> <Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/> </MultiDataTrigger> </ControlTemplate.Triggers> </ControlTemplate>
TabItem由ContentPresenter和两个Border组成(内外边框)
二、优化原有样式
去除内边框并简化触发器
<Style x:Key="MTabControl.TabItemStyle" TargetType="{x:Type TabItem}"> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MTabControl}},Path=HeaderForeground}"/> <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MTabControl}},Path=HeaderBorderBrush}"/> <Setter Property="Background" Value="{DynamicResource ColorBrush.TabControlHeaderBackground}"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="MinWidth" Value="100"/> <Setter Property="MinHeight" Value="40"/> <Setter Property="FontSize" Value="16"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TabItem}"> <Grid SnapsToDevicePixels="true"> <Grid SnapsToDevicePixels="true"> <Border x:Name="PART_Border" Background="{TemplateBinding Background}" BorderThickness="0,0,0,2" BorderBrush="{TemplateBinding BorderBrush}" Padding="{TemplateBinding Padding}"> <ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> </Border> </Grid> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsSelected" Value="true"> <Setter Property="Panel.ZIndex" Value="1"/> <Setter Property="Background" TargetName="PART_Border" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MTabControl}},Path=HeaderBackground}"/> <Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontPrimaryColor}"/> </Trigger> <Trigger Property="TabStripPlacement" Value="Bottom"> <Setter Property="BorderThickness" TargetName="PART_Border" Value="0,2,0,0"/> </Trigger> <Trigger Property="TabStripPlacement" Value="Left"> <Setter Property="BorderThickness" TargetName="PART_Border" Value="0,0,2,0"/> </Trigger> <Trigger Property="TabStripPlacement" Value="Right"> <Setter Property="BorderThickness" TargetName="PART_Border" Value="2,0,0,0"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type local:MTabControl}"> <Setter Property="Foreground" Value="{DynamicResource ColorBrush.FontDefaultColor}"/> <Setter Property="Padding" Value="0"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="BorderBrush" Value="{DynamicResource ColorBrush.PrimaryBorderBrushColor}"/> <Setter Property="Background" Value="{DynamicResource ColorBrush.FontDefaultColorV3}"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="ItemContainerStyle" Value="{StaticResource MTabControl.TabItemStyle}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MTabControl}"> <Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" > <Grid 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> <UniformGrid x:Name="HeaderPanel" HorizontalAlignment="Stretch" IsItemsHost="true" Rows="1" Panel.ZIndex="1" /> <Border x:Name="ContentPanel" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Grid.Column="0" Grid.Row="1" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabNavigation="Local" KeyboardNavigation.TabIndex="2"> <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> </Border> </Grid> </Border> <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"/> </Trigger> <Trigger Property="TabStripPlacement" Value="Left"> <Setter Property="Columns" TargetName="HeaderPanel" Value="1"/> <Setter Property="Rows" TargetName="HeaderPanel" Value="0"/> <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"/> </Trigger> <Trigger Property="TabStripPlacement" Value="Right"> <Setter Property="Columns" TargetName="HeaderPanel" Value="1"/> <Setter Property="Rows" TargetName="HeaderPanel" Value="0"/> <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"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
三、源码地址
gitee地址:https://gitee.com/sirius_machao/Cys_Controls/tree/dev/