wpf timePicker 时间选择控件

wpf里有日期选择控件,但没有时间选择控件。其他地方也有类似的,但效果并不太好,而且复杂。所以就自己写了个。参考codeproject上的。

分两部分。

第一部分是.cs文件。也就是control控件的内部逻辑。定义相关属性,以及委托。代码如下:

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 CustomControls
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:时间选择控件"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:时间选择控件;assembly=时间选择控件"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目,然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:TimeSpanPicker/>
    ///
    /// </summary>
    public class TimeSpanPicker : Control
    {
        static TimeSpanPicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TimeSpanPicker), new FrameworkPropertyMetadata(typeof(TimeSpanPicker)));
        }

        /// <summary>
        /// 时间控制方法,自动计算时间
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            if (e.Property == HourProperty)
            {
                int hour = (int)e.NewValue;
                if (hour == 24)
                {
                    SetValue(HourProperty, 23);
                }
                else if (hour==-1)
                {
                    SetValue(HourProperty, 0);
                }
                SetValue(TimeSpanProperty, new TimeSpan(Hour, Minute, Second));
            }
            else if (e.Property == MinuteProperty)
            {
                int minute = (int)e.NewValue;
                if (minute == -1)
                {
                    if (Hour == 0)
                    {
                        SetValue(MinuteProperty, 0);
                    }
                    else
                    {
                        SetValue(MinuteProperty, 59);
                        SetValue(HourProperty, Hour - 1);
                    }
                }
                else if (minute == 60)
                {
                    if (Hour == 24)
                    {
                        SetValue(MinuteProperty, 59);
                    }
                    else
                    {
                        SetValue(MinuteProperty, 0);
                        SetValue(HourProperty, Hour + 1);
                    }
                }
                SetValue(TimeSpanProperty, new TimeSpan(Hour, Minute, Second));
            }
            else if (e.Property == SecondProperty)
            {
                int second = (int)e.NewValue;
                if (second == -1)
                {
                    if (Minute > 0 || Hour > 0)
                    {
                        SetValue(SecondProperty, 59);
                        SetValue(MinuteProperty, Minute - 1);
                    }
                    else
                    {
                        SetValue(SecondProperty, 0);
                    }
                }
                else if (second == 60)
                {

                    SetValue(SecondProperty, 0);
                    SetValue(MinuteProperty, Minute + 1);

                }
                //设置时间
                SetValue(TimeSpanProperty, new TimeSpan(Hour, Minute, Second));
            }
            else if (e.Property==TimeSpanProperty)
            {
                TimeSpan ts=(TimeSpan)e.NewValue;

                SetValue(SecondProperty, ts.Seconds);                SetValue(MinuteProperty, ts.Minutes);           SetValue(HourProperty, ts.Hours);  
            }
        }

        public TimeSpan TimeSpan
        {
            get { return (TimeSpan)GetValue(TimeSpanProperty); }
            set { SetValue(TimeSpanProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TimeSpan.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TimeSpanProperty =
            DependencyProperty.Register("TimeSpan", typeof(TimeSpan), typeof(TimeSpanPicker), new PropertyMetadata(TimeSpan.Zero));

        public int Hour
        {
            get { return (int)GetValue(HourProperty); }
            set { SetValue(HourProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Hour.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HourProperty =
            DependencyProperty.Register("Hour", typeof(int), typeof(TimeSpanPicker), new PropertyMetadata(0));

        public int Minute
        {
            get { return (int)GetValue(MinuteProperty); }
            set { SetValue(MinuteProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Minute.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MinuteProperty =
            DependencyProperty.Register("Minute", typeof(int), typeof(TimeSpanPicker), new PropertyMetadata(0));

        public int Second
        {
            get { return (int)GetValue(SecondProperty); }
            set { SetValue(SecondProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Second.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SecondProperty =
            DependencyProperty.Register("Second", typeof(int), typeof(TimeSpanPicker), new PropertyMetadata(0)); 

    }
}

另外,有时候时间选择控件的前台是不希望让用户输入字符及符号的。只能让用户输入int型的时分秒。所以定义了一个numbericTextbox。也就是只能输入数字的文本框。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace CustomControls
{
    public class NumbiricTextBox : TextBox
    {
        private static Regex regex = new Regex("[0-9]+");
        public NumbiricTextBox()
        {
            SetValue(InputMethod.IsInputMethodEnabledProperty, false);//禁用输入法
            DataObject.AddPastingHandler(this, TextBoxPasting);//粘贴时候判断
            this.MaxLength = 2;//设置长度,避免过多输入
        }

        /// <summary>
        /// 输入判定,只能输入数字 大于0
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            e.Handled = !regex.IsMatch(e.Text);
        }

        /// <summary>
        /// 滚轮改变值大小
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
        {
            base.OnPreviewMouseWheel(e);

           var x =   e.Source;
           if (x!=null&&x is NumbiricTextBox)
           {
               NumbiricTextBox tbx = x as NumbiricTextBox;
               if (e.Delta > 0)
               {
                   tbx.Text = (int.Parse(tbx.Text) + 1).ToString();
               }
               else
               {
                   tbx.Text = (int.Parse(tbx.Text) - 1).ToString();
               }
           }
        }
        //保证值不为空····························
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            base.OnLostFocus(e);
            if (string.IsNullOrEmpty(this.Text))
            {
                this.Text = "0";
            }
        }
        /// <summary>
        /// 粘贴事件检查
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBoxPasting(object sender, DataObjectPastingEventArgs e)
        {
            if (e.DataObject.GetDataPresent(typeof(String)))
            {
                String text = (String)e.DataObject.GetData(typeof(String));
                if (!regex.IsMatch(text))
                {
                    e.CancelCommand();
                }
            }
            else
            {
                e.CancelCommand();
            }
        }

    }
}

我们定义了控件,但还是不能用的,必须给控件模板才能 用。控件模板就是根据需要来自定义样式了。我做了个简单的。xaml代码如下:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomControls">
    <!--<local:Hour2StringConverter x:Key="HourConverter"/>-->

    <Style TargetType="{x:Type local:TimeSpanPicker}">

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TimeSpanPicker}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <StackPanel Orientation="Horizontal">
                            <local:NumbiricTextBox x:Name="hourTbx" MinWidth="20"  Text="{Binding Path=Hour, RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}"  VerticalAlignment="Center" Height="Auto" >
                            </local:NumbiricTextBox>
                            <Label Content=":" VerticalAlignment="Center" Height="Auto"/>
                            <local:NumbiricTextBox  MinWidth="20" Text="{Binding Path=Minute,RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Height="Auto"></local:NumbiricTextBox>
                            <Label Content=":" VerticalAlignment="Center" Height="Auto"/>
                            <local:NumbiricTextBox  MinWidth="20" Text="{Binding Path=Second,RelativeSource={RelativeSource TemplatedParent},UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Height="Auto"></local:NumbiricTextBox>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

这样就能用了。

调用的时候可以写个测试程序:

前台xaml:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CustomControls" x:Class="CustomControls.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:NumbiricTextBox HorizontalAlignment="Left" Margin="74,183,0,0" TextWrapping="Wrap"  VerticalAlignment="Top" Height="69" Width="257" />
        <local:TimeSpanPicker HorizontalAlignment="Left" Margin="62,69,0,0" VerticalAlignment="Top" Height="28" Width="180" x:Name="timePicker"  />
        <Button Content="获取时间" HorizontalAlignment="Left" Margin="62,117,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
        <Label x:Name="lb1" Content="Label" HorizontalAlignment="Left" Margin="164,117,0,0" VerticalAlignment="Top"/>
        <Button Content="设置时间" HorizontalAlignment="Left" Margin="51,32,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>

    </Grid>
</Window>

后台cs文件:

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 CustomControls
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            lb1.Content = timePicker.TimeSpan.ToString();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            timePicker.SetValue(TimeSpanPicker.TimeSpanProperty, new TimeSpan(DateTime.Now.Hour,DateTime.Now.Minute,DateTime.Now.Second));
        }

    }
}

是不是很简单呢?

效果如下:

感谢每一位阅读此篇博客的人,希望可以帮助到你。

时间: 2024-10-07 07:04:31

wpf timePicker 时间选择控件的相关文章

共享MFC每周时间选择控件代码

自己写的周时间选择控件,原理就是在Static上用GDI画图. 支持选择每周内每一天内的任意时间段,可以任意拖动修改时间段,任意合并时间段 效果如下图: VS2012代码下载:https://github.com/yinkaisheng/MFCWeekTimePicker

WPF多线程访问控件

大家知道WPF中多线程访问UI控件时会提示UI线程的数据不能直接被其他线程访问或者修改,该怎样来做呢? 分下面两种情况 1.WinForm程序 1 2 1)第一种方法,使用委托: 3 private delegate void SetTextCallback(string text); 4 private void SetText(string text) 5 { 6 // InvokeRequired需要比较调用线程ID和创建线程ID 7 // 如果它们不相同则返回true 8 if (thi

WPF中Ribbon控件的使用

这篇博客将分享如何在WPF程序中使用Ribbon控件.Ribbon可以很大的提高软件的便捷性. 上面截图使Outlook 2010的界面,在Home标签页中,将所属的Menu都平铺的布局,非常容易的可以找到想要的Menu.在Outlook 2003时代,将Home下面的Menu都垂直的排列下来,操作的便捷程度降低了很多.Ribbon的布局会随着窗体的变化动态的调整. 上面的图片中标注了Ribbon的4个区块. 下面我们就在WPF中使用Ribbon控件来实现一个简单的界面. 1. 添加System

Wpf使用Winform控件后Wpf元素被Winform控件遮盖问题的解决

有人会说不建议Wpf中使用Winform控件,有人会说建议使用Winform控件在Wpf下的替代方案,然而在实际工作中由于项目的特殊需求,考虑到时间.成本等因素,往往难免会碰到在WPF中使用Winfrom控件的问题,我们知道Wpf可以通过使用WindowsFormsHost容器调用Winform控件,但是在一些场合需要将Wpf元素显示在Winform控件的上层,此时就会出现Wpf元素被Winform控件遮盖的问题. 一.场景再现 接到公司命令,在时间紧迫的情况下,需要将原来的Winform程序(

Xceed WPF 主题皮肤控件Xceed Professional Themes for WPF详细介绍

Xceed Professional Themes for WPF是一款为你的整个应用程序提供完美WPF主题风格的控件,包含Office 2007和Windows 7,可以应用到任何微软官方的WPF控件. 具体功能: WPF/.NET技术: 完全基于WPF创建,由C#和XAML编写,适用于.NET 3.5和4.0 完整的XAML源代码可用 兼容CLS 完全整合帮助文档到Visual Studio里 主要功能: 提供了7种预定义WPF主题风格 Glass 主题风格 Media 主题风格,使您的应用

WPF送走控件的focus方法

我们可以调用Focus()方法,让WPF控件获得焦点, 那我现在不想要焦点了, 如何把这个包袱抛出去? 可以,  恩, 没有Unfocus(), 但下面的方法也许可行(把焦点抛给另一个不知道的控件): myControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); WPF送走控件的focus方法,布布扣,bubuko.com

在WPF的WebBrowser控件中屏蔽脚本错误的提示

在WPF中使用WebBrowser控件显示网页时,经常会报脚本错误的提示,如何屏蔽掉这些错误提示呢.方法是定义如下方法: public void SuppressScriptErrors(WebBrowser wb, bool Hide) { FieldInfo fiComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);

InteropBitmap指定内存,绑定WPF的Imag控件时刷新问题。

1.InteropBitmap指定内存,绑定WPF的Imag控件的Source属性 创建InteropBitmap的时候,像素的格式必须为PixelFormats.Bgr32, 如果不是的话在绑定到Image控件的Source属性,刷新新界面(BitmapSource.Invalidate())的时候会造成内存泄露. 2. 内存映射: //内存共享类 internal class Win32Mess { [DllImport("VCamBridge.dll", EntryPoint =

Extjs DateTime 日期时间选择控件 (非点击日期强制选择) 支持4.0以上

Extjs的日期控件,仅仅能支持到日期选择,对时间的选择并不完好.而网上下载的控件,都是基于Ext.form.dateField 开发.在选中日期后自己主动选择,并隐藏此选择窗体. 在经过一番改造后,最终做好了一个带确认button的时间选择控件.截图例如以下 详细代码在附件里.  要想正常使用,还须要加一段css样式: .x-datepicker-sel { border-color: rgb(224, 162, 162); border-style: solid; border-width: