DateTimePicker automatically move to next datepart

时间秒杀一切 提交于 2019-11-27 23:13:38
lc.

As @Wael Dalloul says, there is no property to do what you want. After a lot of fiddling and Spy++ work, I came upon the following solution:

  1. Inheriting from System.Windows.Forms.DateTimePicker, and declaring private fields for flags:

    public class DPDateTimePicker : DateTimePicker
    {
        private bool selectionComplete = false;
        private bool numberKeyPressed = false;
    
  2. Defining constants and structs for Win API:

        private const int WM_KEYUP = 0x0101;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_REFLECT = 0x2000;
        private const int WM_NOTIFY = 0x004e;
    
        [StructLayout(LayoutKind.Sequential)]
        private struct NMHDR
        {
           public IntPtr hwndFrom;
           public IntPtr idFrom;
           public int Code;
        }    
    

    It's also necessary to include a using statement for System.Runtime.InteropServices in order to use Win API.

  3. Override OnKeyDown and set or clear a flag based on if the key pressed was a number (and clear the second flag below).

    protected override void OnKeyDown(KeyEventArgs e)
    {
        numberKeyPressed = (e.Modifiers == Keys.None && ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) || (e.KeyCode != Keys.Back && e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9)));
        selectionComplete = false;
        base.OnKeyDown(e);
    }
    
  4. Override WndProc and trap the WM_REFLECT+WM_NOTIFY message, extract the NMHDR from lParam, then set another flag if the code is -759 (this event is triggered after one of the fields is completely filled in with the keyboard and a date is selected).

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_REFLECT + WM_NOTIFY)
        {
            var hdr = (NMHDR)m.GetLParam(typeof(NMHDR));
            if (hdr.Code == -759) //date chosen (by keyboard)
                selectionComplete = true;
        }
        base.WndProc(ref m);
    }
    
  5. Override OnKeyUp and if both flags are set and the key pressed was a number, manually call base.WndProc with a WM_KEYDOWN followed by a WM_KEYUP with Keys.Right, then clear your flags. You can set the lParam of these messages to 0 and not worry about it, and HWnd is of course this.Handle.

    protected override void OnKeyUp(KeyEventArgs e)
    {
        base.OnKeyUp(e);
        if (numberKeyPressed && selectionComplete &&
            (e.Modifiers == Keys.None && ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) || (e.KeyCode != Keys.Back && e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9))))
        {
            Message m = new Message();
            m.HWnd = this.Handle;
            m.LParam = IntPtr.Zero;
            m.WParam = new IntPtr((int)Keys.Right); //right arrow key
            m.Msg = WM_KEYDOWN;
            base.WndProc(ref m);
            m.Msg = WM_KEYUP;
            base.WndProc(ref m);
            numberKeyPressed = false;
            selectionComplete = false;
        }
    }
    

Apologies for the lack of blank lines in the code, but it wouldn't display right with the blank lines, so I took them out. Trust me, this is the more readable version.

It will work on winforms datetimepicker

on value change event paste the below code

sendkeys.send(".");

There is no Property to make it work like what you want, I think you have to handle keypress event and do it by code.

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