WPF 仿照ElementUI实现时间选择器(TimePicker)

一、效果图,仿照ElementUI的TimePicker

 

二、创建一个自定义用户控件TimePicker。

Generic.xaml部分的代码

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


    <Style TargetType="{x:Type local:TimePicker}">
        <Setter Property="Height" Value="40" />
        <Setter Property="Width" Value="220" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="Foreground" Value="#606266" />
        <Setter Property="Background" Value="White" />
        <Setter Property="BorderBrush" Value="#dcdfe6" />
        <Setter Property="BorderThickness" Value="1" /> 
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TimePicker}">
                    <ControlTemplate.Resources>
                        <Style TargetType="Button"> 
                            <Setter Property="Cursor" Value="Hand" />
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="Button" >
                                        <Border Background="Transparent">
                                            <ContentPresenter  HorizontalAlignment="Center" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                                        </Border>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ControlTemplate.Resources>
                    <Border x:Name="border" Background="{TemplateBinding Background}"
                            Height="{TemplateBinding Height}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Cursor="Hand" SnapsToDevicePixels="True" CornerRadius="4">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="25" />
                                <ColumnDefinition />
                                <ColumnDefinition Width="25" /> 
                            </Grid.ColumnDefinitions>
                            <Viewbox  Width="14" Height="14">
                                <Path Fill="#8d8d8d">
                                    <Path.Data>
                                        <Geometry>
                                            M512 56.889c-250.311 0-455.111 204.8-455.111 455.111S261.689 967.111 512 967.111 967.111 762.311 967.111 512 762.311 56.889 512 56.889m0 853.333c-221.867 0-398.222-176.355-398.222-398.222S290.133 113.778 512 113.778 910.222 290.133 910.222 512 733.867 910.222 512 910.222
                                            M512 512V227.556h-56.889v341.333h284.445V512z
                                        </Geometry>
                                    </Path.Data>
                                </Path>
                            </Viewbox>
                            <TextBlock Grid.Column="1" VerticalAlignment="Center"
                                       FontSize="{TemplateBinding FontSize}"
                                       x:Name="Placeholder" Visibility="Hidden"
                                       Foreground="#9d9d9d" Text="{TemplateBinding Placeholder}" />
                            <TextBox x:Name="PART_ContentHost" Height="{TemplateBinding Height}" 
                                     Text="{TemplateBinding SelectedTime}" 
                                     Background="Transparent" Grid.Column="1"
                                     BorderThickness="0" VerticalContentAlignment="Center"  />
                            <Popup x:Name="PART_Popup" PlacementTarget="{Binding ElementName=PART_ContentHost}" 
                                   HorizontalOffset="-28"
                                   Grid.ColumnSpan="3" AllowDrop="True"   Width="186" Height="238"> 
                                <Popup.Resources>
                                    <Style TargetType="Popup">
                                        <Setter Property="AllowsTransparency" Value="True"/>
                                        <Setter Property="PopupAnimation" Value="Fade"/>
                                        <Setter Property="Placement" Value="Bottom" /> 
                                        <Setter Property="Child">
                                            <Setter.Value>
                                                <Border Padding="2" >
                                                    <Grid>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition Height="10"/>
                                                            <RowDefinition/>
                                                        </Grid.RowDefinitions>
                                                        <!--Popup的内容-->
                                                        <Border Grid.Row="1" CornerRadius="4" Background="White" BorderThickness="1" BorderBrush="#d9d9d9">
                                                            <Border.Effect>
                                                                <DropShadowEffect Color="Black" Opacity="0.1" BlurRadius="10" ShadowDepth="0" Direction="0" /> 
                                                            </Border.Effect>
                                                            <Grid>
                                                                <Grid.RowDefinitions>
                                                                    <RowDefinition />
                                                                    <RowDefinition Height="38" />
                                                                </Grid.RowDefinitions> 
                                                                <Border Margin="15 3 15 0"  Height="32" VerticalAlignment="Center"  BorderThickness="0 1" BorderBrush="#D9D9d9" />
                                                                <UniformGrid Columns="3"> 
                                                                    <!--时-->
                                                                    <Grid>
                                                                        <Grid.RowDefinitions>
                                                                            <RowDefinition Height="24" />
                                                                            <RowDefinition />
                                                                            <RowDefinition Height="24"/>
                                                                        </Grid.RowDefinitions> 
                                                                        <Button x:Name="PART_HourUp" FocusManager.IsFocusScope="True">
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M253.651376 655.446718c-6.503881 0-13.007763-2.16796-18.066337-7.226535-10.117149-10.117149-10.117149-26.015526 0-35.410021l231.971771-231.971771c10.117149-10.117149 23.847565-15.898377 39.023289-15.898377s28.183486 5.781228 39.023288 15.898377l231.249118 231.249118c10.117149 10.117149 10.117149 26.015526 0 35.410021-10.117149 10.117149-26.015526 10.117149-35.410021 0l-231.249118-231.249118c-2.16796-2.16796-4.335921-2.16796-5.781228 0l-232.694425 231.249118c-5.058574 5.058574-11.562456 7.949188-18.066337 7.949188z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button>
                                                                        <Button x:Name="PART_HourDown" Grid.Row="2" FocusManager.IsFocusScope="True">
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M508.025406 655.446718c-14.45307 0-28.183486-5.781228-39.023289-15.898376l-231.249118-231.249118c-10.117149-10.117149-10.117149-26.015526 0-36.132675s26.015526-10.117149 36.132675 0l231.249118 231.249118c2.16796 2.16796 4.335921 2.16796 5.781228 0l231.971771-231.971771c10.117149-10.117149 26.015526-10.117149 35.410021 0 10.117149 10.117149 10.117149 26.015526 0 36.132674l-231.971771 231.971772c-9.394495 10.117149-23.124912 15.898377-38.300635 15.898376z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button> 
                                                                        <ItemsControl x:Name="PART_HourItems" Grid.Row="1" >
                                                                            <ItemsControl.ItemsPanel>
                                                                                <ItemsPanelTemplate>
                                                                                    <VirtualizingStackPanel/>
                                                                                </ItemsPanelTemplate>
                                                                            </ItemsControl.ItemsPanel>
                                                                            <ItemsControl.ItemTemplate>
                                                                                <DataTemplate>
                                                                                    <Border Height="28" VerticalAlignment="Center">
                                                                                        <TextBlock x:Name="ItemValue" Margin="5 0 0 0" FontSize="12"  Foreground="#606266" FontWeight="Normal" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Value}"/>
                                                                                    </Border>
                                                                                    <DataTemplate.Triggers>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="True">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#303133" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Black" />
                                                                                        </DataTrigger>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="False">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#606266" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Normal" />
                                                                                        </DataTrigger>
                                                                                    </DataTemplate.Triggers>
                                                                                </DataTemplate>
                                                                            </ItemsControl.ItemTemplate>
                                                                        </ItemsControl>
                                                                    </Grid>
                                                                    <!--分-->
                                                                    <Grid Grid.Column="1">
                                                                        <Grid.RowDefinitions>
                                                                            <RowDefinition Height="24" />
                                                                            <RowDefinition />
                                                                            <RowDefinition Height="24"/>
                                                                        </Grid.RowDefinitions>
                                                                        <Button  x:Name="PART_MinuteUp" FocusManager.IsFocusScope="True" >
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M253.651376 655.446718c-6.503881 0-13.007763-2.16796-18.066337-7.226535-10.117149-10.117149-10.117149-26.015526 0-35.410021l231.971771-231.971771c10.117149-10.117149 23.847565-15.898377 39.023289-15.898377s28.183486 5.781228 39.023288 15.898377l231.249118 231.249118c10.117149 10.117149 10.117149 26.015526 0 35.410021-10.117149 10.117149-26.015526 10.117149-35.410021 0l-231.249118-231.249118c-2.16796-2.16796-4.335921-2.16796-5.781228 0l-232.694425 231.249118c-5.058574 5.058574-11.562456 7.949188-18.066337 7.949188z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button>
                                                                        <Button  Grid.Row="2"  x:Name="PART_MinuteDown"  FocusManager.IsFocusScope="True" >
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M508.025406 655.446718c-14.45307 0-28.183486-5.781228-39.023289-15.898376l-231.249118-231.249118c-10.117149-10.117149-10.117149-26.015526 0-36.132675s26.015526-10.117149 36.132675 0l231.249118 231.249118c2.16796 2.16796 4.335921 2.16796 5.781228 0l231.971771-231.971771c10.117149-10.117149 26.015526-10.117149 35.410021 0 10.117149 10.117149 10.117149 26.015526 0 36.132674l-231.971771 231.971772c-9.394495 10.117149-23.124912 15.898377-38.300635 15.898376z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button>
                                                                        <ItemsControl x:Name="PART_MinuteItems" Grid.Row="1" >
                                                                            <ItemsControl.ItemsPanel>
                                                                                <ItemsPanelTemplate>
                                                                                    <VirtualizingStackPanel/>
                                                                                </ItemsPanelTemplate>
                                                                            </ItemsControl.ItemsPanel>
                                                                            <ItemsControl.ItemTemplate>
                                                                                <DataTemplate>
                                                                                    <Border Height="28" VerticalAlignment="Center">
                                                                                        <TextBlock x:Name="ItemValue" Margin="5 0 0 0" FontSize="12"  Foreground="#606266" FontWeight="Normal" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Value}"/>
                                                                                    </Border>
                                                                                    <DataTemplate.Triggers>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="True">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#303133" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Black" />
                                                                                        </DataTrigger>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="False">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#606266" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Normal" />
                                                                                        </DataTrigger>
                                                                                    </DataTemplate.Triggers>
                                                                                </DataTemplate>
                                                                            </ItemsControl.ItemTemplate>
                                                                        </ItemsControl>
                                                                    </Grid>
                                                                    <!--秒-->
                                                                    <Grid Grid.Column="2">
                                                                        <Grid.RowDefinitions>
                                                                            <RowDefinition Height="24" />
                                                                            <RowDefinition />
                                                                            <RowDefinition Height="24"/>
                                                                        </Grid.RowDefinitions>
                                                                        <Button x:Name="PART_SecondUp" FocusManager.IsFocusScope="True" >
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M253.651376 655.446718c-6.503881 0-13.007763-2.16796-18.066337-7.226535-10.117149-10.117149-10.117149-26.015526 0-35.410021l231.971771-231.971771c10.117149-10.117149 23.847565-15.898377 39.023289-15.898377s28.183486 5.781228 39.023288 15.898377l231.249118 231.249118c10.117149 10.117149 10.117149 26.015526 0 35.410021-10.117149 10.117149-26.015526 10.117149-35.410021 0l-231.249118-231.249118c-2.16796-2.16796-4.335921-2.16796-5.781228 0l-232.694425 231.249118c-5.058574 5.058574-11.562456 7.949188-18.066337 7.949188z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button>
                                                                        <Button  Grid.Row="2" Name="PART_SecondDown" FocusManager.IsFocusScope="True" >
                                                                            <Viewbox  Width="16" Height="16">
                                                                                <Path Fill="#8d8d8d">
                                                                                    <Path.Data>
                                                                                        <Geometry>
                                                                                            M508.025406 655.446718c-14.45307 0-28.183486-5.781228-39.023289-15.898376l-231.249118-231.249118c-10.117149-10.117149-10.117149-26.015526 0-36.132675s26.015526-10.117149 36.132675 0l231.249118 231.249118c2.16796 2.16796 4.335921 2.16796 5.781228 0l231.971771-231.971771c10.117149-10.117149 26.015526-10.117149 35.410021 0 10.117149 10.117149 10.117149 26.015526 0 36.132674l-231.971771 231.971772c-9.394495 10.117149-23.124912 15.898377-38.300635 15.898376z
                                                                                        </Geometry>
                                                                                    </Path.Data>
                                                                                </Path>
                                                                            </Viewbox>
                                                                        </Button>
                                                                        <ItemsControl x:Name="PART_SecondItems" Grid.Row="1" >
                                                                            <ItemsControl.ItemsPanel>
                                                                                <ItemsPanelTemplate>
                                                                                    <VirtualizingStackPanel/>
                                                                                </ItemsPanelTemplate>
                                                                            </ItemsControl.ItemsPanel>
                                                                            <ItemsControl.ItemTemplate>
                                                                                <DataTemplate>
                                                                                    <Border Height="28" VerticalAlignment="Center">
                                                                                        <TextBlock x:Name="ItemValue" Margin="5 0 0 0" FontSize="12"  Foreground="#606266" FontWeight="Normal" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Value}"/>
                                                                                    </Border>
                                                                                    <DataTemplate.Triggers>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="True">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#303133" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Black" />
                                                                                        </DataTrigger>
                                                                                        <DataTrigger Binding="{Binding IsActive}" Value="False">
                                                                                            <Setter TargetName="ItemValue"  Property="Foreground" Value="#606266" />
                                                                                            <Setter TargetName="ItemValue"  Property="FontWeight" Value="Normal" />
                                                                                        </DataTrigger>
                                                                                    </DataTemplate.Triggers>
                                                                                </DataTemplate>
                                                                            </ItemsControl.ItemTemplate>
                                                                        </ItemsControl>
                                                                    </Grid>
                                                                </UniformGrid> 
                                                                <Border Grid.Row="1" BorderThickness="0 1 0 0" BorderBrush="#d8d8d8" Padding="4" Margin="0 5 0 0" >
                                                                    <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
                                                                        <Button x:Name="PART_Cancel" Content="取消" Foreground="#303133" FontSize="12"  Margin="5 0" />   
                                                                        <Button x:Name="PART_Submit" Content="确定" FontWeight="Black" Foreground="#409eff" FontSize="12"  Margin="5 0" />
                                                                    </StackPanel>
                                                                </Border>
                                                            </Grid>

                                                        </Border>
                                                        <!--三角形指示器-->
                                                        <Grid Width="Auto" Margin="25 0 0 0">
                                                            <Polyline VerticalAlignment="Bottom" Margin="0 0 0 -1" Points="0,12 6,6 12,12" Stroke="#d9d9d9" StrokeThickness="1" Fill="White"/>
                                                            <Polyline VerticalAlignment="Bottom" Margin="0 0 0 -1" Points="0,12 6,6 12,12" Stroke="#d9d9d9" StrokeThickness="1" Fill="White"/>
                                                            <Polyline VerticalAlignment="Bottom" Margin="0 0 0 -1" Points="0,12 6,6 12,12" Stroke="#d9d9d9" StrokeThickness="1" Fill="White"/> 
                                                        </Grid>
                                                    </Grid>
                                                </Border>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Popup.Resources> 
                            </Popup>
                            <Button Grid.Column="2"   Name="PART_Clear" Margin="25 0 0 0"> 
                                <Viewbox  Width="14" Height="14">
                                    <Path Fill="#8d8d8d">
                                        <Path.Data>
                                            <Geometry>
                                                M963.9424 520.17664c-1.792 238.32064-192.47104 436.28032-431.04256 445.2352-237.76256 8.92416-443.0592-180.59776-459.008-416.5376-16.0256-237.02016 166.00576-449.23392 401.73056-472.27904 236.04736-23.07584 454.79936 151.43936 484.9152 386.6368 2.41152 18.87744 3.25632 37.92384 3.4048 56.94464 0.25088 33.00352 51.45088 33.024 51.2 0-1.75616-234.1376-168.20736-439.81824-398.31552-486.90688C386.97472-13.76768 149.35552 113.76128 59.76064 329.97888c-90.78784 219.10528-9.93792 472.9344 185.22112 604.7744 195.7888 132.26496 465.39264 98.176 629.39648-68.16256 90.53696-91.83232 139.79648-218.08128 140.75904-346.4192 0.25088-33.024-50.94912-32.9984-51.19488 0.00512z
                                                  M339.26656 735.56992l394.63936-394.64448c23.36768-23.36768-12.83584-59.5712-36.1984-36.20352l-394.64448 394.6496c-23.36768 23.36256 12.83584 59.56608 36.20352 36.1984z
                                                  M733.90592 699.37152l-394.63936-394.6496c-23.36768-23.36256-59.5712 12.83584-36.20352 36.20352 131.54816 131.54816 263.0912 263.10144 394.64448 394.64448 23.36256 23.36768 59.56608-12.83584 36.1984-36.1984z
                                            </Geometry>
                                        </Path.Data>
                                    </Path>
                                </Viewbox>
                            </Button> 
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="BorderBrush" Value="#c0c4cc" />
                        </Trigger>
                        <Trigger SourceName="PART_ContentHost"  Property="IsFocused" Value="True">
                            <Setter Property="BorderBrush" Value="#409eff" />
                            <Setter TargetName="PART_Popup"  Property="IsOpen" Value="True" />
                        </Trigger>
                        <Trigger SourceName="PART_ContentHost" Property="Text" Value="">
                            <Setter TargetName="PART_Clear" Property="Visibility" Value="Hidden" />
                            <Setter TargetName="Placeholder" Property="Visibility" Value="Visible" />
                        </Trigger> 
                        <EventTrigger RoutedEvent="MouseEnter">
                            <BeginStoryboard>
                                <Storyboard>
                                    <ThicknessAnimation Storyboard.TargetName="PART_Clear"
                                                        Storyboard.TargetProperty="Margin"
                                                        To="0" Duration="0:0:.1" />
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Clear" Storyboard.TargetProperty="Opacity">
                                        <SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard> 
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseLeave">
                            <BeginStoryboard>
                                <Storyboard>
                                    <Storyboard>
                                        <ThicknessAnimation Storyboard.TargetName="PART_Clear"
                                                            Storyboard.TargetProperty="Margin"
                                                            To="25 0 0 0" Duration="0:0:0.1"/>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Clear" Storyboard.TargetProperty="Opacity">
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.1" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate> 
            </Setter.Value>
        </Setter> 
    </Style>
</ResourceDictionary>

TimePicker.cs部分的代码

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace WpfApp1
{
    public class ValueItem:INotifyPropertyChanged
    {
        private string _value;
        public string Value 
        {
            get { return _value; }
            set 
            {
                _value = value;
                OnPropertyChanged(nameof(Value));
            }
        }

        private bool _isActive;
        public bool IsActive 
        {
            get { return _isActive; } 
            set
            {
                _isActive = value;
                OnPropertyChanged(nameof(IsActive));
            }
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
        }

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

        public string Placeholder
        {
            get { return (string)GetValue(PlaceholderProperty); }
            set { SetValue(PlaceholderProperty, value); }
        }

        public static readonly DependencyProperty PlaceholderProperty =
            DependencyProperty.Register("Placeholder", typeof(string), typeof(TimePicker), new PropertyMetadata("请选择时间"));


        public string SelectedTime
        {
            get { return (string)GetValue(SelectedTimeProperty); }
            set { SetValue(SelectedTimeProperty, value); }
        }

        public static readonly DependencyProperty SelectedTimeProperty =
            DependencyProperty.Register("SelectedTime", typeof(string), typeof(TimePicker), new PropertyMetadata(""));

      
        public ObservableCollection<ValueItem> Hours = new ObservableCollection<ValueItem>();
        public ObservableCollection<ValueItem> Minutes = new ObservableCollection<ValueItem>();
        public ObservableCollection<ValueItem> Seconds = new ObservableCollection<ValueItem>();
         
        public override void OnApplyTemplate()
        {
            var lastSelectedTime = SelectedTime;
            var popup = GetTemplateChild("PART_Popup") as Popup; 
            var txtBox = GetTemplateChild("PART_ContentHost") as TextBox;
            if (popup != null)
            {
                var hourUpBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_HourUp") as Button;
                var hourDownBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_HourDown") as Button;

                var minuteUpBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_MinuteUp") as Button;
                var minuteDownBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_MinuteDown") as Button;

                var secondUpBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_SecondUp") as Button;
                var secondDownBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_SecondDown") as Button;

                var clearBtn = GetTemplateChild("PART_Clear") as Button;

                var submitBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_Submit") as Button; 

                var cancelBtn = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_Cancel") as Button; 


                var hourItems = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_HourItems") as ItemsControl;
                if(hourItems != null)
                { 
                    hourItems.ItemsSource = Hours;
                    hourItems.MouseEnter += (sender, e) =>
                    {
                        txtBox?.Select(0,2);
                    };
                }
                var minuteItems = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_MinuteItems") as ItemsControl;  
                if (minuteItems != null)
                {
                    minuteItems.ItemsSource = Minutes;
                    minuteItems.MouseEnter += (sender, e) =>
                    {
                        txtBox?.Select(3, 2);
                    };
                }
                var secondItems = LogicalTreeHelper.FindLogicalNode(popup?.Child, "PART_SecondItems") as ItemsControl;
                if (secondItems != null)
                { 
                    secondItems.ItemsSource = Seconds;
                    secondItems.MouseEnter += (sender, e) =>
                    {
                        txtBox?.Select(6, 2);
                    };
                }
                if (hourUpBtn != null)
                {
                    hourUpBtn.Click += (sender, e) =>
                    {
                        var temp = Hours.Last(); 
                        Hours.RemoveAt(Hours.Count - 1);
                        Hours.Insert(0, temp);
                        Hours[1].IsActive = false;
                        Hours[2].IsActive = true;
                        Hours[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(0, 2);
                    };
                } 

                if (hourDownBtn != null)
                {
                    hourDownBtn.Click += (sender, e) =>
                    { 
                        var temp = Hours.First(); 
                        Hours.RemoveAt(0);
                        Hours.Add(temp);
                        Hours[1].IsActive = false;
                        Hours[2].IsActive = true;
                        Hours[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(0, 2);
                    };
                  
                }

                if (minuteUpBtn != null)
                {
                    minuteUpBtn.Click += (sender, e) =>
                    {
                        var temp = Minutes.Last(); 
                        Minutes.RemoveAt(Minutes.Count - 1);
                        Minutes.Insert(0, temp);
                        Minutes[1].IsActive = false;
                        Minutes[2].IsActive = true;
                        Minutes[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(3, 2);
                    }; 
             
                }

                if (minuteDownBtn != null)
                {
                    minuteDownBtn.Click += (sender, e) =>
                    {
                        var temp = Minutes.First(); 
                        Minutes.RemoveAt(0);
                        Minutes.Add(temp);
                        Minutes[1].IsActive = false;
                        Minutes[2].IsActive = true;
                        Minutes[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(3, 2);
                    };
    
                }

                if (secondUpBtn != null)
                {
                    secondUpBtn.Click += (sender, e) =>
                    {
                        var temp = Seconds.Last(); 
                        Seconds.RemoveAt(Seconds.Count - 1);
                        Seconds.Insert(0, temp);
                        Seconds[1].IsActive = false;
                        Seconds[2].IsActive = true;
                        Seconds[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(6, 2);
                    };
                    
                }

                if (secondDownBtn != null)
                {
                    secondDownBtn.Click += (sender, e) =>
                    {
                        var temp = Seconds.First(); 
                        Seconds.RemoveAt(0);
                        Seconds.Add(temp);
                        Seconds[1].IsActive = false;
                        Seconds[2].IsActive = true;
                        Seconds[3].IsActive = false;
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        txtBox?.Select(6,2);
                        
                    };
                }

                if (submitBtn != null)
                {
                    submitBtn.Click += (sender, e) =>
                    {
                        SelectedTime = $"{Hours[2].Value}:{Minutes[2].Value}:{Seconds[2].Value}";
                        lastSelectedTime = SelectedTime;
                    };
                }

                if (cancelBtn !=null)
                {
                    cancelBtn.Click += (sender, e) =>
                    {
                        SelectedTime = lastSelectedTime;
                    };
                }

                if (clearBtn != null)
                {
                    clearBtn.Click += (sender, e) =>
                    {
                        SelectedTime = "";
                        lastSelectedTime = SelectedTime;
                    };
                }

                txtBox.PreviewGotKeyboardFocus += (sender, e) =>
                {
                    txtBox.Select(0,2);
                }; 
                popup.Opened += (sender, e) =>
                {
                    Hours.Clear();
                    Minutes.Clear();
                    Seconds.Clear(); 
                      
                    for (int i = 0; i < 24; i++)
                    { 
                        Hours.Add(new ValueItem
                        {
                            Value = i.ToString("00"),
                            IsActive = false
                        });
                    }  

                    for (int i = 0; i < 60; i++)
                    {
                        Minutes.Add(new ValueItem
                        {
                            Value = i.ToString("00"),
                            IsActive = false
                        }); 
                        Seconds.Add(new ValueItem
                        {
                            Value = i.ToString("00"),
                            IsActive = false
                        });
                    }
                     
                    if (!string.IsNullOrWhiteSpace(SelectedTime))
                    { 
                        var time = SelectedTime.Split(":");
                        var targetHour = Hours.FirstOrDefault(it => it.Value == time[0]);
                        if (targetHour != null)
                        { 
                            targetHour.IsActive = true;
                            var hourIndex = Hours.IndexOf(targetHour); 
                            for (int i = 0; i < hourIndex - 2; i++)
                            {
                                var hourTemp = Hours[0];
                                Hours.RemoveAt(0);
                                Hours.Add(hourTemp);
                            } 
                        }

                        var targetMinute = Minutes.FirstOrDefault(it => it.Value == time[1]);
                        if (targetMinute != null)
                        {
                            targetMinute.IsActive = true;
                            var minuteIndex = Minutes.IndexOf(targetMinute);
                            for (int i = 0; i < minuteIndex - 2; i++)
                            {
                                var minuteTemp = Minutes[0];
                                Minutes.RemoveAt(0);
                                Minutes.Add(minuteTemp);
                            }
                        }

                        var targetSecond = Seconds.FirstOrDefault(it => it.Value == time[2]);
                        if (targetSecond != null)
                        {
                            targetSecond.IsActive = true;
                            var secondIndex = Seconds.IndexOf(targetSecond);
                            for (int i = 0; i < secondIndex - 2; i++)
                            {
                                var secondTemp = Seconds[0];
                                Seconds.RemoveAt(0);
                                Seconds.Add(secondTemp);
                            }
                        }

                    }
                    else
                    {
                        Hours[2].IsActive = true;
                        Minutes[2].IsActive = true;
                        Seconds[2].IsActive = true;
                    }
                }; 
            } 
        }
    }
}

三、使用部分

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"> 
    <StackPanel> 
        <local:TimePicker Margin="50" SelectedTime="15:04:03" /> 
    </StackPanel>
</Window>

源码: https://download.csdn.net/download/qq_38060581/89030726

  • 13
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值