WPF自定义DatePicker实现仿HTML5日期选择控件功能

ぃ、小莉子 提交于 2020-01-15 03:38:25

最近重写了WPF自带的DatePicker控件样式,可响应鼠标滚轮、键盘等事件修改日期,效果如图:

话不多说,直接上源码:

1、主窗体xaml:

<Window x:Class="WpfApp2.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:WpfApp2"
        mc:Ignorable="d" 
        Title="MainWindow" Height="200" Width="400" WindowStartupLocation="CenterScreen"
        Loaded="Window_Loaded">
    <Window.Resources>
        <!--RepeatButton样式-->
        <Style x:Key="ArrowButtonStyle" BasedOn="{x:Null}" TargetType="{x:Type RepeatButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                        <Grid >
                            <Border Background="#f3f3f3" CornerRadius="5,5,0,0" BorderBrush="#D3D3D3D3" BorderThickness="1,1,1,0"></Border>
                            <Path Fill="#FF5A5A5A" Stretch="Fill" Stroke="{x:Null}" Data="M135.66667,4.6666667 L132.16599,9.4999781 139.16621,9.4999778 z" Width="6" Height="4" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsFocused" Value="True"/>
                            <Trigger Property="IsMouseOver" Value="True"/>
                            <Trigger Property="IsPressed" Value="True"/>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Background" Value="Red"/>
                                <Setter Property="Opacity" Value="0.2"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--DatePickerTextBox样式-->
        <Style TargetType="DatePickerTextBox">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBox x:Name="YearText" AllowDrop="False" IsReadOnly="True"
                                     BorderThickness="0,0,0,0" Margin="2,2,2,2"
                                     Text="{Binding Path=SelectedDate, StringFormat='yyyy', RelativeSource={RelativeSource AncestorType=DatePicker}}"
                                     GotFocus="YearText_GotFocus" LostFocus="YearText_LostFocus"
                                     MouseWheel="Text_MouseWheel" PreviewKeyDown="Text_PreviewKeyDown"/>
                            <TextBlock Text="/" VerticalAlignment="Center"></TextBlock>
                            <TextBox x:Name="MonthText" AllowDrop="False" IsReadOnly="True"
                                     BorderThickness="0,0,0,0" Margin="2,2,2,2"
                                     Text="{Binding Path=SelectedDate, StringFormat='MM', RelativeSource={RelativeSource AncestorType=DatePicker}}"
                                     GotFocus="MonthText_GotFocus" LostFocus="MonthText_LostFocus"
                                     MouseWheel="Text_MouseWheel" PreviewKeyDown="Text_PreviewKeyDown"/>
                            <TextBlock Text="/" VerticalAlignment="Center"></TextBlock>
                            <TextBox x:Name="DayText" AllowDrop="False" IsReadOnly="True"
                                     BorderThickness="0,0,0,0" Margin="2,2,2,2"
                                     Text="{Binding Path=SelectedDate, StringFormat='dd', RelativeSource={RelativeSource AncestorType=DatePicker}}"
                                     GotFocus="DayText_GotFocus" LostFocus="DayText_LostFocus"
                                     MouseWheel="Text_MouseWheel" PreviewKeyDown="Text_PreviewKeyDown"/>
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--DatePicker样式-->
        <Style TargetType="{x:Type DatePicker}">
            <Setter Property="Foreground" Value="#FF333333"/>
            <Setter Property="IsTodayHighlighted" Value="True"/>
            <Setter Property="SelectedDateFormat" Value="Short"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Padding" Value="2"/>
            <Setter Property="BorderBrush" Value="#D3D3D3D3"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DatePicker}">
                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_DisabledVisual"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid x:Name="PART_Root" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
                                <Grid.Resources>
                                    <SolidColorBrush x:Key="DisabledBrush" Color="#A5FFFFFF"/>
                                    <ControlTemplate x:Key="DropDownButtonTemplate" TargetType="{x:Type Button}">
                                        <Grid>
                                            <VisualStateManager.VisualStateGroups>
                                                <VisualStateGroup x:Name="CommonStates">
                                                    <VisualStateGroup.Transitions>
                                                        <VisualTransition GeneratedDuration="0"/>
                                                        <VisualTransition GeneratedDuration="0:0:0.1" To="MouseOver"/>
                                                        <VisualTransition GeneratedDuration="0:0:0.1" To="Pressed"/>
                                                    </VisualStateGroup.Transitions>
                                                    <VisualState x:Name="Normal"/>
                                                    <VisualState x:Name="MouseOver">
                                                        <Storyboard>
                                                            <ColorAnimation Duration="0" To="#FF448DCA" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Storyboard.TargetName="Background"/>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[3].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#7FFFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#CCFFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#F2FFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                        </Storyboard>
                                                    </VisualState>
                                                    <VisualState x:Name="Pressed">
                                                        <Storyboard>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" Storyboard.TargetName="Background">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#FF448DCA"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <DoubleAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Highlight">
                                                                <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
                                                            </DoubleAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#EAFFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[2].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#C6FFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[3].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#6BFFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                            <ColorAnimationUsingKeyFrames BeginTime="0" Duration="00:00:00.001" Storyboard.TargetProperty="(Border.Background).(GradientBrush.GradientStops)[0].(GradientStop.Color)" Storyboard.TargetName="BackgroundGradient">
                                                                <SplineColorKeyFrame KeyTime="0" Value="#F4FFFFFF"/>
                                                            </ColorAnimationUsingKeyFrames>
                                                        </Storyboard>
                                                    </VisualState>
                                                    <VisualState x:Name="Disabled"/>
                                                </VisualStateGroup>
                                            </VisualStateManager.VisualStateGroups>
                                            <Grid Background="#11FFFFFF" FlowDirection="LeftToRight" HorizontalAlignment="Center" Height="18" Margin="0" VerticalAlignment="Center" Width="19">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="20*"/>
                                                    <ColumnDefinition Width="20*"/>
                                                    <ColumnDefinition Width="20*"/>
                                                    <ColumnDefinition Width="20*"/>
                                                </Grid.ColumnDefinitions>
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="23*"/>
                                                    <RowDefinition Height="19*"/>
                                                    <RowDefinition Height="19*"/>
                                                    <RowDefinition Height="19*"/>
                                                </Grid.RowDefinitions>
                                                <Border x:Name="Highlight" BorderBrush="#FF45D6FA" BorderThickness="1" Grid.ColumnSpan="4" CornerRadius="0,0,1,1" Margin="-1" Opacity="0" Grid.Row="0" Grid.RowSpan="4"/>
                                                <Border x:Name="Background" BorderBrush="#FFFFFFFF" BorderThickness="1" Background="#FF1F3B53" Grid.ColumnSpan="4" CornerRadius=".5" Margin="0,-1,0,0" Opacity="1" Grid.Row="1" Grid.RowSpan="3"/>
                                                <Border x:Name="BackgroundGradient" BorderBrush="#BF000000" BorderThickness="1" Grid.ColumnSpan="4" CornerRadius=".5" Margin="0,-1,0,0" Opacity="1" Grid.Row="1" Grid.RowSpan="3">
                                                    <Border.Background>
                                                        <LinearGradientBrush EndPoint=".7,1" StartPoint=".7,0">
                                                            <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                                            <GradientStop Color="#F9FFFFFF" Offset="0.375"/>
                                                            <GradientStop Color="#E5FFFFFF" Offset="0.625"/>
                                                            <GradientStop Color="#C6FFFFFF" Offset="1"/>
                                                        </LinearGradientBrush>
                                                    </Border.Background>
                                                </Border>
                                                <Rectangle Grid.ColumnSpan="4" Grid.RowSpan="1" StrokeThickness="1">
                                                    <Rectangle.Fill>
                                                        <LinearGradientBrush EndPoint="0.3,-1.1" StartPoint="0.46,1.6">
                                                            <GradientStop Color="#FF4084BD"/>
                                                            <GradientStop Color="#FFAFCFEA" Offset="1"/>
                                                        </LinearGradientBrush>
                                                    </Rectangle.Fill>
                                                    <Rectangle.Stroke>
                                                        <LinearGradientBrush EndPoint="0.48,-1" StartPoint="0.48,1.25">
                                                            <GradientStop Color="#FF494949"/>
                                                            <GradientStop Color="#FF9F9F9F" Offset="1"/>
                                                        </LinearGradientBrush>
                                                    </Rectangle.Stroke>
                                                </Rectangle>
                                                <Path Grid.ColumnSpan="4" Grid.Column="0" Data="M11.426758,8.4305077 L11.749023,8.4305077 L11.749023,16.331387 L10.674805,16.331387 L10.674805,10.299648 L9.0742188,11.298672 L9.0742188,10.294277 C9.4788408,10.090176 9.9094238,9.8090878 10.365967,9.4510155 C10.82251,9.0929432 11.176106,8.7527733 11.426758,8.4305077 z M14.65086,8.4305077 L18.566387,8.4305077 L18.566387,9.3435936 L15.671368,9.3435936 L15.671368,11.255703 C15.936341,11.058764 16.27293,10.960293 16.681133,10.960293 C17.411602,10.960293 17.969301,11.178717 18.354229,11.615566 C18.739157,12.052416 18.931622,12.673672 18.931622,13.479336 C18.931622,15.452317 18.052553,16.438808 16.294415,16.438808 C15.560365,16.438808 14.951641,16.234707 14.468243,15.826504 L14.881817,14.929531 C15.368796,15.326992 15.837872,15.525723 16.289043,15.525723 C17.298809,15.525723 17.803692,14.895514 17.803692,13.635098 C17.803692,12.460618 17.305971,11.873379 16.310528,11.873379 C15.83071,11.873379 15.399232,12.079271 15.016094,12.491055 L14.65086,12.238613 z" Fill="#FF2F2F2F" HorizontalAlignment="Center" Margin="4,3,4,3" Grid.Row="1" Grid.RowSpan="3" RenderTransformOrigin="0.5,0.5" Stretch="Fill" VerticalAlignment="Center"/>
                                                <Ellipse Grid.ColumnSpan="4" Fill="#FFFFFFFF" HorizontalAlignment="Center" Height="3" StrokeThickness="0" VerticalAlignment="Center" Width="3"/>
                                                <Border x:Name="DisabledVisual" BorderBrush="#B2FFFFFF" BorderThickness="1" Grid.ColumnSpan="4" CornerRadius="0,0,.5,.5" Opacity="0" Grid.Row="0" Grid.RowSpan="4"/>
                                            </Grid>
                                        </Grid>
                                    </ControlTemplate>
                                </Grid.Resources>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <DatePickerTextBox x:Name="PART_TextBox" Grid.Row="0" Grid.Column="0" Focusable="{TemplateBinding Focusable}" VerticalAlignment="Center" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
                                <Grid Grid.Column="1">
                                    <Grid.RowDefinitions>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                    </Grid.RowDefinitions>
                                    <RepeatButton x:Name="UpButton" Grid.Row="0" Style="{DynamicResource ArrowButtonStyle}" Width="12" Height="12" Cursor="Hand" HorizontalAlignment="Left" VerticalAlignment="Top" Click="UpButton_Click" Focusable="False"/>
                                    <RepeatButton x:Name="DownButton" Grid.Row="1" Style="{DynamicResource ArrowButtonStyle}" Width="12" Height="12" Cursor="Hand" HorizontalAlignment="Left" VerticalAlignment="Top" Click="DownButton_Click" RenderTransformOrigin="0.5,0.5" Focusable="False">
                                        <RepeatButton.RenderTransform>
                                            <TransformGroup>
                                                <ScaleTransform ScaleX="1" ScaleY="-1"/>
                                                <SkewTransform AngleX="0" AngleY="0"/>
                                                <RotateTransform Angle="0"/>
                                                <TranslateTransform X="0" Y="0"/>
                                            </TransformGroup>
                                        </RepeatButton.RenderTransform>
                                    </RepeatButton>
                                </Grid>
                                <Button x:Name="PART_Button" Grid.Column="2" Foreground="{TemplateBinding Foreground}" Focusable="False" HorizontalAlignment="Left" Margin="5,0,3,0" Cursor="Hand" Grid.Row="0" Template="{StaticResource DropDownButtonTemplate}" VerticalAlignment="Center" Width="20"/>
                                <Grid x:Name="PART_DisabledVisual" Grid.ColumnSpan="2" Grid.Column="0" IsHitTestVisible="False" Opacity="0" Grid.Row="0">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Rectangle Grid.Column="0" Fill="#A5FFFFFF" RadiusY="1" Grid.Row="0" RadiusX="1"/>
                                    <Rectangle Grid.Column="1" Fill="#A5FFFFFF" Height="18" Margin="3,0,3,0" RadiusY="1" Grid.Row="0" RadiusX="1" Width="19"/>
                                    <Popup x:Name="PART_Popup" AllowsTransparency="True" Placement="Bottom" PlacementTarget="{Binding ElementName=PART_TextBox}" StaysOpen="False"/>
                                </Grid>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding Source={x:Static SystemParameters.HighContrast}}" Value="false">
                                <Setter Property="Foreground" TargetName="PART_TextBox" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <DatePicker x:Name="ucDatePicker" Width="185" Height="30"></DatePicker>
    </Grid>
</Window>

2、交互源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp2
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool isYearSelect;
        private bool isMonthSelect;
        private bool isDaySelect;
        private int wheelNumber = 1;// 每次滚动滚轮的增量值
        private int increment = 1;// 点击增减按钮的增量值

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.ucDatePicker.SelectedDate = DateTime.Now;
        }

        private void UpButton_Click(object sender, RoutedEventArgs e)
        {
            if (isYearSelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(increment);
            if (isMonthSelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(increment);
            if (isDaySelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(increment);
        }
        private void DownButton_Click(object sender, RoutedEventArgs e)
        {
            if (isYearSelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(-increment);
            if (isMonthSelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(-increment);
            if (isDaySelect)
                this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(-increment);
        }
        private void Text_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0)
            {
                if (isYearSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(wheelNumber);
                if (isMonthSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(wheelNumber);
                if (isDaySelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(wheelNumber);
            }
            if (e.Delta < 0)
            {
                if (isYearSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(-wheelNumber);
                if (isMonthSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(-wheelNumber);
                if (isDaySelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(-wheelNumber);
            }
        }
        private void Text_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Up)
            {
                if (isYearSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(increment);
                if (isMonthSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(increment);
                if (isDaySelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(increment);
            }
            if (e.Key == Key.Down)
            {
                if (isYearSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddYears(-increment);
                if (isMonthSelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddMonths(-increment);
                if (isDaySelect)
                    this.ucDatePicker.SelectedDate = this.ucDatePicker.SelectedDate.Value.AddDays(-increment);
            }
        }
        private void YearText_GotFocus(object sender, RoutedEventArgs e)
        {
            isYearSelect = true;
        }
        private void MonthText_GotFocus(object sender, RoutedEventArgs e)
        {
            isMonthSelect = true;
        }
        private void DayText_GotFocus(object sender, RoutedEventArgs e)
        {
            isDaySelect = true;
        }
        private void YearText_LostFocus(object sender, RoutedEventArgs e)
        {
            isYearSelect = false;
        }
        private void MonthText_LostFocus(object sender, RoutedEventArgs e)
        {
            isMonthSelect = false;
        }
        private void DayText_LostFocus(object sender, RoutedEventArgs e)
        {
            isDaySelect = false;
        }
    }
}

这里只是简单做个演示,使用时可封装成自定义控件来用。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!