WPF时间长度自定义选择控件TimeSpanBox

以下控件采用https://www.cnblogs.com/cssmystyle/archive/2011/01/17/1937361.html部分代码

以下控件采用https://www.cnblogs.com/xiaomingg/p/11180355.html部分代码

TimeSpanBox 效果如下,用于选择时间的长度,年最大值99,月最大值11,日最大值30,时最大值23,分最大值59,秒最大值59

第一步,新建WPF用户TimepanBox

<UserControl x:Class="WPFApp.Controls.TimeSpanBox.TimeSpanBox"
             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:WPFApp.Controls.TimeSpanBox"             
             BorderBrush="Gray" BorderThickness="1" mc:Ignorable="d"     Focusable="False"        
             d:DesignHeight="25" d:DesignWidth="200">
    <UserControl.Resources>
        <ControlTemplate x:Key="UPBtnTemplate" TargetType="{x:Type RepeatButton}">
            <Border BorderThickness="0">
                <Border.Background>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" Opacity="1">
                        <GradientStop Color="#FFF9FAFB" Offset="0" />
                        <GradientStop Color="#FFC4CCD4" Offset="0.973" />
                    </LinearGradientBrush>
                </Border.Background>
                <ContentPresenter HorizontalAlignment="Center" Content="{TemplateBinding Button.Content}" VerticalAlignment="Center"/>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="RenderTransform">
                        <Setter.Value>
                            <TranslateTransform X=".5" Y=".3"/>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
        <ControlTemplate x:Key="DownBtnTemplate" TargetType="{x:Type RepeatButton}">
            <Border BorderThickness="0">
                <Border.Background>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" Opacity="1">
                        <GradientStop Color="#FFC4CCD4" Offset="0.973" />
                        <GradientStop Color="#FFF9FAFB" Offset="0" />
                    </LinearGradientBrush>
                </Border.Background>
                <ContentPresenter HorizontalAlignment="Center" Content="{TemplateBinding Button.Content}" VerticalAlignment="Center"/>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="RenderTransform">
                        <Setter.Value>
                            <TranslateTransform X=".5" Y=".3"/>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </UserControl.Resources>
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition/>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="auto"/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="0" BorderBrush="Transparent" BorderThickness="0" x:Name="txtYear" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="年" Grid.Column="1" VerticalAlignment="Center" x:Name="txtbYear"/>
        <TextBox Grid.Column="2" BorderBrush="Transparent" BorderThickness="0" x:Name="txtMonth" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False"  TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="月" Grid.Column="3" VerticalAlignment="Center" x:Name="txtbMonth"/>
        <TextBox Grid.Column="4" BorderBrush="Transparent" BorderThickness="0" x:Name="txtDay" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="天" Grid.Column="5" VerticalAlignment="Center" x:Name="txtbDay"/>
        <TextBox Grid.Column="6" BorderBrush="Transparent" BorderThickness="0" x:Name="txtHour" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="时" Grid.Column="7" VerticalAlignment="Center" x:Name="txtbHour"/>
        <TextBox Grid.Column="8" BorderBrush="Transparent" BorderThickness="0" x:Name="txtMinute" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="分" Grid.Column="9" VerticalAlignment="Center" x:Name="txtbMinute"/>
        <TextBox Grid.Column="10" BorderBrush="Transparent" BorderThickness="0" x:Name="txtSecound" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"/>
        <TextBlock Text="秒" Grid.Column="11" VerticalAlignment="Center" x:Name="txtbSecound"/>
        <UniformGrid Grid.Column="12" Rows="2" x:Name="gridShow" Margin="1,0,0,0">
            <RepeatButton Padding="0" Width="{Binding RelativeSource={RelativeSource Mode=Self},Path=ActualHeight}" Focusable="False"
                    VerticalContentAlignment="Center" Template="{StaticResource UPBtnTemplate}" Command="{x:Static local:TimeSpanBox.IncreaseCommand}">
                <Path Height="6" Width="12" Stretch="Fill" Opacity="1" Fill="#FF554646"
                    Data="M 666.5,597 C666.5,597 678.5,597 678.5,597 678.5,597 672.5,591 672.5,591 672.5,591 666.5,597 666.5,597 z"/>
            </RepeatButton>
            <RepeatButton Padding="0" Width="{Binding RelativeSource={RelativeSource Mode=Self},Path=ActualHeight}" Focusable="False"
                    VerticalContentAlignment="Center" Template="{StaticResource DownBtnTemplate}" Command="{x:Static local:TimeSpanBox.DecreaseCommand}">
                <Path Height="6" Width="12" Stretch="Fill" Opacity="1" Fill="#FF554646"
                        Data="M 666.5,609 C666.5,609 678.5,609 678.5,609 678.5,609 672.5,615 672.5,615 672.5,615 666.5,609 666.5,609 z"/>
            </RepeatButton>
        </UniformGrid>
    </Grid>
</UserControl>

对应自定义用户控件的后台代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 WPFApp.Controls.TimeSpanBox
{
    public partial class TimeSpanBox : UserControl
    {
        private TextBox previousFocus = null;
        public TimeSpanBox()
        {
            InitializeComponent();

            MouseButtonEventHandler PreviewMouseDown = (o1, e1) =>
            {
                TextBox textbox = o1 as TextBox;
                if (textbox != null)
                {
                    textbox.Focus();
                    e1.Handled = true;
                }
            };
            txtYear.PreviewMouseDown += PreviewMouseDown;
            txtMonth.PreviewMouseDown += PreviewMouseDown;
            txtDay.PreviewMouseDown += PreviewMouseDown;
            txtHour.PreviewMouseDown += PreviewMouseDown;
            txtMinute.PreviewMouseDown += PreviewMouseDown;
            txtSecound.PreviewMouseDown += PreviewMouseDown;

            RoutedEventHandler GotFocus = (o1, e1) =>
            {
                TextBox txt = o1 as TextBox;
                if (txt != null)
                {
                    txt.SelectAll();
                    txt.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFBFEDF1"));
                    txt.Foreground = new SolidColorBrush(Colors.Black);
                    txt.PreviewMouseDown -= new MouseButtonEventHandler(PreviewMouseDown);
                }
                previousFocus = txt;
            };
            txtYear.GotFocus += GotFocus;
            txtMonth.GotFocus += GotFocus;
            txtDay.GotFocus += GotFocus;
            txtHour.GotFocus += GotFocus;
            txtMinute.GotFocus += GotFocus;
            txtSecound.GotFocus += GotFocus;


            RoutedEventHandler LostFocus = (o1, e1) =>
            {
                var textBox = o1 as TextBox;
                if (textBox != null)
                {
                    textBox.Background = new SolidColorBrush(Colors.Transparent);
                    textBox.Foreground = new SolidColorBrush(Colors.Black);
                    textBox.PreviewMouseDown += new MouseButtonEventHandler(PreviewMouseDown);
                }
            };
            txtYear.LostFocus += LostFocus;
            txtMonth.LostFocus += LostFocus;
            txtDay.LostFocus += LostFocus;
            txtHour.LostFocus += LostFocus;
            txtMinute.LostFocus += LostFocus;
            txtSecound.LostFocus += LostFocus;


            KeyEventHandler KeyDowm = (o1, e1) =>
            {
                TextBox txt = o1 as TextBox;
                if (e1.Key == Key.Enter || (e1.Key == Key.Right && txt.SelectionStart == txt.Text.Length))
                {
                    FocusNextText(txt);
                }
                else if (((e1.Key == Key.Delete || e1.Key == Key.Back) && (txt.Text == "0" || string.IsNullOrEmpty(txt.Text))) ||
                    (txt.SelectionStart == 0 && e1.Key == Key.Left)
                )
                {
                    e1.Handled = true;
                    if (!(txt.SelectionStart == 0 && e1.Key == Key.Left))
                        txt.Text = "0";
                    FocusPreviousText(txt);
                }
            };

            txtYear.PreviewKeyDown += KeyDowm;
            txtMonth.PreviewKeyDown += KeyDowm;
            txtDay.PreviewKeyDown += KeyDowm;
            txtHour.PreviewKeyDown += KeyDowm;
            txtMinute.PreviewKeyDown += KeyDowm;
            txtSecound.PreviewKeyDown += KeyDowm;


            txtYear.TextChanged += TextYear_Changed;
            txtMonth.TextChanged += txtMonth_Changed;
            txtDay.TextChanged += txtDay_Changed;
            txtHour.TextChanged += txtHour_Changed;
            txtMinute.TextChanged += txtMinute_Changed;
            txtSecound.TextChanged += txtSecound_Changed;
            

            txtbYear.MouseLeftButtonDown += (o1, e1) => { txtYear.Focus(); txtYear.SelectAll(); };
            txtbMonth.MouseLeftButtonDown += (o1, e1) => { txtMonth.Focus(); txtMonth.SelectAll(); };
            txtbDay.MouseLeftButtonDown += (o1, e1) => { txtDay.Focus(); txtDay.SelectAll(); };
            txtbHour.MouseLeftButtonDown += (o1, e1) => { txtHour.Focus(); txtHour.SelectAll(); };
            txtbMinute.MouseLeftButtonDown += (o1, e1) => { txtMinute.Focus(); txtMinute.SelectAll(); };
            txtbSecound.MouseLeftButtonDown += (o1, e1) => { txtSecound.Focus(); txtSecound.SelectAll(); };

            this.SetTextBoxBindingText(this.txtYear, "YearText");
            this.SetTextBoxBindingText(this.txtMonth, "MonthText");
            this.SetTextBoxBindingText(this.txtDay, "DayText");
            this.SetTextBoxBindingText(this.txtHour, "HourText");
            this.SetTextBoxBindingText(this.txtMinute, "MinuteText");
            this.SetTextBoxBindingText(this.txtSecound, "SecoundText");
        }
        private void SetTextBoxBindingText(TextBox targetTxt,string bindingPath)
        {
            Binding binding = new Binding(bindingPath);
            binding.Source = this;
            binding.Mode = BindingMode.TwoWay;
            binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            targetTxt.SetBinding(TextBox.TextProperty, binding);
        }

        #region 注册路由命令
        #region 注册鼠标左键按下事件
        static TimeSpanBox()
        {
            InitializeCommands();
            //EventManager.RegisterClassHandler(typeof(TimeSpanBox),
            //   Mouse.MouseDownEvent, new MouseButtonEventHandler(TimeSpanBox.OnMouseLeftButtonDown), true);
        }
        private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            TimeSpanBox control = (TimeSpanBox)sender;
            //如果某人点击控件的部分只,但是该控件没有聚焦,
            //本空间需要获取焦点来处理正确处理键盘
            if (!control.IsKeyboardFocusWithin)//获取一个值,该值指示键盘焦点是否处于元素边界内的任何位置(包括键盘焦点是否位于任何可视子元素的边界内)。
            {
                e.Handled = control.Focus() || e.Handled;
            }
        }
        #endregion

        private static RoutedCommand m_IncreaseCommand;
        private static RoutedCommand m_DecreaseCommand;
        public static RoutedCommand IncreaseCommand
        {
            get
            {
                return m_IncreaseCommand;
            }
        }
        public static RoutedCommand DecreaseCommand
        {
            get
            {
                return m_DecreaseCommand;
            }
        }
        private static void InitializeCommands()
        {
            m_IncreaseCommand = new RoutedCommand("IncreaseCommand", typeof(TimeSpanBox));
            CommandManager.RegisterClassCommandBinding(typeof(TimeSpanBox), new CommandBinding(m_IncreaseCommand, OnIncreaseCommand));
            CommandManager.RegisterClassInputBinding(typeof(TimeSpanBox), new InputBinding(m_IncreaseCommand, new KeyGesture(Key.Up)));

            m_DecreaseCommand = new RoutedCommand("DecreaseCommand", typeof(TimeSpanBox));
            CommandManager.RegisterClassCommandBinding(typeof(TimeSpanBox), new CommandBinding(m_DecreaseCommand, OnDecreaseCommand));
            CommandManager.RegisterClassInputBinding(typeof(TimeSpanBox), new InputBinding(m_DecreaseCommand, new KeyGesture(Key.Down)));
        }

        private static void OnDecreaseCommand(object sender, ExecutedRoutedEventArgs e)
        {
            TimeSpanBox control = sender as TimeSpanBox;
            if (control != null && control.previousFocus != null)
            {
                if (control.previousFocus == control.txtYear)
                {
                    control.Year = Math.Max(0, control.Year - 1);
                    control.txtYear.SelectAll();
                }
                else if (control.previousFocus == control.txtMonth)
                {
                    control.Month = Math.Max(0, control.Month - 1);
                    control.txtMonth.SelectAll();
                }
                else if (control.previousFocus == control.txtDay)
                {
                    control.Day = Math.Max(0, control.Day - 1);
                    control.txtDay.SelectAll();
                }
                else if (control.previousFocus == control.txtHour)
                {
                    control.Hour = Math.Max(0, control.Hour - 1);
                    control.txtHour.SelectAll();
                }
                else if (control.previousFocus == control.txtMinute)
                {
                    control.Minutes = Math.Max(0, control.Minutes - 1);
                    control.txtMinute.SelectAll();
                }
                else if (control.previousFocus == control.txtSecound)
                {
                    control.Secound = Math.Max(0, control.Secound - 1);
                    control.txtSecound.SelectAll();
                }
            }
        }

        private static void OnIncreaseCommand(object sender, ExecutedRoutedEventArgs e)
        {
            TimeSpanBox control = sender as TimeSpanBox;
            if (control != null)
            {
                if (control.previousFocus == control.txtYear)
                {
                    control.Year = Math.Min(99, control.Year + 1);
                    control.txtYear.SelectAll();
                }
                else if (control.previousFocus == control.txtMonth)
                {
                    control.Month = Math.Min(11, control.Month + 1);
                    control.txtMonth.SelectAll();
                }
                else if (control.previousFocus == control.txtDay)
                {
                    control.Day = Math.Min(30, control.Day + 1);
                    control.txtDay.SelectAll();
                }
                else if (control.previousFocus == control.txtHour)
                {
                    control.Hour = Math.Min(23, control.Hour + 1);
                    control.txtHour.SelectAll();
                }
                else if (control.previousFocus == control.txtMinute)
                {
                    control.Minutes = Math.Min(59, control.Minutes + 1);
                    control.txtMinute.SelectAll();
                }
                else if (control.previousFocus == control.txtSecound)
                {
                    control.Secound = Math.Min(59, control.Secound + 1);
                    control.txtSecound.SelectAll();
                }
            }
        }
        #endregion

        #region 跳转到上个文本框
        private void FocusNextText(TextBox currTxt)
        {
            TextBox[] textboxArray = new TextBox[] { txtYear, txtMonth, txtDay, txtHour, txtMinute, txtSecound };
            bool[] isVisibleArray = new bool[] { IsYearVisible, IsMonthVisible, IsDayVisible, IsHourVisible, IsMinuteVisible, IsSecoundVisible };

            int index = Array.IndexOf(textboxArray, currTxt);
            if (index != -1)
            {
                var findTxt = textboxArray.Skip(index + 1).FirstOrDefault(txt => isVisibleArray[Array.IndexOf(textboxArray, txt)]);
                if (findTxt != null)
                {
                    findTxt.Focus();
                }
            }
        }
        #endregion
        #region 跳转到下个文本框
        private void FocusPreviousText(TextBox currTxt)
        {
            TextBox[] textboxArray = new TextBox[] { txtYear, txtMonth, txtDay, txtHour, txtMinute, txtSecound };
            bool[] isVisibleArray = new bool[] { IsYearVisible, IsMonthVisible, IsDayVisible, IsHourVisible, IsMinuteVisible, IsSecoundVisible };
            int index = Array.IndexOf(textboxArray, currTxt);
            if (index != -1)
            {
                var findTxt = textboxArray.Take(index).Reverse().FirstOrDefault(txt => isVisibleArray[Array.IndexOf(textboxArray, txt)]);
                if (findTxt != null)
                {
                    findTxt.Focus();
                }
            }
        }
        #endregion

        #region 年份改变
        private bool CanRaiseTxtChaned_Year_AttachDP = true;
        private void TextYear_Changed(object sender, TextChangedEventArgs e)
        {
            string year = txtYear.Text;
            if (string.IsNullOrEmpty(year))
            {
                SetYear(0);
                if (txtYear.IsFocused == true)
                    txtYear.SelectAll();
            }
            else
            {
                int newYear = 0;
                bool result = int.TryParse(year, out newYear);
                if (result == true && newYear >= 0 && this.Year != newYear)
                {
                    if (newYear > 99)
                    {
                        SetText_Year("99");
                        SetYear(99);
                        txtMonth.Focus();
                    }
                    else
                    {
                        SetText_Year(newYear.ToString());
                        SetYear(newYear);
                    }
                    if (txtYear.Text.Length == 2)
                        FocusNextText(txtYear);
                }
                else
                {
                    SetYear(0);
                    SetText_Year("0");
                }
            }
        }
        private void SetText_Year(string newValue)
        {
            txtYear.TextChanged -= TextYear_Changed;
            txtYear.Text = newValue;
            txtYear.TextChanged += TextYear_Changed;
        }
        private void SetYear(int year)
        {
            CanRaiseTxtChaned_Year_AttachDP = false;
            this.Year = year;
            CanRaiseTxtChaned_Year_AttachDP = true;
        }
        #endregion
        #region 月份改变
        private bool CanRaiseTxtChaned_Month_AttachDP = true;
        private void txtMonth_Changed(object sender, TextChangedEventArgs e)
        {
            string month = txtMonth.Text;
            if (string.IsNullOrEmpty(month))
            {
                SetMonth(0);
                if (txtMonth.IsFocused == true)
                    txtMonth.SelectAll();
            }
            else
            {
                int newMonth = 0;
                bool result = int.TryParse(month, out newMonth);
                if (result == true && newMonth >= 0 && this.Month != newMonth)
                {
                    if (newMonth > 11)
                    {
                        SetText_Month("11");
                        SetMonth(11);
                        txtDay.Focus();
                    }
                    else
                    {
                        SetText_Month(newMonth.ToString());
                        SetMonth(newMonth);
                    }
                    if (txtMonth.Text.Length == 2)
                        FocusNextText(txtMonth);
                }
                else
                {
                    SetMonth(0);
                    SetText_Month("0");
                }
            }
        }
        private void SetText_Month(string newValue)
        {
            txtMonth.TextChanged -= txtMonth_Changed;
            txtMonth.Text = newValue;
            txtMonth.TextChanged += txtMonth_Changed;
        }
        private void SetMonth(int month)
        {
            CanRaiseTxtChaned_Month_AttachDP = false;
            this.Month = month;
            CanRaiseTxtChaned_Month_AttachDP = true;
        }
        #endregion
        #region 日改变
        private bool CanRaiseTxtChaned_Day_AttachDP = true;
        private void txtDay_Changed(object sender, TextChangedEventArgs e)
        {
            string day = txtDay.Text;
            if (string.IsNullOrEmpty(day))
            {
                SetDay(0);
                if (txtDay.IsFocused == true)
                    txtDay.SelectAll();
            }
            else
            {
                int newDay = 0;
                bool result = int.TryParse(day, out newDay);
                if (result == true && newDay >= 0 && this.Day != newDay)
                {
                    if (newDay > 30)
                    {
                        SetText_Day("30");
                        SetDay(30);
                        txtHour.Focus();
                    }
                    else
                    {
                        SetText_Day(newDay.ToString());
                        SetDay(newDay);
                    }
                    if (txtDay.Text.Length == 2)
                        FocusNextText(txtDay);
                }
                else
                {
                    SetDay(0);
                    SetText_Day("0");
                }
            }
        }
        private void SetText_Day(string newValue)
        {
            txtDay.TextChanged -= txtDay_Changed;
            txtDay.Text = newValue;
            txtDay.TextChanged += txtDay_Changed;
        }
        private void SetDay(int day)
        {
            CanRaiseTxtChaned_Day_AttachDP = false;
            this.Day = day;
            CanRaiseTxtChaned_Day_AttachDP = true;
        }
        #endregion
        #region 时改变
        private bool CanRaiseTxtChaned_Hour_AttachDP = true;
        private void txtHour_Changed(object sender, TextChangedEventArgs e)
        {
            string hour = txtHour.Text;
            if (string.IsNullOrEmpty(hour))
            {
                SetDay(0);
                if (txtHour.IsFocused == true)
                    txtHour.SelectAll();
            }
            else
            {
                int newHour = 0;
                bool result = int.TryParse(hour, out newHour);
                if (result == true && newHour >= 0 && this.Hour != newHour)
                {
                    if (newHour > 23)
                    {
                        SetText_Hour("23");
                        SetHour(23);
                        txtMinute.Focus();
                    }
                    else
                    {
                        SetText_Hour(newHour.ToString());
                        SetHour(newHour);
                    }
                    if (txtHour.Text.Length == 2)
                        FocusNextText(txtHour);
                }
                else
                {
                    SetHour(0);
                    SetText_Hour("0");
                }
            }
        }
        private void SetText_Hour(string newValue)
        {
            txtHour.TextChanged -= txtHour_Changed;
            txtHour.Text = newValue;
            txtHour.TextChanged += txtHour_Changed;
        }
        private void SetHour(int hour)
        {
            CanRaiseTxtChaned_Hour_AttachDP = false;
            this.Hour = hour;
            CanRaiseTxtChaned_Hour_AttachDP = true;
        }
        #endregion
        #region 分改变
        private bool CanRaiseTxtChaned_Minute_AttachDP = true;
        private void txtMinute_Changed(object sender, TextChangedEventArgs e)
        {
            string minute = txtMinute.Text;
            if (string.IsNullOrEmpty(minute))
            {
                SetMinute(0);
                if (txtMinute.IsFocused == true)
                    txtMinute.SelectAll();
            }
            else
            {
                int newMinute = 0;
                bool result = int.TryParse(minute, out newMinute);
                if (result == true && newMinute >= 0 && this.Minutes != newMinute)
                {
                    if (newMinute > 59)
                    {
                        SetText_Minute("59");
                        SetMinute(59);
                        txtSecound.Focus();
                    }
                    else
                    {
                        SetText_Minute(newMinute.ToString());
                        SetMinute(newMinute);
                    }
                    if (txtMinute.Text.Length == 2)
                        FocusNextText(txtMinute);
                }
                else
                {
                    SetMinute(0);
                    SetText_Minute("0");
                }
            }
        }
        private void SetText_Minute(string newValue)
        {
            txtMinute.TextChanged -= txtMinute_Changed;
            txtMinute.Text = newValue;
            txtMinute.TextChanged += txtMinute_Changed;
        }
        private void SetMinute(int Minute)
        {
            CanRaiseTxtChaned_Minute_AttachDP = false;
            this.Minutes = Minute;
            CanRaiseTxtChaned_Minute_AttachDP = true;
        }
        #endregion
        #region 秒改变
        private bool CanRaiseTxtChaned_Secound_AttachDP = true;
        private void txtSecound_Changed(object sender, TextChangedEventArgs e)
        {
            string secound = txtSecound.Text;
            if (string.IsNullOrEmpty(secound))
            {
                SetSecound(0);
                if (txtSecound.IsFocused == true)
                    txtSecound.SelectAll();
            }
            else
            {
                int newSecound = 0;
                bool result = int.TryParse(secound, out newSecound);
                if (result == true && newSecound >= 0 && this.Secound != newSecound)
                {
                    if (newSecound > 59)
                    {
                        SetText_Secound("59");
                        SetSecound(59);
                        txtSecound.SelectionStart = txtSecound.Text.Length;
                    }
                    else
                    {
                        SetText_Secound(newSecound.ToString());
                        SetSecound(newSecound);
                    }
                }
                else
                {
                    SetSecound(0);
                    SetText_Secound("0");
                }
            }
        }
        private void SetText_Secound(string newValue)
        {
            txtSecound.TextChanged -= txtSecound_Changed;
            txtSecound.Text = newValue;
            txtSecound.TextChanged += txtSecound_Changed;
        }
        private void SetSecound(int secound)
        {
            CanRaiseTxtChaned_Secound_AttachDP = false;
            this.Secound = secound;
            CanRaiseTxtChaned_Secound_AttachDP = true;
        }
        #endregion

        #region IsYearVisible
        public bool IsYearVisible
        {
            get { return (bool)GetValue(IsYearVisibleProperty); }
            set { SetValue(IsYearVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsYearVisibleProperty =
            DependencyProperty.Register("IsYearVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsYearVisible));

        private static void On_IsYearVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[0].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[1].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
                if (innerGrid.ColumnDefinitions[0].Width == new GridLength(0))
                {
                    sender.SetText_Year("0");
                    sender.CanRaiseTxtChaned_Year_AttachDP = false;
                    sender.Year = 0;
                    sender.CanRaiseTxtChaned_Year_AttachDP = true;
                }
            }
        }
        #endregion
        #region IsMonthVisible
        public bool IsMonthVisible
        {
            get { return (bool)GetValue(IsMonthVisibleProperty); }
            set { SetValue(IsMonthVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsMonthVisibleProperty =
            DependencyProperty.Register("IsMonthVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsMonthVisible));

        private static void On_IsMonthVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[2].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[3].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
            }
        }
        #endregion
        #region IsDayVisible
        public bool IsDayVisible
        {
            get { return (bool)GetValue(IsDayVisibleProperty); }
            set { SetValue(IsDayVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsDayVisibleProperty =
            DependencyProperty.Register("IsDayVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsDayVisible));

        private static void On_IsDayVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[4].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                  ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[5].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
            }
        }
        #endregion
        #region IsHourVisible
        public bool IsHourVisible
        {
            get { return (bool)GetValue(IsHourVisibleProperty); }
            set { SetValue(IsHourVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsHourVisibleProperty =
            DependencyProperty.Register("IsHourVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsHourVisible));

        private static void On_IsHourVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[6].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[7].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
            }
        }
        #endregion
        #region IsMinuteVisible
        public bool IsMinuteVisible
        {
            get { return (bool)GetValue(IsMinuteVisibleProperty); }
            set { SetValue(IsMinuteVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsMinuteVisibleProperty =
            DependencyProperty.Register("IsMinuteVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsMinuteVisible));

        private static void On_IsMinuteVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[8].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[9].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
            }
        }
        #endregion
        #region IsSecoundVisible
        public bool IsSecoundVisible
        {
            get { return (bool)GetValue(IsSecoundVisibleProperty); }
            set { SetValue(IsSecoundVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsSecoundVisibleProperty =
            DependencyProperty.Register("IsSecoundVisible", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(true, On_IsSecoundVisible));

        private static void On_IsSecoundVisible(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                Grid innerGrid = (Grid)sender.Content;
                innerGrid.ColumnDefinitions[10].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
             ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
                innerGrid.ColumnDefinitions[11].Width = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true
                    ? GridLength.Auto : new GridLength(0);
            }
        }
        #endregion

        #region IsReadOnly
        public bool IsReadOnly
        {
            get { return (bool)GetValue(IsReadOnlyProperty); }
            set { SetValue(IsReadOnlyProperty, value); }
        }
        public static readonly DependencyProperty IsReadOnlyProperty =
            DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(TimeSpanBox), new PropertyMetadata(false, OnIsReadOnly_Changed));

        private static void OnIsReadOnly_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null)
            {
                bool isreadOnly = e.NewValue != null && e.NewValue is bool && (bool)e.NewValue == true;
                sender.txtYear.IsReadOnly = isreadOnly;
                sender.txtYear.IsReadOnly = isreadOnly;
                sender.txtYear.IsReadOnly = isreadOnly;
                sender.txtYear.IsReadOnly = isreadOnly;
                sender.txtYear.IsReadOnly = isreadOnly;
                sender.txtYear.IsReadOnly = isreadOnly;
            }
        }
        #endregion        
        #region Year
        public int Year
        {
            get { return (int)GetValue(YearProperty); }
            set { SetValue(YearProperty, value); }
        }
        public static readonly DependencyProperty YearProperty =
            DependencyProperty.Register("Year", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, On_Year_Changed));

        private static void On_Year_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Year_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Year("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Year(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Year("0");
                }
            }
        }
        #endregion
        #region Month
        public int Month
        {
            get { return (int)GetValue(MonthProperty); }
            set { SetValue(MonthProperty, value); }
        }
        public static readonly DependencyProperty MonthProperty =
            DependencyProperty.Register("Month", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, On_Month_Changed));
        private static void On_Month_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Month_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Month("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Month(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Month("0");
                }
            }
        }
        #endregion
        #region Day
        public int Day
        {
            get { return (int)GetValue(DayProperty); }
            set { SetValue(DayProperty, value); }
        }
        public static readonly DependencyProperty DayProperty =
            DependencyProperty.Register("Day", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, ON_Day_Changed));

        private static void ON_Day_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Day_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Day("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Day(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Day("0");
                }
            }
        }
        #endregion
        #region Hour
        public int Hour
        {
            get { return (int)GetValue(HourProperty); }
            set { SetValue(HourProperty, value); }
        }
        public static readonly DependencyProperty HourProperty =
            DependencyProperty.Register("Hour", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, On_Hour_CHanged));
        private static void On_Hour_CHanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Hour_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Hour("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Hour(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Hour("0");
                }
            }
        }
        #endregion
        #region Minutes
        public int Minutes
        {
            get { return (int)GetValue(MinutesProperty); }
            set { SetValue(MinutesProperty, value); }
        }
        public static readonly DependencyProperty MinutesProperty =
            DependencyProperty.Register("Minutes", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, On_Minute_CHanged));

        private static void On_Minute_CHanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Minute_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Minute("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Minute(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Minute("0");
                }
            }
        }
        #endregion
        #region Secound
        public int Secound
        {
            get { return (int)GetValue(SecoundProperty); }
            set { SetValue(SecoundProperty, value); }
        }
        public static readonly DependencyProperty SecoundProperty =
            DependencyProperty.Register("Secound", typeof(int), typeof(TimeSpanBox), new PropertyMetadata(0, On_Secound_Changed));

        private static void On_Secound_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null && selector.CanRaiseTxtChaned_Secound_AttachDP == true)
            {
                if (e.NewValue == null)
                    selector.SetText_Secound("");
                else if (e.NewValue.GetType().Equals(typeof(int)))
                {
                    selector.SetText_Secound(int.Parse(e.NewValue.ToString()).ToString());
                }
                else
                {
                    selector.SetText_Secound("0");
                }
            }
        }
        #endregion

        #region 文本
        public string YearText
        {
            get { return (string)GetValue(YearTextProperty); }
            set { SetValue(YearTextProperty, value); }
        }
        public static readonly DependencyProperty YearTextProperty =
            DependencyProperty.Register("YearText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));


        public string MonthText
        {
            get { return (string)GetValue(MonthTextProperty); }
            set { SetValue(MonthTextProperty, value); }
        }
        public static readonly DependencyProperty MonthTextProperty =
            DependencyProperty.Register("MonthText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));


        public string DayText
        {
            get { return (string)GetValue(DayTextProperty); }
            set { SetValue(DayTextProperty, value); }
        }
        public static readonly DependencyProperty DayTextProperty =
            DependencyProperty.Register("DayText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));


        public string HourText
        {
            get { return (string)GetValue(HourTextProperty); }
            set { SetValue(HourTextProperty, value); }
        }
        public static readonly DependencyProperty HourTextProperty =
            DependencyProperty.Register("HourText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));


        public string MinuteText
        {
            get { return (string)GetValue(MinuteTextProperty); }
            set { SetValue(MinuteTextProperty, value); }
        }
        public static readonly DependencyProperty MinuteTextProperty =
            DependencyProperty.Register("MinuteText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));

        public string SecoundText
        {
            get { return (string)GetValue(SecoundTextProperty); }
            set { SetValue(SecoundTextProperty, value); }
        }
        public static readonly DependencyProperty SecoundTextProperty =
            DependencyProperty.Register("SecoundText", typeof(string), typeof(TimeSpanBox), new PropertyMetadata("0"));
        #endregion

        #region UpDownBtnVisible
        public Visibility UpDownBtnVisible
        {
            get { return (Visibility)GetValue(UpDownBtnVisibleProperty); }
            set { SetValue(UpDownBtnVisibleProperty, value); }
        }
        public static readonly DependencyProperty UpDownBtnVisibleProperty =
            DependencyProperty.Register("UpDownBtnVisible", typeof(Visibility), typeof(TimeSpanBox), new PropertyMetadata(Visibility.Collapsed, OnUpDownBtnVisible_Changed));

        private static void OnUpDownBtnVisible_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox selector = d as TimeSpanBox;
            if (selector != null)
            {
                selector.gridShow.Visibility = e.NewValue != null && e.NewValue is Visibility && (Visibility)e.NewValue == Visibility.Visible
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            }
        }
        #endregion

        #region 标题颜色
        public Brush TitleBrush
        {
            get { return (Brush)GetValue(TitleBrushProperty); }
            set { SetValue(TitleBrushProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TitleBrush.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleBrushProperty =
            DependencyProperty.Register("TitleBrush", typeof(Brush), typeof(TimeSpanBox), new PropertyMetadata(new SolidColorBrush(Colors.Black), on_TitleBrush_CHanged));

        private static void on_TitleBrush_CHanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            TimeSpanBox sender = d as TimeSpanBox;
            if (sender != null && e.NewValue != null && e.NewValue is Brush)
            {
                Brush newBrush = ((Brush)e.NewValue);
                sender.txtbYear.Foreground = newBrush;
                sender.txtbMonth.Foreground = newBrush;
                sender.txtbDay.Foreground = newBrush;
                sender.txtbMinute.Foreground = newBrush;
                sender.txtbHour.Foreground = newBrush;
                sender.txtbSecound.Foreground = newBrush;
            }
            else if (e.NewValue != null || !(e.NewValue is Brush))
            {
                sender.txtbYear.Foreground = new SolidColorBrush(Colors.Black);
                sender.txtbMonth.Foreground = new SolidColorBrush(Colors.Black);
                sender.txtbDay.Foreground = new SolidColorBrush(Colors.Black);
                sender.txtbMinute.Foreground = new SolidColorBrush(Colors.Black);
                sender.txtbHour.Foreground = new SolidColorBrush(Colors.Black);
                sender.txtbSecound.Foreground = new SolidColorBrush(Colors.Black);
            }
        }
        #endregion

    }
}

如何使用:

        xmlns:local="clr-namespace:TimeEditerDemo"
....



            <!--不显示年,不现实月,不显示秒,显示按钮,天时分红字,-->
            <local:TimeSpanBox x:Name="pick" Height="25" TitleBrush="Red"
                    IsYearVisible="False" IsMonthVisible="False" IsSecoundVisible="False" UpDownBtnVisible="Visible"/>
            <TextBox x:Name="inputNum"/>
            <Button Content="Set Year" Click="Button_Click"/>
            <TextBlock Margin="0 10 0 0">
                <Run Text="当前时间:"/>
                <Run Text="{Binding Year,ElementName=pick}"/>
                <Run Text="年"/>
                  <Run Text="{Binding Month,ElementName=pick}"/>
                <Run Text="月"/>
                  <Run Text="{Binding Day,ElementName=pick}"/>
                <Run Text="日"/>
                  <Run Text="{Binding Hour,ElementName=pick}"/>
                <Run Text="时"/>
                  <Run Text="{Binding Minutes,ElementName=pick}"/>
                <Run Text="分"/>
                  <Run Text="{Binding Secound,ElementName=pick}"/>
                <Run Text="秒"/>
            </TextBlock>

            <x:Code>
                private void Button_Click(object sender, RoutedEventArgs e)
                {
                    int tempValue = 0;
                    pick.Day = int.TryParse(this.inputNum.Text, out tempValue) ? tempValue : 0;
                }
            </x:Code>

上述控件待完善: 给 Year、Month、Day、Hour、Minite、Secound、负值的时候的CoerceValueCallback

缺少对应的Year_Change  Month_Change Day_Change Hour_CHange Minite_Change Secound_Chang 等事件

//本人懒的弄了,UI也没有任何优化

运行结果

 Bug Fix 初始化时设置时分秒后显示值为0

原因: UserControl 种的Initial事件种如果包含的BInding是Relative,不能及时更新对应的绑定源,如下

<TextBox Grid.Column="0" BorderBrush="Transparent" BorderThickness="0" x:Name="txtYear" VerticalContentAlignment="Center"
                 InputMethod.IsInputMethodEnabled="False" TextAlignment="Right" Padding="0,0,1,0"
                 Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:TimeSpanBox},Path=YearText,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
原文地址:https://www.cnblogs.com/wandia/p/13953466.html