You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinForms DateTimePicker切换月份默认选1日的自定义需求咨询

解决WinForms DateTimePicker切换月份保留原日期的问题

这个痛点我太懂了——默认情况下用左右箭头切换月份时,控件会自动跳到当月1号,完全忽略用户之前选中的日期。要解决这个问题,我们可以通过自定义控件继承DateTimePicker,重写WndProc方法来拦截系统消息,手动保留日期的“日”部分。

实现步骤:

  • 创建一个自定义控件,继承自DateTimePicker
  • 重写WndProc方法,捕获WM_NOTIFY消息
  • 在NM_DATETIMECHANGE事件中,判断是否是月份/年份切换导致的日期变更,然后恢复原有的“日”值

完整代码示例:

using System;
using System.Windows.Forms;

public class CustomDateTimePicker : DateTimePicker
{
    private DateTime _lastSelectedDate;

    protected override void OnValueChanged(EventArgs e)
    {
        // 记录用户手动选择的日期
        _lastSelectedDate = this.Value;
        base.OnValueChanged(e);
    }

    protected override void WndProc(ref Message m)
    {
        const int WM_NOTIFY = 0x004E;
        const int DTN_FIRST = 0xFFFFFD00;
        const int DTN_DATETIMECHANGE = DTN_FIRST + 1;

        if (m.Msg == WM_NOTIFY)
        {
            var nmhdr = (NMHDR)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
            if (nmhdr.code == DTN_DATETIMECHANGE)
            {
                var dtChange = (NMDATETIMECHANGE)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(NMDATETIMECHANGE));
                
                // 检查是否是月份或年份变更(而非用户手动选择新日期)
                if (dtChange.dwFlags == 0)
                {
                    // 计算新日期:保留原有的日,替换为新的年月
                    DateTime newDate;
                    try
                    {
                        newDate = new DateTime(dtChange.st.wYear, dtChange.st.wMonth, _lastSelectedDate.Day);
                    }
                    catch (ArgumentOutOfRangeException)
                    {
                        // 如果原有的日在新月份不存在(比如31号切换到2月),则取新月份的最后一天
                        newDate = new DateTime(dtChange.st.wYear, dtChange.st.wMonth, 1).AddMonths(1).AddDays(-1);
                    }
                    
                    // 更新控件值,同时避免触发不必要的ValueChanged
                    this.Value = newDate;
                    _lastSelectedDate = newDate;
                    return;
                }
            }
        }
        base.WndProc(ref m);
    }

    // 定义需要用到的Win32结构体
    private struct NMHDR
    {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }

    private struct SYSTEMTIME
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }

    private struct NMDATETIMECHANGE
    {
        public NMHDR nmhdr;
        public uint dwFlags;
        public SYSTEMTIME st;
    }
}

代码说明:

  • _lastSelectedDate用来记录用户最后手动选中的日期,确保我们知道要保留哪一天
  • 在WndProc中拦截DTN_DATETIMECHANGE消息,这个消息会在日期变更时触发
  • 当dwFlags为0时,说明是通过月份/年份箭头切换导致的变更,而非用户点击选择日期
  • 处理特殊情况:如果原日期的“日”在新月份不存在(比如31号切到4月),自动取新月份的最后一天,避免报错

使用方法:

把这个自定义控件添加到你的WinForms项目中,直接在设计器里拖放使用,或者在代码中实例化,效果和原生DateTimePicker完全一样,只是切换月份时会保留原有的日期。

内容的提问来源于stack exchange,提问作者Movva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:10:01