WPF自定义控件

news2024/11/16 1:53:13

控件模板

顾名思义就是在原有的控件上进行模版修改成自己需要的样式

把ProgressBar修改为一个水液面的进度条

<Window x:Class="XH.CustomLesson.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:XH.CustomLesson"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF06B025"/>
        <SolidColorBrush x:Key="ProgressBar.Background" Color="#FFE6E6E6"/>
        <SolidColorBrush x:Key="ProgressBar.Border" Color="#FFBCBCBC"/>
        <Style x:Key="ProgressBarStyle" TargetType="{x:Type ProgressBar}">
            <Setter Property="Foreground" Value="{StaticResource ProgressBar.Progress}"/>
            <Setter Property="Background" Value="{StaticResource ProgressBar.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ProgressBar.Border}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ProgressBar}">
                        <Grid x:Name="TemplateRoot">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Determinate"/>
                                    <VisualState x:Name="Indeterminate">
                                        <Storyboard RepeatBehavior="Forever">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Animation" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                                                <EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
                                                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
                                                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <PointAnimationUsingKeyFrames Storyboard.TargetName="Animation" Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)">
                                                <EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
                                                <EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
                                                <EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
                                            </PointAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="{TemplateBinding BorderThickness}"
                                    CornerRadius="5"/>
                            <Rectangle x:Name="PART_Track"/>
                            <Grid x:Name="PART_Indicator" ClipToBounds="True" HorizontalAlignment="Left">
                                <Border x:Name="Indicator" Background="Transparent">
                                    <Canvas>
                                        <Canvas.LayoutTransform>
                                            <RotateTransform Angle="90" />
                                        </Canvas.LayoutTransform>
                                        <Path Data="M0 0
                                                    A70 70 0 0 0 50 0
                                                    A70 70 0 0 1 100 0
                                                    L 100 100 0 100" Fill="Orange">
                                            <Path.RenderTransform>
                                                <TranslateTransform X="-30" x:Name="tt" />
                                            </Path.RenderTransform>
                                        </Path>
                                        <Path Data="M0 0
                                                    A70 70 0 0 0 50 0
                                                    A70 70 0 0 1 100 0
                                                    L 100 100 0 100" Fill="#9f90">
                                            <Path.RenderTransform>
                                                <TranslateTransform X="0" x:Name="tt_2" />
                                            </Path.RenderTransform>
                                        </Path>

                                        <Path Data="M0 0
                                                    A70 70 0 0 0 50 0
                                                    A70 70 0 0 1 100 0
                                                    L100 100 0 100" Fill="#9f80">
                                            <Path.RenderTransform>
                                                <TranslateTransform X="-50" x:Name="tt_3" />
                                            </Path.RenderTransform>
                                        </Path>
                                    </Canvas>
                                </Border>
                            </Grid>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="Orientation" Value="Vertical">
                                <Setter Property="LayoutTransform" TargetName="TemplateRoot">
                                    <Setter.Value>
                                        <RotateTransform Angle="-90"/>
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="IsIndeterminate" Value="true">
                                <Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
                            </Trigger>
                            <EventTrigger RoutedEvent="Loaded">
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation RepeatBehavior="Forever" Duration="0:0:1" From="0" To="-50" Storyboard.TargetName="tt" Storyboard.TargetProperty="X"/>
                                        <DoubleAnimation RepeatBehavior="Forever" BeginTime="0:0:0.3" Duration="0:0:1.3" From="0" To="-50" Storyboard.TargetName="tt_2" Storyboard.TargetProperty="X"/>
                                        <DoubleAnimation RepeatBehavior="Forever" BeginTime="0:0:0.6" Duration="0:0:1.6" From="-50" To="0" Storyboard.TargetName="tt_3" Storyboard.TargetProperty="X"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ProgressBar Style="{DynamicResource ProgressBarStyle}"
                     BorderThickness="0"
                     Height="200" Width="50" Orientation="Vertical" 
                     Value="450" Maximum="750" Minimum="0"/>
    </Grid>
</Window>

想要弧度更大,修改对应的path即可:

UserControl 用户控件

案例:日期时间选择器 DatePicker

XAML代码:

<UserControl x:Class="XH.CustomLesson.Controls.DateTimePicker"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XH.CustomLesson.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="200">
    <UserControl.Resources>
        <Style TargetType="RepeatButton" x:Key="ButtonUpStyle">
            <Setter Property="Height" Value="18"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RepeatButton">
                        <Border Background="Transparent">
                            <Path Data="M838.116 732.779 877.7 693.195 511.979 327.549 146.3 693.195 185.883 732.779 512.003 406.652Z"
                          Fill="#999" Stretch="Uniform" Margin="6"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="RepeatButton" x:Key="ButtonDownStyle">
            <Setter Property="Height" Value="18"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="RepeatButton">
                        <Border Background="Transparent">
                            <Path Data="M185.884 327.55 146.3 367.133 512.021 732.779 877.7 367.133 838.117 327.55 511.997 653.676Z"
                          Fill="#999" Stretch="Uniform" Margin="6"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="Button" x:Key="ButtonCancelStyle">
            <Setter Property="Width" Value="24"/>
            <Setter Property="Height" Value="24"/>
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="#EEE">
                            <Path Data="M557.3 512l329.3-329.4a32 32 0 1 0-45.2-45.2L512 466.7 182.6 137.4a32 32 0 1 0-45.2 45.2L466.7 512 137.4 841.4a31.9 31.9 0 0 0 0 45.2 31.9 31.9 0 0 0 45.2 0L512 557.3l329.4 329.3a31.9 31.9 0 0 0 45.2 0 31.9 31.9 0 0 0 0-45.2z"
                          Fill="#999" Stretch="Uniform" HorizontalAlignment="Center" Margin="5"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="Button" x:Key="ButtonAcceptStyle">
            <Setter Property="Width" Value="24"/>
            <Setter Property="Height" Value="24"/>
            <Setter Property="HorizontalAlignment" Value="Right"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="#EEE">
                            <Path Data="M892.064 261.888a31.936 31.936 0 0 0-45.216 1.472L421.664 717.248l-220.448-185.216a32 32 0 1 0-41.152 48.992l243.648 204.704a31.872 31.872 0 0 0 20.576 7.488 31.808 31.808 0 0 0 23.36-10.112L893.536 307.136a32 32 0 0 0-1.472-45.248z"
                          Fill="#999" Stretch="Uniform" HorizontalAlignment="Center" Margin="5"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>


        <Style x:Key="CalendarCalendarDayButtonStyle1" TargetType="{x:Type CalendarDayButton}">
            <Setter Property="MinWidth" Value="10"/>
            <Setter Property="MinHeight" Value="10"/>
            <Setter Property="FontSize" Value="13"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CalendarDayButton}">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0:0:0.1"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="HighlightBackground" To="0.5" Storyboard.TargetProperty="Opacity"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="HighlightBackground" To="0.5" Storyboard.TargetProperty="Opacity"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="HighlightBackground" To="0" Storyboard.TargetProperty="Opacity"/>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="NormalText" To=".35" Storyboard.TargetProperty="Opacity"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="SelectionStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="Unselected"/>
                                    <VisualState x:Name="Selected">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="SelectedBackground" To=".75" Storyboard.TargetProperty="Opacity"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="NormalText" To="#FFFFFFFF" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="CalendarButtonFocusStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="CalendarButtonFocused">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="DayButtonFocusVisual" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="CalendarButtonUnfocused">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="DayButtonFocusVisual" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ActiveStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="Active"/>
                                    <VisualState x:Name="Inactive">
                                        <Storyboard>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="NormalText" To="#FF777777" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="DayStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="RegularDay"/>
                                    <VisualState x:Name="Today">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="TodayBackground" To="1" Storyboard.TargetProperty="Opacity"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="NormalText" To="#FFFFFFFF" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="BlackoutDayStates">
                                    <VisualStateGroup.Transitions>
                                        <VisualTransition GeneratedDuration="0"/>
                                    </VisualStateGroup.Transitions>
                                    <VisualState x:Name="NormalDay"/>
                                    <VisualState x:Name="BlackoutDay">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="Blackout" To=".2" Storyboard.TargetProperty="Opacity"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Rectangle x:Name="TodayBackground" Fill="Gray" Opacity="0" RadiusX="1" RadiusY="1"/>
                            <Rectangle x:Name="SelectedBackground" Fill="#409EFE" Opacity="0" RadiusX="1" RadiusY="1"/>
                            <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
                            <Rectangle x:Name="HighlightBackground" Fill="#FFBADDE9" Opacity="0" RadiusX="1" RadiusY="1"/>
                            <ContentPresenter x:Name="NormalText" TextElement.Foreground="#FF333333" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="5,1,5,1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            <Path x:Name="Blackout" Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" Fill="#FF000000" HorizontalAlignment="Stretch" Margin="3" Opacity="0" RenderTransformOrigin="0.5,0.5" Stretch="Fill" VerticalAlignment="Stretch"/>
                            <Rectangle x:Name="DayButtonFocusVisual" IsHitTestVisible="false" RadiusX="1" RadiusY="1" Stroke="#FF45D6FA" Visibility="Collapsed"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="CalendarCalendarItemStyle1" TargetType="{x:Type CalendarItem}">
            <Setter Property="Margin" Value="0,3,0,3"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CalendarItem}">
                        <ControlTemplate.Resources>
                            <DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
                                <TextBlock Foreground="#FF333333" FontFamily="Verdana" FontWeight="Bold" FontSize="9.5" HorizontalAlignment="Center" Margin="0,6,0,6" Text="{Binding}" VerticalAlignment="Center"/>
                            </DataTemplate>
                        </ControlTemplate.Resources>
                        <Grid x:Name="PART_Root">
                            <Grid.Resources>
                                <SolidColorBrush x:Key="DisabledColor" Color="#A5FFFFFF"/>
                            </Grid.Resources>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="PART_DisabledVisual" To="1" Storyboard.TargetProperty="Opacity"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1">
                                <Border BorderBrush="#FFFFFFFF" BorderThickness="0" CornerRadius="1">
                                    <Grid>
                                        <Grid.Resources>
                                            <ControlTemplate x:Key="PreviousButtonTemplate" TargetType="{x:Type Button}">
                                                <Grid Cursor="Hand">
                                                    <VisualStateManager.VisualStateGroups>
                                                        <VisualStateGroup x:Name="CommonStates">
                                                            <VisualState x:Name="Normal"/>
                                                            <VisualState x:Name="MouseOver">
                                                                <Storyboard>
                                                                    <ColorAnimation Duration="0" Storyboard.TargetName="path" To="#FF73A9D8" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                            <VisualState x:Name="Disabled">
                                                                <Storyboard>
                                                                    <DoubleAnimation Duration="0" Storyboard.TargetName="path" To=".5" Storyboard.TargetProperty="(Shape.Fill).(Brush.Opacity)"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                        </VisualStateGroup>
                                                    </VisualStateManager.VisualStateGroups>
                                                    <Grid Background="Transparent">
                                                        <Path x:Name="path" Data="M671.968176 911.99957c-12.287381 0-24.576482-4.67206-33.951566-14.047144L286.048434 545.984249c-18.751888-18.719204-18.751888-49.12028 0-67.872168L638.016611 126.111222c18.751888-18.751888 49.12028-18.751888 67.872168 0 18.751888 18.719204 18.751888 49.12028 0 67.872168l-318.016611 318.047574L705.888778 830.047574c18.751888 18.751888 18.751888 49.12028 0 67.872168C696.544658 907.32751 684.255557 911.99957 671.968176 911.99957z" 
                                                              Fill="#FF333333" HorizontalAlignment="Center"  VerticalAlignment="Center"
                                                              Height="10" Width="6" Stretch="Fill" Margin="10,0"/>
                                                    </Grid>
                                                </Grid>
                                            </ControlTemplate>
                                            <ControlTemplate x:Key="NextButtonTemplate" TargetType="{x:Type Button}">
                                                <Grid Cursor="Hand">
                                                    <VisualStateManager.VisualStateGroups>
                                                        <VisualStateGroup x:Name="CommonStates">
                                                            <VisualState x:Name="Normal"/>
                                                            <VisualState x:Name="MouseOver">
                                                                <Storyboard>
                                                                    <ColorAnimation Duration="0" Storyboard.TargetName="path" To="#FF73A9D8" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                            <VisualState x:Name="Disabled">
                                                                <Storyboard>
                                                                    <DoubleAnimation Duration="0" Storyboard.TargetName="path" To=".5" Storyboard.TargetProperty="(Shape.Fill).(Brush.Opacity)"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                        </VisualStateGroup>
                                                    </VisualStateManager.VisualStateGroups>
                                                    <Grid Background="Transparent">
                                                        <Path x:Name="path" Data="M761.055557 532.128047c0.512619-0.992555 1.343475-1.823411 1.792447-2.848649 8.800538-18.304636 5.919204-40.703346-9.664077-55.424808L399.935923 139.743798c-19.264507-18.208305-49.631179-17.344765-67.872168 1.888778-18.208305 19.264507-17.375729 49.631179 1.888778 67.872168l316.960409 299.839269L335.199677 813.631716c-19.071845 18.399247-19.648112 48.767639-1.247144 67.872168 9.407768 9.791372 21.984142 14.688778 34.560516 14.688778 12.000108 0 24.000215-4.479398 33.311652-13.439914l350.048434-337.375729c0.672598-0.672598 0.927187-1.599785 1.599785-2.303346 0.512619-0.479935 1.056202-0.832576 1.567101-1.343475C757.759656 538.879828 759.199462 535.391265 761.055557 532.128047z" 
                                                              Fill="#FF333333" HorizontalAlignment="Right" VerticalAlignment="Center" 
                                                              Height="10" Stretch="Fill" Width="6" Margin="10,0"/>
                                                    </Grid>
                                                </Grid>
                                            </ControlTemplate>
                                            <ControlTemplate x:Key="HeaderButtonTemplate" TargetType="{x:Type Button}">
                                                <Grid Cursor="Hand" Background="Transparent">
                                                    <VisualStateManager.VisualStateGroups>
                                                        <VisualStateGroup x:Name="CommonStates">
                                                            <VisualState x:Name="Normal"/>
                                                            <VisualState x:Name="MouseOver">
                                                                <Storyboard>
                                                                    <ColorAnimation Duration="0" Storyboard.TargetName="buttonContent" To="#FF73A9D8" Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                            <VisualState x:Name="Disabled">
                                                                <Storyboard>
                                                                    <DoubleAnimation Duration="0" Storyboard.TargetName="buttonContent" To=".5" Storyboard.TargetProperty="Opacity"/>
                                                                </Storyboard>
                                                            </VisualState>
                                                        </VisualStateGroup>
                                                    </VisualStateManager.VisualStateGroups>
                                                    <ContentPresenter x:Name="buttonContent" ContentTemplate="{TemplateBinding ContentTemplate}" 
                                                                      Content="{TemplateBinding Content}" TextElement.Foreground="#FF333333"
                                                                      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                                                      Margin="1,9" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                                </Grid>
                                            </ControlTemplate>
                                        </Grid.Resources>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="auto"/>
                                            <ColumnDefinition Width="auto"/>
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                            <RowDefinition Height="*"/>
                                        </Grid.RowDefinitions>
                                        <Button x:Name="PART_HeaderButton" Grid.Column="0" Focusable="False" FontWeight="Bold" FontSize="14" HorizontalAlignment="Left" 
                                                VerticalContentAlignment="Center"
                                                Grid.Row="0" Template="{StaticResource HeaderButtonTemplate}" VerticalAlignment="Center" Margin="10,5"/>

                                        <Button x:Name="PART_PreviousButton" Grid.Column="1" Focusable="False" HorizontalAlignment="Center" 
                                                Template="{StaticResource PreviousButtonTemplate}" Margin="0,5"/>
                                        <Button x:Name="PART_NextButton" Grid.Column="2" Focusable="False" HorizontalAlignment="Right" 
                                                Template="{StaticResource NextButtonTemplate}" Margin="0,5"/>

                                        <Grid x:Name="PART_MonthView" Grid.ColumnSpan="3" HorizontalAlignment="Center" Margin="6,-1,6,6" Grid.Row="1" Visibility="Visible">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                                <ColumnDefinition Width="30"/>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto"/>
                                                <RowDefinition Height="30"/>
                                                <RowDefinition Height="30"/>
                                                <RowDefinition Height="30"/>
                                                <RowDefinition Height="30"/>
                                                <RowDefinition Height="30"/>
                                                <RowDefinition Height="30"/>
                                            </Grid.RowDefinitions>
                                        </Grid>
                                        <Grid x:Name="PART_YearView" Grid.ColumnSpan="3" HorizontalAlignment="Center" Margin="6,-3,7,6" Grid.Row="1" Visibility="Hidden">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="50"/>
                                                <ColumnDefinition Width="50"/>
                                                <ColumnDefinition Width="50"/>
                                                <ColumnDefinition Width="50"/>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="50"/>
                                                <RowDefinition Height="50"/>
                                                <RowDefinition Height="50"/>
                                            </Grid.RowDefinitions>
                                        </Grid>
                                    </Grid>
                                </Border>
                            </Border>
                            <Rectangle x:Name="PART_DisabledVisual" Fill="{StaticResource DisabledColor}" Opacity="0" RadiusX="2" RadiusY="2" Stroke="{StaticResource DisabledColor}" Stretch="Fill" StrokeThickness="1" Visibility="Collapsed"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Visibility" TargetName="PART_DisabledVisual" Value="Visible"/>
                            </Trigger>
                            <DataTrigger Binding="{Binding DisplayMode, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Calendar}}}" Value="Year">
                                <Setter Property="Visibility" TargetName="PART_MonthView" Value="Hidden"/>
                                <Setter Property="Visibility" TargetName="PART_YearView" Value="Visible"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding DisplayMode, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Calendar}}}" Value="Decade">
                                <Setter Property="Visibility" TargetName="PART_MonthView" Value="Hidden"/>
                                <Setter Property="Visibility" TargetName="PART_YearView" Value="Visible"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="30" />
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="0" IsReadOnly="True" Name="textBox" VerticalContentAlignment="Center"
                 Text="{Binding CurrentDateTime,RelativeSource={RelativeSource AncestorType=UserControl},StringFormat={}{0:yyyy-MM-dd HH:mm:ss}}"/>
        <ToggleButton Grid.Column="1" x:Name="toggleButton" Background="AliceBlue">
            <ToggleButton.Content>
                <local:DateTimePickerIcon />
            </ToggleButton.Content>
        </ToggleButton>

        <!--AllowsTransparency:控件包含透明内容-->
        <Popup StaysOpen="False" IsOpen="{Binding ElementName=toggleButton,Path=IsChecked}" 
               PlacementTarget="{Binding ElementName=textBox}" AllowsTransparency="True"
               HorizontalOffset="0" Name="popup">
            <Border Background="#F7F9FA" Width="auto" Height="auto" Margin="3">
                <Border.Effect>
                    <DropShadowEffect BlurRadius="10" Color="Gray" ShadowDepth="0" Opacity="0.3" />
                </Border.Effect>

                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto"/>
                        <RowDefinition Height="auto"/>
                    </Grid.RowDefinitions>

                    <Calendar x:Name="calendar" 
                              BorderThickness="0"
                              Background="Transparent"
                              CalendarItemStyle="{StaticResource CalendarCalendarItemStyle1}" 
                              CalendarDayButtonStyle="{StaticResource CalendarCalendarDayButtonStyle1}"/>

                    <Grid Grid.Row="1" Margin="10,5,10,10">
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition Height="30"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <RepeatButton Content="△" Style="{StaticResource ButtonUpStyle}" Click="BtnHourUp_Click"/>
                        <RepeatButton Content="△" Style="{StaticResource ButtonUpStyle}" Grid.Column="2" Click="BtnMinuteUp_Click"/>
                        <RepeatButton Content="△" Style="{StaticResource ButtonUpStyle}" Grid.Column="4" Click="BtnSecondUp_Click"/>
                        <RepeatButton Content="▽" Style="{StaticResource ButtonDownStyle}" Grid.Row="2" Click="BtnHourDown_Click"/>
                        <RepeatButton Content="▽" Style="{StaticResource ButtonDownStyle}" Grid.Row="2" Grid.Column="2" Click="BtnMinuteDown_Click"/>
                        <RepeatButton Content="▽" Style="{StaticResource ButtonDownStyle}" Grid.Row="2" Grid.Column="4" Click="BtnSecondDown_Click"/>

                        <TextBox Grid.Row="1" Name="tb_hour" TextChanged="tb_hour_TextChanged" Text="{Binding HourInt,RelativeSource={RelativeSource AncestorType=UserControl}}" 
                                 VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" Margin="3"/>
                        <TextBlock Text=":" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="3"/>
                        <TextBox Grid.Row="1" Grid.Column="2" Name="tb_minute" TextChanged="tb_minute_TextChanged" Text="{Binding MinuteInt,RelativeSource={RelativeSource AncestorType=UserControl}}" 
                                 VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" Margin="3"/>
                        <TextBlock Text=":" Grid.Row="1" Grid.Column="3" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="3"/>
                        <TextBox Grid.Row="1" Grid.Column="4" Name="tb_second" TextChanged="tb_second_TextChanged"  Text="{Binding SecondInt,RelativeSource={RelativeSource AncestorType=UserControl}}"
                                 VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Height="24" Margin="3"/>

                        <Button Content="×" Style="{StaticResource ButtonCancelStyle}" Click="Button_Click_1" BorderThickness="0" Grid.Row="1" Grid.Column="5" />
                        <Button Content="√" Style="{StaticResource ButtonAcceptStyle}" BorderThickness="0" Grid.Row="1" Grid.Column="6" Click="Button_Click"/>
                    </Grid>
                </Grid>

            </Border>
        </Popup>
    </Grid>
</UserControl>

图标XAML:

<UserControl x:Class="XH.CustomLesson.Controls.DateTimePickerIcon"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XH.CustomLesson.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Viewbox>
        <Grid>
            <Path Data="M47.93344 167.1936h-0.98816v248.83712h933.248V355.31264c0.04096-1.00352 0.29696-1.9456 0.29696-2.95936V177.00864c0-1.01376-0.256-1.95584-0.29696-2.95936v-6.85568h-0.6912c-4.80256-33.50016-33.3312-59.3408-68.16256-59.3408H116.096c-34.82624 0.00512-63.36 25.84064-68.16256 59.3408z" Fill="#F38181" />
            <Path Data="M979.5072 856.8064h0.98816V404.9152H47.24224v60.71296c-0.04096 1.00352-0.29696 1.9456-0.29696 2.95424v378.40384c0 1.01376 0.256 1.95584 0.29696 2.95424v6.85568h0.6912c4.80256 33.50016 33.3312 59.3408 68.16256 59.3408h795.24352c34.83136 0.00512 63.36512-25.8304 68.16768-59.33056z" Fill="#E6E4E2" />
            <Path Data="M489.54368 646.2208c-4.83328 5.80608-9.02144 9.98912-19.98336 19.65568-6.12352 5.80608-12.5696 11.60704-18.688 17.40288-19.65568 18.37056-23.20896 22.89152-27.71456 38.03136h77.6704v43.19232H365.4656v-18.37056c0-25.13408 6.44096-44.14976 23.5264-62.848 11.92448-13.2096 25.7792-24.81152 38.99904-37.05856 19.01568-17.40288 22.88128-22.23616 22.88128-37.70368v-16.75776c0-4.51584 0-20.95104-17.40288-20.62848-11.60704 0-15.79008 6.76352-17.40288 11.60192-0.64512 1.93536-0.96768 4.51072-0.96768 9.344v31.90784h-47.69792v-29.32736c0-17.0752 0.96768-32.22528 14.50496-45.11744 9.02144-8.704 24.81664-16.75776 52.85376-16.75776 26.42944 0 41.25184 8.05888 50.2784 15.47264 2.2528 1.93024 9.02144 7.7312 12.5696 16.4352 2.89792 7.0912 3.8656 14.8224 3.8656 30.61248-0.00512 19.33312 0.64 34.79552-11.9296 50.91328zM654.87872 742.90688c-3.2256 5.16096-8.05888 9.02656-9.67168 10.31168-18.688 15.47264-42.86464 15.15008-50.2784 15.15008-25.45152 0-40.92928-8.05888-49.3056-15.15008-6.44608-5.4784-10.31168-11.60192-13.21984-19.33312-3.8656-11.27936-3.8656-22.56384-3.8656-34.16064v-103.45984c0-4.19328 0-8.704 0.32768-12.89216 0.31744-5.15072 1.28512-19.33312 11.60192-31.2576 8.704-10.63936 23.84896-19.01568 53.17632-19.01568 23.20896 0 53.17632 5.4784 63.16544 29.97248 2.90816 6.76352 3.87584 12.24192 3.87584 26.42944v15.79008h-47.05792v-11.92448c0-4.51584 0.64512-19.66592-16.75776-19.66592-19.33824 0-19.01568 16.11776-19.01568 21.59616v42.21952c6.44608-6.44608 14.50496-14.8224 34.80576-14.8224 27.71456 0 41.57952 13.85472 46.09024 24.81152 2.2528 5.80608 3.2256 9.67168 3.2256 20.95104v37.38624c-0.00512 12.89728 0.32256 25.78432-7.09632 37.06368z m-41.5744-65.41824c0-4.83328 1.6128-23.53152-17.72032-23.53152-4.83328 0-8.38656 0.65024-11.92448 3.54816-6.44608 5.4784-6.12864 13.85472-6.12864 21.26848v27.71456c0 5.80608-1.28512 21.91872 18.37056 21.91872 6.12352 0 9.02656-1.6128 11.60704-3.54816 5.79584-4.83328 6.12352-12.89216 6.12352-27.39712-0.00512-6.43584-0.00512-13.2096-0.32768-19.97312z" Fill="#5B5144" />
            <Path Data="M455.91552 325.95456V239.50848l-18.54464 86.44608h-25.52832l-18.55488-86.44608v86.44608h-25.35424V205.81376h38.79936l17.87392 88.14592 17.86368-88.14592h39.31136v120.1408h-25.86624zM550.05696 325.95456l-4.08064-27.40224h-22.29248l-4.25472 27.40224H491.008l24.33536-120.1408h38.79936l24.32512 120.1408h-28.41088z m-14.97088-101.08416l-8.16128 53.43744h16.16384l-8.00256-53.43744zM629.56032 283.75552v42.19904h-28.42112v-42.19904l-28.59008-77.93664h28.59008l14.62784 53.0944 14.63808-53.0944h29.10208l-29.94688 77.93664z" Fill="#FFFFFF" />
        </Grid>
    </Viewbox>
</UserControl>

效果:

C#逻辑代码:

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace XH.CustomLesson.Controls
{
    /// <summary>
    /// DateTimePicker.xaml 的交互逻辑
    /// </summary>
    public partial class DateTimePicker : UserControl, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        public event EventHandler<DateTime> SeletedChanged;

        private int hourInt = DateTime.Now.Hour;

        public int HourInt
        {
            get { return hourInt; }
            set
            {
                hourInt = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("HourInt"));
            }
        }

        private int minuteInt = DateTime.Now.Minute;

        public int MinuteInt
        {
            get { return minuteInt; }
            set
            {
                minuteInt = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MinuteInt"));
            }
        }

        private int secondInt = DateTime.Now.Second;

        public int SecondInt
        {
            get { return secondInt; }
            set
            {
                secondInt = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SecondInt"));
            }
        }

        public DateTime CurrentDateTime
        {
            get { return (DateTime)GetValue(CurrentDateTimeProperty); }
            set
            {
                SetValue(CurrentDateTimeProperty, value);
            }
        }

        // Using a DependencyProperty as the backing store for CurrentDateTime.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentDateTimeProperty =
            DependencyProperty.Register(
                "CurrentDateTime", 
                typeof(DateTime), typeof(DateTimePicker), 
                new PropertyMetadata(DateTime.Now,
                    new PropertyChangedCallback(OnCurentDateTimeChanged)));

        private static void OnCurentDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DateTimePicker).Refresh();
        }

        private void Refresh()
        {
            this.calendar.SelectedDate = this.CurrentDateTime; 
            this.calendar.DisplayDate = this.CurrentDateTime; // 确保日历显示正确的月份

            HourInt = this.CurrentDateTime.Hour;
            MinuteInt = this.CurrentDateTime.Minute;
            SecondInt = this.CurrentDateTime.Second;
        }

        public DateTimePicker()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 把日历和时间合并
            int year = calendar.SelectedDate.Value.Year;
            int month = calendar.SelectedDate.Value.Month;
            int day = calendar.SelectedDate.Value.Day;
            int.TryParse(this.tb_hour.Text.Trim(), out int hour);
            int.TryParse(this.tb_minute.Text.Trim(), out int minute);
            int.TryParse(this.tb_second.Text.Trim(), out int second);
            CurrentDateTime = new DateTime(year, month, day, hour, minute, second);
            this.popup.IsOpen = false;

            SeletedChanged?.Invoke(this, CurrentDateTime);
        }

        private void BtnHourUp_Click(object sender, RoutedEventArgs e)
        {
            var hour_str = string.IsNullOrEmpty(this.tb_hour.Text) ? "0" : this.tb_hour.Text;
            HourInt = int.Parse(hour_str);

            HourInt++;
            HourInt %= 24;

            //this.tb_hour.Text = hourInt.ToString();
        }

        private void BtnHourDown_Click(object sender, RoutedEventArgs e)
        {
            var hour_str = string.IsNullOrEmpty(this.tb_hour.Text) ? "0" : this.tb_hour.Text;
            HourInt = int.Parse(hour_str);

            HourInt--;
            HourInt = (HourInt + 24) % 24;

        }

        private void BtnMinuteDown_Click(object sender, RoutedEventArgs e)
        {
            var minute_str = string.IsNullOrEmpty(this.tb_minute.Text) ? "0" : this.tb_minute.Text;
            MinuteInt = int.Parse(minute_str);

            MinuteInt--;
            MinuteInt = (MinuteInt + 60) % 60;

        }

        private void BtnMinuteUp_Click(object sender, RoutedEventArgs e)
        {
            var minute_str = string.IsNullOrEmpty(this.tb_minute.Text) ? "0" : this.tb_minute.Text;
            MinuteInt = int.Parse(minute_str);

            MinuteInt++;
            MinuteInt %= 60;
        }

        private void BtnSecondUp_Click(object sender, RoutedEventArgs e)
        {
            var second_str = string.IsNullOrEmpty(this.tb_second.Text) ? "0" : this.tb_second.Text;
            SecondInt = int.Parse(second_str);

            SecondInt++;
            SecondInt %= 60;
        }

        private void BtnSecondDown_Click(object sender, RoutedEventArgs e)
        {
            var second_str = string.IsNullOrEmpty(this.tb_second.Text) ? "0" : this.tb_second.Text;
            SecondInt = int.Parse(second_str);

            SecondInt--;
            SecondInt = (SecondInt + 60) % 60;
        }

        private void tb_hour_TextChanged(object sender, TextChangedEventArgs e)
        {
            HourInt = int.Parse(tb_hour.Text);
            if (HourInt >= 24 || HourInt < 0)
                HourInt = 0;
        }

        private void tb_minute_TextChanged(object sender, TextChangedEventArgs e)
        {
            MinuteInt = int.Parse(tb_minute.Text);
            if (MinuteInt >= 60 || MinuteInt < 0)
                MinuteInt = 0;
        }

        private void tb_second_TextChanged(object sender, TextChangedEventArgs e)
        {
            SecondInt = int.Parse(tb_second.Text);
            if (SecondInt >= 60 || SecondInt < 0)
                SecondInt = 0;
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            this.popup.IsOpen = false;
        }
    }
}

使用自定义控件代码:

<c:DateTimePicker HorizontalAlignment="Center"
  SeletedChanged="DateTimePicker_SeletedChanged" VerticalAlignment="Top"
  Width="200" CurrentDateTime="{Binding Current,RelativeSource={RelativeSource AncestorType=Window}}" />

CustomControl 自定义控件

案例:自定义数值控件

XAML代码:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:c="clr-namespace:XH.CustomLesson.Controls"
    xmlns:local="clr-namespace:XH.CustomLesson">


    <Style TargetType="{x:Type c:NumericBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type c:NumericBox}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="5">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width="30"/>
                            </Grid.ColumnDefinitions>
                            <TextBox Name="PART_Value" VerticalAlignment="Center" Margin="3,5" BorderThickness="0"
                                     Foreground="{TemplateBinding Foreground}"/>
                            <UniformGrid Rows="2" Grid.Column="1">
                                <RepeatButton Content="+" Background="Transparent" 
                                              BorderThickness="1,0,0,0" Name="PART_IncreaseButton"
                                                  BorderBrush="#DDD"/>
                                <RepeatButton Content="-" Background="Transparent" 
                                              BorderThickness="1,1,0,0" Name="PART_DecreaseButton"
                                                  BorderBrush="#DDD"/>
                            </UniformGrid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

C#逻辑代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;

namespace XH.CustomLesson.Controls
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:XH.CustomLesson.Controls"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:XH.CustomLesson.Controls;assembly=XH.CustomLesson.Controls"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目,然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:NumericBox/>
    ///
    /// </summary>
    /// 
    [TemplatePart(Name = "PART_Vlaue", Type = typeof(TextBox))]
    public class NumericBox : Control
    {
        TextBox txtValue = new TextBox();
        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(
            "Value",
            typeof(int),
            typeof(NumericBox),
            new PropertyMetadata(0, new PropertyChangedCallback(OnValueChanged)));

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var nb = (d as NumericBox);
            if (nb.txtValue == null) return;
            if (nb.Value > 30)
                nb.txtValue.Foreground = Brushes.Red;
            else if(nb.Value >0 && nb.Value < 30)
                nb.txtValue.Foreground = Brushes.Black;
            else if (nb.Value < 0)
                nb.txtValue.Foreground = Brushes.Orange;
        }


        static NumericBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericBox), new FrameworkPropertyMetadata(typeof(NumericBox)));
        }
        // 应用模板
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // 获取模板中的事件
            txtValue = base.GetTemplateChild("PART_Value") as TextBox;
            var btnIncrease = base.GetTemplateChild("PART_IncreaseButton") as RepeatButton;
            var btnDecrease = base.GetTemplateChild("PART_DecreaseButton") as RepeatButton;

            if (txtValue != null)
            {
                // 建立对象中的Value属性和模板中的TextBox控件的Text属性的绑定
                Binding binding = new Binding("Value");
                binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
                binding.RelativeSource = new RelativeSource() { AncestorType = typeof(NumericBox), Mode = RelativeSourceMode.FindAncestor };
                txtValue.SetBinding(TextBox.TextProperty, binding);
            }
            if (btnIncrease != null)
                btnIncrease.Click += BtnIncrease_Click;
            if (btnDecrease != null)
                btnDecrease.Click += BtnDecrease_Click;
        }

        private void BtnDecrease_Click(object sender, RoutedEventArgs e)
        {
            this.Value--;
        }

        private void BtnIncrease_Click(object sender, RoutedEventArgs e)
        {
            this.Value++;
        }
    }
}

使用部分代码:

<c:NumericBox VerticalAlignment="Bottom" Width="200" BorderBrush="#ddd"
  BorderThickness="1" Margin="20"/>

UserControl与CustomControl区别

  1. 自定义控件:注重控件对象的功能,必须遵守WPF的控件规则
    • 完全自己实现一个控件 继承现有控件进行功能扩展,并且添加新功能 WPF的控件要求
    • 后台代码(控制逻辑)和Generic.Xaml(样式 模板)进行组合
    • 支持模板重写
    • 继承Control
  1. 用户控件:注重复合控件组合使用,非常灵活,可以根据控件开发人员自己的意愿进行功能处理
    • 多个现有控件的集合,组成一个可复用的控件组
    • XAML和后台代码组成 绑定非常紧密
    • 不支持模板重写、样式
    • 继承UserControl

案例

工业仪表

XAML代码:

<UserControl x:Class="XH.CustomLesson.Controls.Instrument"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XH.CustomLesson.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="400" d:DesignWidth="400">
    <Grid>
        <Border Name="border" Background="#FF030A28">
            <Border.Effect>
                <DropShadowEffect BlurRadius="20" Opacity="0.5"  ShadowDepth="0" Direction="0" Color="#FF3CAFFF"/>
            </Border.Effect>
            <Grid>
                <Path Stroke="#333CAFFF" StrokeThickness="10" Name="circle" RenderTransformOrigin="0.5,0.5">
                    <Path.RenderTransform>
                        <RotateTransform Angle="-45"/>
                    </Path.RenderTransform>
                </Path>
                <Canvas x:Name="canvasPlate" RenderTransformOrigin="0.5,0.5" Margin="0">
                    <Canvas.RenderTransform>
                        <RotateTransform Angle="-45"/>
                    </Canvas.RenderTransform>
                </Canvas>
                <Path Data="" Name="plateBorder" Stroke="#FF3CAFFF" StrokeThickness="3" RenderTransformOrigin="0.5,0.5"
                      Width="{Binding ElementName=border,Path=Width}"
                      Height="{Binding ElementName=border,Path=Height}">
                    <Path.RenderTransform>
                        <RotateTransform Angle="-45"/>
                    </Path.RenderTransform>
                </Path>
                <Path Data="M200 205,360 200,200 195,195 200 200 205" Fill="Red" RenderTransformOrigin="0.5,0.5" Name="pointer">
                    <Path.RenderTransform>
                        <RotateTransform Angle="135" x:Name="rtPointer"/>
                    </Path.RenderTransform>
                </Path>
                <Border Width="60" Height="60" CornerRadius="30" Background="#FF030A28">
                    <Border.Effect>
                        <DropShadowEffect BlurRadius="20" Opacity="0.3" ShadowDepth="0" Direction="0" Color="#FF3CAFFF"/>
                    </Border.Effect>
                    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
                        <TextBlock Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center">
                        <Run Text="{Binding Value,RelativeSource={RelativeSource AncestorType=UserControl}}"
                             FontSize="20"/>
                        <Run Text="m³/H" FontSize="8"/>
                        </TextBlock>
                        <TextBlock Text="NATURAL GAS" Foreground="#FF8CBEF0" VerticalAlignment="Center" HorizontalAlignment="Center"
                               FontSize="6"/>
                    </StackPanel>
                </Border>
            </Grid>
        </Border>
    </Grid>
</UserControl>

C#代码:

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace XH.CustomLesson.Controls
{
    /// <summary>
    /// Instrument.xaml 的交互逻辑
    /// </summary>
    public partial class Instrument : UserControl
    {
        // 当前值 
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(Instrument),
                                        new PropertyMetadata(default(double), new PropertyChangedCallback(OnPropertyChanged)));

        // 最小刻度
        public double Minimum
        {
            get { return (double)GetValue(MinimumProperty); }
            set { SetValue(MinimumProperty, value); }
        }
        public static readonly DependencyProperty MinimumProperty =
            DependencyProperty.Register("Minimum", typeof(double), typeof(Instrument),
                                        new PropertyMetadata(default(double), new PropertyChangedCallback(OnPropertyChanged)));

        // 最大刻度
        public double Maximum
        {
            get { return (double)GetValue(MaximumProperty); }
            set { SetValue(MaximumProperty, value); }
        }
        public static readonly DependencyProperty MaximumProperty =
            DependencyProperty.Register("Maximum", typeof(double), typeof(Instrument),
                                        new PropertyMetadata(default(double), new PropertyChangedCallback(OnPropertyChanged)));

         间隔
        //public double Interval
        //{
        //    get { return (double)GetValue(IntervalProperty); }
        //    set { SetValue(IntervalProperty, value); }
        //}
        //public static readonly DependencyProperty IntervalProperty =
        //    DependencyProperty.Register("Interval", typeof(double), typeof(Instrument), new PropertyMetadata(default(double), new PropertyChangedCallback(OnPropertyChanged)));

        // 大刻度的个数
        public int ScaleCount
        {
            get { return (int)GetValue(ScaleCountProperty); }
            set { SetValue(ScaleCountProperty, value); }
        }
        public static readonly DependencyProperty ScaleCountProperty =
            DependencyProperty.Register("ScaleCount", typeof(int), typeof(Instrument),
                                        new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropertyChanged)));

        // 刻度的厚度
        public double ScaleThickness
        {
            get { return (double)GetValue(ScaleThicknessProperty); }
            set { SetValue(ScaleThicknessProperty, value); }
        }
        public static readonly DependencyProperty ScaleThicknessProperty =
            DependencyProperty.Register("ScaleThickness", typeof(double), typeof(Instrument),
                                        new PropertyMetadata(default(double), new PropertyChangedCallback(OnPropertyChanged)));
        // 刻度的颜色
        public Brush ScaleBrush
        {
            get { return (Brush)GetValue(ScaleBrushProperty); }
            set { SetValue(ScaleBrushProperty, value); }
        }
        public static readonly DependencyProperty ScaleBrushProperty =
            DependencyProperty.Register("ScaleBrush", typeof(Brush), typeof(Instrument),
                                        new PropertyMetadata(default(Brush), new PropertyChangedCallback(OnPropertyChanged)));

        // 指针的颜色
        public Brush PointerBrush
        {
            get { return (Brush)GetValue(PointerBrushProperty); }
            set { SetValue(PointerBrushProperty, value); }
        }
        public static readonly DependencyProperty PointerBrushProperty =
            DependencyProperty.Register("PointerBrush", typeof(Brush), typeof(Instrument),
                                        new PropertyMetadata(default(Brush), new PropertyChangedCallback(OnPropertyChanged)));

        // 刻度字体大小 
        public new double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }
        public static new readonly DependencyProperty FontSizeProperty =
            DependencyProperty.Register("FontSize", typeof(double), typeof(Instrument),
                                        new PropertyMetadata(9.0, new PropertyChangedCallback(OnPropertyChanged)));




        public Instrument()
        {
            InitializeComponent();

            SetCurrentValue(MinimumProperty, 0d);
            SetCurrentValue(MaximumProperty, 100d);
            //SetCurrentValue(IntervalProperty, 10d);

            SizeChanged += (se, ev) => { Refresh(); };
        }

        static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            => (d as Instrument).Refresh();

        private void Refresh()
        {
            // 圆
            this.border.Width = Math.Min(RenderSize.Width, RenderSize.Height);
            this.border.Height = Math.Min(RenderSize.Width, RenderSize.Height);
            this.border.CornerRadius = new CornerRadius(this.border.Width / 2);
            // 半径
            double radius = this.border.Width / 2;

            this.canvasPlate.Children.Clear();
            if (ScaleCount <= 0 || radius <= 0) return;

            // 画边
            string borderPathData = $"M4,{radius}A{radius - 4} {radius - 4} 0 1 1 {radius} {this.border.Height - 4}";
            // 将字符串转换成Geometry
            var converter = TypeDescriptor.GetConverter(typeof(Geometry));
            //this.plateBorder.Data = (Geometry)converter.ConvertFrom(borderPathData);
            this.plateBorder.Data = PathGeometry.Parse(borderPathData);



            // 计算刻度
            double label = this.Minimum;
            double interval = 0;
            double step = 270.0 / (this.Maximum - this.Minimum);

            // 计算小刻度
            for (int i = 0; i < this.Maximum - this.Minimum; i++)
            {
                //添加刻度线
                Line lineScale = new Line();
                // 角度需要转换弧度
                lineScale.X1 = radius - (radius - 13) * Math.Cos(step * i * Math.PI / 180);
                lineScale.Y1 = radius - (radius - 13) * Math.Sin(step * i * Math.PI / 180);
                lineScale.X2 = radius - (radius - 8) * Math.Cos(step * i * Math.PI / 180);
                lineScale.Y2 = radius - (radius - 8) * Math.Sin(step * i * Math.PI / 180);

                lineScale.Stroke = this.ScaleBrush;
                lineScale.StrokeThickness = this.ScaleThickness;

                this.canvasPlate.Children.Add(lineScale);
            }
            // 计算大刻度
            do
            {
                //添加刻度线
                Line lineScale = new Line();
                lineScale.X1 = radius - (radius - 20) * Math.Cos(interval * step * Math.PI / 180);
                lineScale.Y1 = radius - (radius - 20) * Math.Sin(interval * step * Math.PI / 180);
                lineScale.X2 = radius - (radius - 8) * Math.Cos(interval * step * Math.PI / 180);
                lineScale.Y2 = radius - (radius - 8) * Math.Sin(interval * step * Math.PI / 180);

                lineScale.Stroke = this.ScaleBrush;
                lineScale.StrokeThickness = this.ScaleThickness;

                this.canvasPlate.Children.Add(lineScale);

                TextBlock txtScale = new TextBlock();
                txtScale.Text = label.ToString("0");
                txtScale.Width = 34;
                txtScale.TextAlignment = TextAlignment.Center;
                txtScale.Foreground = new SolidColorBrush(Colors.White);
                txtScale.RenderTransform = new RotateTransform() { Angle = 45, CenterX = 17, CenterY = 8 };
                txtScale.FontSize = this.FontSize;
                Canvas.SetLeft(txtScale, radius - (radius - 34) * Math.Cos(interval * step * Math.PI / 180) - 17);
                Canvas.SetTop(txtScale, radius - (radius - 34) * Math.Sin(interval * step * Math.PI / 180) - 8);
                this.canvasPlate.Children.Add(txtScale);

                interval += (this.Maximum - this.Minimum) / this.ScaleCount;
                label += (this.Maximum - this.Minimum) / this.ScaleCount;

            } while (interval <= this.Maximum - this.Minimum);


            // 修改指针
            string sData = "M{0} {1},{2} {0},{0} {3},{3} {0},{0} {1}";
            sData = string.Format(sData, radius, radius + 2, this.border.Width - radius / 10, radius - 4);
            converter = TypeDescriptor.GetConverter(typeof(Geometry));
            this.pointer.Data = (Geometry)converter.ConvertFrom(sData);
            this.pointer.Fill = this.PointerBrush;

            //DoubleAnimation da = new DoubleAnimation((Value - Minimum) * step + 135, new Duration(TimeSpan.FromMilliseconds(200)));
            //this.rtPointer.BeginAnimation(RotateTransform.AngleProperty, da);
            this.rtPointer.Angle = (Value - Minimum) * step + 135;

            // 修改圆  M100 200 A100 100 0 1 1 200 300
            // 厚度
            double thickness = radius / 2;
            this.circle.StrokeThickness = thickness;
            double startX = radius - thickness / 2;
            double startY = radius;
            double endX = radius - (radius - thickness / 2) * Math.Cos((Value - Minimum) * step * Math.PI / 180);
            double endY = radius - (radius - thickness / 2) * Math.Sin((Value - Minimum) * step * Math.PI / 180);

            int isLarge = 1;
            if ((Value - Minimum) * step < 180)
                isLarge = 0;

            sData = $"M{startX},{startY}A{radius / 2} {radius / 2} 0 1 1 {endX} {endY}";
            sData = $"M{thickness / 2},{radius}A{radius - thickness / 2} {radius - thickness / 2} 0 {isLarge} 1 {endX} {endY}";
            //sData = string.Format(sData, radius * 0.5, radius, radius * 1.5);
            this.circle.Data = (Geometry)converter.ConvertFrom(sData);
            this.circle.Visibility = Visibility.Visible;

            if (this.border.Width < 200)
                this.circle.Visibility = Visibility.Collapsed;
        }
    }
}

使用代码:

<c:Instrument Height="200" Width="200" Maximum="100" Value="25" Minimum="0"
  ScaleCount="10" ScaleThickness="0.5" ScaleBrush="White"
  PointerBrush="Red" FontSize="9"/>

效果展示:

各种常见开关

自定义控件:代码:

<Style TargetType="{x:Type c:Switch}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type c:Switch}">
                <Grid>
                    <Border Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}" 
                            CornerRadius="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"
                            Visibility="Collapsed" Name="dropdown" Margin="-23">
                        <Border.Background>
                            <RadialGradientBrush>
                                <GradientStop Color="Transparent" Offset="1"/>
                                <GradientStop Color="#5500D787" Offset="0.7"/>
                                <GradientStop Color="Transparent" Offset="0.59"/>
                            </RadialGradientBrush>
                        </Border.Background>
                    </Border>
                    <Border Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"
                            BorderBrush="DarkGreen" BorderThickness="5"
                            CornerRadius="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"
                            Background="Gray" Name="bor">
                        <Border Background="#FF00C88C" Margin="2" Name="bor1"
                                Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}" 
                                CornerRadius="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"/>
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard >
                                    <ColorAnimation To="White" Duration="0:0:0.5"
                                                    Storyboard.TargetName="bor1"
                                                    Storyboard.TargetProperty="Background.Color"/>
                                    <ColorAnimation To="#FF32FAC8" Duration="0:0:0.5"
                                                    Storyboard.TargetName="bor"
                                                    Storyboard.TargetProperty="BorderBrush.Color"/>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="dropdown"
                                                                   Storyboard.TargetProperty="Visibility">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard >
                                    <ColorAnimation Storyboard.TargetName="bor1"
                                                    Storyboard.TargetProperty="Background.Color"/>
                                    <ColorAnimation Storyboard.TargetName="bor"
                                                    Storyboard.TargetProperty="BorderBrush.Color"/>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="dropdown"
                                                                   Storyboard.TargetProperty="Visibility">
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

C#代码:

public class Switch :ToggleButton 
{
    static Switch()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Switch), new FrameworkPropertyMetadata(typeof(Switch)));
    }
}

使用场景:

<UniformGrid Rows="1" HorizontalAlignment="Center">
  <c:Switch Height="60" IsChecked="False" Margin="0 0 20 0" />
  <c:Switch Height="60" IsChecked="True" Margin="0 0 20 0"/>
  <c:Switch Height="60" IsChecked="False" Style="{StaticResource RotarySwitchStyle}" Margin="0 0 20 0"/>
  <c:Switch Height="60" IsChecked="True" Style="{StaticResource RotarySwitchStyle}" Margin="0 0 20 0"/>
  <c:Switch Height="60" IsChecked="False" Style="{StaticResource ButtonSwitchStyle}" Margin="0 0 20 0"/>
  <c:Switch Height="60" IsChecked="True" Style="{StaticResource ButtonSwitchStyle}" Margin="0 0 20 0"/>
  <ToggleButton Height="60" Style="{StaticResource Button2SwitchStyle}" Margin="0 0 20 0"/>
</UniformGrid>

其他按钮自定义样式:其实都是ToggleButton的自定义模板

<Style TargetType="c:Switch" x:Key="RotarySwitchStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="c:Switch">
                <Grid>
                    <Border Background="#FF3C3C3C" 
                            Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"
                            CornerRadius="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"/>
                    <Border BorderBrush="Black" BorderThickness="1" Margin="8"
                            Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"
                            CornerRadius="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}"/>
                    <Border Width="12" CornerRadius="6" Background="#FF3C3C3C"
                            RenderTransformOrigin="0.5,0.5">
                        <Border.Effect>
                            <DropShadowEffect BlurRadius="14" Color="Black" Opacity="0.9" Direction="0"
                                              ShadowDepth="0"/>
                        </Border.Effect>
                        <Border.RenderTransform>
                            <RotateTransform Angle="-30" x:Name="rt"/>
                        </Border.RenderTransform>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="1*"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Border Background="White" Margin="3,3" CornerRadius="4"/>
                        </Grid>
                    </Border>
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation To="30" Duration="0:0:0.2"
                                                     Storyboard.TargetName="rt"
                                                     Storyboard.TargetProperty="Angle"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="rt"
                                                     Storyboard.TargetProperty="Angle"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="c:Switch" x:Key="ButtonSwitchStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="c:Switch">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="borWidth"/>
                    <Border Width="{Binding ElementName=borWidth,Path=ActualWidth}"
                            Height="{Binding ElementName=borWidth,Path=ActualHeight}"
                            Grid.ColumnSpan="2"
                            Background="#FF3C3C3C" CornerRadius="3">
                        <Grid>
                            <Border Background="#FFA24F3F" Margin="6"/>
                            <Grid Margin="6,18,6,6" Name="grid">
                                <Border>
                                    <Border.Background>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" x:Name="lgb">
                                            <GradientStop Color="#FF4B4B4B" Offset="0"/>
                                            <GradientStop Color="#FF863424" Offset="0.7"/>
                                        </LinearGradientBrush>
                                    </Border.Background>
                                </Border>
                                <Border>
                                    <Border.OpacityMask>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Color="Black" Offset="1" x:Name="gs1"/>
                                            <GradientStop Color="Transparent" Offset="0" x:Name="gs2"/>
                                        </LinearGradientBrush>
                                    </Border.OpacityMask>

                                    <Grid TextBlock.FontSize="16" Margin="0,3">
                                        <TextBlock Text="I" VerticalAlignment="Top" HorizontalAlignment="Center" Foreground="White"/>
                                        <TextBlock Text="O" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="White"/>
                                    </Grid>
                                </Border>
                            </Grid>
                        </Grid>
                    </Border>
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ThicknessAnimation To="6,6,6,18"
                                                        Duration="0:0:0.2"
                                                        Storyboard.TargetName="grid"
                                                        Storyboard.TargetProperty="Margin"/>
                                    <DoubleAnimation To="0" Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs1"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <DoubleAnimation To="1" Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs2"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <PointAnimation To="0,1" Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="StartPoint"/>
                                    <PointAnimation To="0,0" Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="EndPoint"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ThicknessAnimation Duration="0:0:0.2"
                                                        Storyboard.TargetName="grid"
                                                        Storyboard.TargetProperty="Margin"/>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs1"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs2"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <PointAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="StartPoint"/>
                                    <PointAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="EndPoint"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style TargetType="ToggleButton" x:Key="Button2SwitchStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Border x:Name="borWidth"/>
                    <Border Width="{Binding ElementName=borWidth,Path=ActualWidth}"
                            Height="{Binding ElementName=borWidth,Path=ActualHeight}"
                            Grid.ColumnSpan="2"
                            Background="#FF3C3C3C" CornerRadius="3">
                        <Grid>
                            <Border Background="#FFA24F3F" Margin="6"/>
                            <Grid Margin="6,18,6,6" Name="grid">
                                <Border>
                                    <Border.Background>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" x:Name="lgb">
                                            <GradientStop Color="#FF4B4B4B" Offset="0"/>
                                            <GradientStop Color="#FF863424" Offset="0.7"/>
                                        </LinearGradientBrush>
                                    </Border.Background>
                                </Border>
                                <Border>
                                    <Border.OpacityMask>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Color="Black" Offset="1" x:Name="gs1"/>
                                            <GradientStop Color="Transparent" Offset="0" x:Name="gs2"/>
                                        </LinearGradientBrush>
                                    </Border.OpacityMask>

                                    <Grid TextBlock.FontSize="16" Margin="0,3">
                                        <TextBlock Text="I" VerticalAlignment="Top" HorizontalAlignment="Center" Foreground="White"/>
                                        <TextBlock Text="O" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="White"/>
                                    </Grid>
                                </Border>
                            </Grid>
                        </Grid>
                    </Border>
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ThicknessAnimation To="6,6,6,18"
                                                        Duration="0:0:0.2"
                                                        Storyboard.TargetName="grid"
                                                        Storyboard.TargetProperty="Margin"/>
                                    <DoubleAnimation To="0" Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs1"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <DoubleAnimation To="1" Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs2"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <PointAnimation To="0,1" Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="StartPoint"/>
                                    <PointAnimation To="0,0" Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="EndPoint"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ThicknessAnimation Duration="0:0:0.2"
                                                        Storyboard.TargetName="grid"
                                                        Storyboard.TargetProperty="Margin"/>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs1"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <DoubleAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="gs2"
                                                     Storyboard.TargetProperty="Offset"/>
                                    <PointAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="StartPoint"/>
                                    <PointAnimation Duration="0:0:0.2"
                                                     Storyboard.TargetName="lgb"
                                                     Storyboard.TargetProperty="EndPoint"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

显示效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2041762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树莓派3B升级glibc-2.29

自从我树莓派的python升级到3.9&#xff0c;每次import numpy都会报错&#xff1a;libm.so.6: version GLIBC_2.29 not found。这又是怎么回事呢&#xff1f;查了资料才知道&#xff0c;原来是我的respbian系统版本低的原因&#xff08;如图&#xff09;。 &#xff08;可以用l…

Facebook广告投放优化思路分享,为何总是低量级

为什么谷歌优化总是不起效果&#xff1f;今天我来谈谈我的Facebook广告优化思路&#xff0c;希望对你有所帮助。感兴趣的可以点赞、收藏。关注我&#xff0c;每天分享海外推广知识。 以下是Facebook广告优化思路思维导图&#xff1a; 那么&#xff0c;今天主要来谈谈量级低的情…

哪个工具可以ai续写免费?多维度评测分享

在创意的征途中&#xff0c;你是否曾遇到过文思枯竭的困境&#xff1f;当灵感如同夜空中的流星&#xff0c;一闪而逝&#xff0c;如何捕捉并延续那份璀璨&#xff1f;答案或许就藏在ai续写软件的无限可能中。 不知道ai续写软件哪个好&#xff1f;别急&#xff0c;今天&#xff…

Win10 禁止更新-【延长更新时间】

文章目录 操作注册表 操作注册表 Win R regedit 打开注册表 粘贴&#xff1a; ** \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 代开目录 新建32位数据值 &#xff1a;FlightSettingsMaxPauseDays 999 然后到更新界面&#xff0c;就可以一直点

数字信号处理3:数字滤波器设计

文章目录 前言一、实验目的二、实验设备三、实验内容四、实验原理五、实验方法及要求1.用脉冲响应不变法设计巴特沃斯数字滤波器2. 用双线性变换法设计切比雪夫数字滤波器3. 用双线性变换法设计巴特沃斯数字滤波器,并将直接型结构转换成级联型结构4. 数字低通滤波器特性比较5…

GD - EmbeddedBuilder_v1.4.1.23782工程中的gdc名称必须和工程名称一致

文章目录 GD - EmbeddedBuilder_v1.4.1.23782工程中的gdc名称必须和工程名称一致概述笔记总结END GD - EmbeddedBuilder_v1.4.1.23782工程中的gdc名称必须和工程名称一致 概述 在看EmbeddedBuilder_v1.4.1.23782自带的demo工程。 看到 D:\EmbeddedBuilder_v1.4.1.23782\exam…

工业一体机立式报工台助力工厂改善生产报工效率

在现代化工业生产中&#xff0c;生产效率和管理水平是企业竞争力的关键。为了实现高效的生产管理&#xff0c;工厂需要一个可靠、稳定、便捷的报工系统。传统的报工方式存在诸多弊端&#xff0c;例如效率低、易出错、数据难以统计分析等。而工厂软件报工台立式工业一体机的出现…

家里浮毛怎么去掉最高效?这些宠物空气净化器总有一款适合你

我家是三个月大的时候被我领回家的。它简直就是个小天使&#xff0c;乖得不得了&#xff0c;既不乱尿也不掉毛。每次去朋友家&#xff0c;看到他们为猫咪掉毛头疼不已&#xff0c;我就忍不住在心里偷偷乐&#xff0c;觉得自己真是捡到了宝&#xff0c;怎么会有这么省心的猫咪呢…

上海知名泌尿外科专家常态化坐诊黄山新晨医院,让前列腺癌看得更早、更准!

继7月28日上海第四人民医院泌尿外科专家在黄山新晨医院开展义诊之后&#xff0c;8月9日和10日&#xff0c;该团队领头人周铁教授又完成了合作以来的首次坐诊&#xff0c;标志着双方合作从此进入常态化阶段。 周铁主任在查看患者的检查报告 周铁主任曾任中华医学会泌尿外科分会…

拍立淘API:商品ID与标题的快速获取

拍立淘&#xff08;Pailitao&#xff09;是阿里巴巴旗下的一个基于图像识别技术的购物应用功能&#xff0c;用户可以通过拍照或上传图片来快速找到相似的商品。然而&#xff0c;拍立淘本身并不直接提供一个公开的API接口供开发者使用来直接获取商品ID和标题等信息。 如果你想通…

C++现代教程五

#pragma once _Pragma("once")# C/C混合编程 #ifdef __cplusplus extern "C" { #endif // 一些c代码 #ifdef __cplusplus } #endif# 继承构造 struct A {A(int i) {}A(double d,int i){}A(float f,int i,const char* c){}//...等等系列的构造函数版本 }&am…

protobuf 生成 error

一、简介 响应错误时可以直接使用 errors 包中的 New 方法来声明一个 error&#xff0c;也可以直接通过 proto 预定义定义错误码&#xff0c;然后通过 proto-gen-go 生成帮助代码&#xff0c;直接返回 error。 二、使用教程 2.1 错误定义 syntax "proto3"; impor…

深入理解Java中的LocalDateTime与ChronoUnit:精确时间处理的最佳实践

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

人工智能战略:如何实施人工智能解决方案以实现企业成功

人工智能 (AI) 不再是一个未来概念&#xff0c;而是改变全球各行各业的切实现实。 企业正在利用人工智能来提高效率、提高生产力并获得竞争优势。然而&#xff0c;实施人工智能解决方案需要明确的战略。 本指南将引导您完成成功将人工智能融入业务运营的关键步骤&#xff0c;…

局部场电位LFP

声明&#xff1a;本文章是根据网上资料&#xff0c;加上自己整理和理解而成&#xff0c;仅为记录自己学习的点点滴滴。可能有错误&#xff0c;欢迎大家指正。 神经科学最伟大的发现之一是人脑的电活动可以用附在头皮上的电极进行无创测量。脑电图&#xff08;Electroencephalog…

四种实用办法恢复回收站清空的文件!

想要恢复回收站的文件不小心点成清空回收站&#xff0c;如果你也遇上同样的状况&#xff0c;想在回收站找回清空的文件&#xff0c;可以尝试以下这几个恢复办法。 方法一&#xff1a;借用云存储/存储设备找回丢失文件 如果丢失的文件在其他云存储软件上&#xff08;如百度网盘…

一款方便、高效的基于tree-sitter的代码风格转换器,支持Python、C/C++和Java共100多种风格转换

文章目录 工具介绍环境搭建使用教程工具框架流程图增删算法tree-sitter介绍项目目录结构说明风格列表 ​ 源代码网址&#xff1a;https://github.com/rebibabo/SCTS/tree/main 如果有引用本文或者工具&#xff0c;请注明引用来源 如果觉得对您有帮助&#xff0c;还请各位帅哥…

qt客户端与服务端关于传输文件

如果直接发 发送的数据永远要比接受的块 需要有时间间隔 此时定时器的作用就显现出来了 发送数据都先发头&#xff0c;要保证服务器发送的头&#xff0c;客户端能接受到 发送数据后不要直接读数据&#xff0c;加一个延迟 这里以##作为分隔符 发送多少读多少&#xff0c; 发送…

启程与远征Ⅲ--很棒的大语言模型代理

这是关于什么的&#xff1f; 这篇 Awesome-LLM-Agents 是对 Agentic AI 上的最新里程碑论文和开源项目的深度优先回顾。 这个 Awesome-LLM-Agents 项目是我在LLM-Agents-in-Production主题上建立专业知识的第一步&#xff0c;我打算将这个故事用作知识库&#xff0c;记录项目…

Unity转Unreal5从入门到精通之不同的Actor之间如何交互

前言 Unreal不同的Actor蓝图之间如何交互 当我们使用蓝图时&#xff0c;当碰到交互的时候&#xff0c;可能会有点蒙&#xff0c;平时我们写代码&#xff0c;A调用B的函数&#xff0c;非常简单。那么在蓝图中&#xff0c;我们如何调用呢&#xff1f;下面我们来一一讲解 通过函…