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>

显示效果:

相关推荐
玖笙&3 天前
✨WPF编程基础【2.1】布局原则
c++·wpf·visual studio
玖笙&3 天前
✨WPF编程基础【2.2】:布局面板实战
c++·wpf·visual studio
SEO-狼术3 天前
.NET WPF 数据编辑器集合提供列表框控件
.net·wpf
FuckPatience7 天前
WPF 具有跨线程功能的UI元素
wpf
诗仙&李白7 天前
HEFrame.WpfUI :一个现代化的 开源 WPF UI库
ui·开源·wpf
He BianGu7 天前
【笔记】在WPF中Binding里的详细功能介绍
笔记·wpf
He BianGu8 天前
【笔记】在WPF中 BulletDecorator 的功能、使用方式并对比 HeaderedContentControl 与常见 Panel 布局的区别
笔记·wpf
123梦野8 天前
WPF——效果和可视化对象
wpf
He BianGu8 天前
【笔记】在WPF中Decorator是什么以及何时优先考虑 Decorator 派生类
笔记·wpf
时光追逐者9 天前
一款专门为 WPF 打造的开源 Office 风格用户界面控件库
ui·开源·c#·.net·wpf