控件模板
顾名思义就是在原有的控件上进行模版修改成自己需要的样式
把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 0A70 70 0 0 0 50 0A70 70 0 0 1 100 0L 100 100 0 100" Fill="Orange"><Path.RenderTransform><TranslateTransform X="-30" x:Name="tt" /></Path.RenderTransform></Path><Path Data="M0 0A70 70 0 0 0 50 0A70 70 0 0 1 100 0L 100 100 0 100" Fill="#9f90"><Path.RenderTransform><TranslateTransform X="0" x:Name="tt_2" /></Path.RenderTransform></Path><Path Data="M0 0A70 70 0 0 0 50 0A70 70 0 0 1 100 0L100 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代码:
<ResourceDictionaryxmlns="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区别
- 自定义控件:注重控件对象的功能,必须遵守WPF的控件规则
-
- 完全自己实现一个控件 继承现有控件进行功能扩展,并且添加新功能 WPF的控件要求
- 后台代码(控制逻辑)和Generic.Xaml(样式 模板)进行组合
- 支持模板重写
- 继承Control
- 用户控件:注重复合控件组合使用,非常灵活,可以根据控件开发人员自己的意愿进行功能处理
-
- 多个现有控件的集合,组成一个可复用的控件组
- 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}";// 将字符串转换成Geometryvar 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>
显示效果: