WPF自定义控件

控件模板

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

把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>

显示效果:

相关推荐
可喜~可乐12 小时前
C# WPF开发
microsoft·c#·wpf
界面开发小八哥14 小时前
DevExpress WPF中文教程:Grid - 如何移动和调整列大小?(二)
ui·.net·wpf·界面控件·devexpress·ui开发
界面开发小八哥14 小时前
「实战应用」如何用图表控件SciChart WPF实现应用程序的DPI感知?
信息可视化·wpf·数据可视化·图表·scichart wpf·scichart
明耀2 天前
WPF TabControl 设置item不能点击
wpf
军训猫猫头2 天前
20.抽卡只有金,带保底(WPF) C#
ui·c#·wpf
明耀2 天前
WPF 设置平均布局 如果隐藏的话,能够自动扩展
wpf
晚安苏州3 天前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末4 天前
WPF依赖属性详解
wpf
Hat_man_4 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州5 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定