如何将 DateTimePicker 下拉菜单设置为仅选择年或月?

Posted

技术标签:

【中文标题】如何将 DateTimePicker 下拉菜单设置为仅选择年或月?【英文标题】:How can I set the DateTimePicker dropdown to select Years or Months only? 【发布时间】:2020-04-17 17:32:34 【问题描述】:

就像在How can I set the datetimepicker dropdown to show Months only 中讨论的那样,有可能覆盖DateTimePicker 以获得MonthPicker

我已经阅读了很多网站,但不知道如何做类似的事情来获得YearPicker。 也许有人可以帮忙。

【问题讨论】:

不是一个选项。考虑使用 NumericUpDown 或 ComboBox 来选择年份。 【参考方案1】:

此自定义控件对标准 DateTimePicker 稍作调整,以仅获得年份或月份选择样式。

▶ 标准 DateTimePicker 的 CustomFormatFormat 属性被禁用,在内部将前者设置为 yyyy 或 @987654330 @(简单的修改可以添加不同的格式)而后者为DateTimePickerFormat.Custom。这些属性在 PropertyGrid 中是隐藏的,无法更改。

▶ 保留浏览功能,但仅限于十年/年或十年/年/月选择。 单击 DTP 的标题区域,打开年代选择器,上一个和下一个按钮当然是功能性的(这些只能显示年份)。

▶ 当MCN_VIEWCHANGE 通知显示时,DTP 关闭并设置当前值,通过NMVIEWCHANGE 结构中的当前选择级别,当前选择已达到 @987654334 设置的查看模式@ 属性。 此属性值是一个枚举器,它反过来反映 MonthCalendar 的 MCM_SETCURRENTVIEW 消息值。

▶ 当前视图设置为向 MonthCalendar 控件发送 MCM_SETCURRENTVIEW 消息,将默认视图更改为 MCMV_DECADEMCMV_YEAR(取决于当前 SelectionMode) 每次显示 MonthCalendar 控件时。然后会保留开场动画。

▶ 唯一改变的样式是MCS_NOTODAY,在OnHandleCreated 方法中设置。可以随时开启/关闭,调用ShowMonCalToday()方法。 此样式在 DateTimerPicker 底部显示 Today 日期。单击时设置当前的年或月值。


这就是它的工作原理:

在 VisualStudio 2017 上测试。 .Net Framework 4.8(仅限)。

using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;

[DesignerCategory("Code")]
public class MonthYearPicker : DateTimePicker

    private string m_CustomFormat = "yyyy";
    private DateTimePickerFormat m_Format = DateTimePickerFormat.Custom;
    private SelectionViewMode m_SelectionMode = SelectionViewMode.Year;
    private bool m_ShowToday = false;
    private IntPtr hWndCal = IntPtr.Zero;

    public MonthYearPicker() 
        base.CustomFormat = m_CustomFormat;
        base.Format = m_Format;
    

    [DefaultValue(SelectionViewMode.Year)]
    [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    [Category("Appearance"), Description("Set the current selection mode to either Month or Year")]
    public SelectionViewMode SelectionMode 
        get => m_SelectionMode;
        set 
            if (value != m_SelectionMode) 
                m_SelectionMode = value;
                m_CustomFormat = m_SelectionMode == SelectionViewMode.Year ? "yyyy" : "MMMM";
                base.CustomFormat = m_CustomFormat;
            
        
    

    [DefaultValue(false)]
    [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
    [Category("Appearance"), Description("Shows or hides \"Today\" date at the bottom of the Calendar Control")]
    public bool ShowToday 
        get => m_ShowToday;
        set 
            if (value != m_ShowToday) 
                m_ShowToday = value;
                ShowMonCalToday(m_ShowToday);
            
        
    

    [DefaultValue("yyyy")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public new string CustomFormat 
        get => base.CustomFormat;
        set => base.CustomFormat = m_CustomFormat;
    

    [DefaultValue(DateTimePickerFormat.Custom)]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public new DateTimePickerFormat Format 
        get => base.Format;
        set => base.Format = m_Format;
    

    protected override void OnHandleCreated(EventArgs e)
    
        base.OnHandleCreated(e);
        ShowMonCalToday(m_ShowToday);
    

    protected override void OnDropDown(EventArgs e)
    
        hWndCal = SendMessage(this.Handle, DTM_GETMONTHCAL, 0, 0);
        if (hWndCal != IntPtr.Zero) 
            SendMessage(hWndCal, MCM_SETCURRENTVIEW, 0, (int)(MonCalStyles)m_SelectionMode);
        
        base.OnDropDown(e);
    

    private void ShowMonCalToday(bool show)
    
        int styles = SendMessage(this.Handle, DTM_GETMCSTYLE, 0, 0).ToInt32();
        styles = show ? styles &~(int)MonCalStyles.MCS_NOTODAY : styles | (int)MonCalStyles.MCS_NOTODAY;
        SendMessage(this.Handle, DTM_SETMCSTYLE, 0, styles);
    

    protected override void WndProc(ref Message m)
    
        switch (m.Msg) 
            case WM_NOTIFY:
                var nmh = (NMHDR)m.GetLParam(typeof(NMHDR));
                switch (nmh.code) 
                    case MCN_VIEWCHANGE:
                        var nmView = (NMVIEWCHANGE)m.GetLParam(typeof(NMVIEWCHANGE));
                        if (nmView.dwNewView < (MonCalView)m_SelectionMode) 
                            SendMessage(this.Handle, DTM_CLOSEMONTHCAL, 0, 0);
                        
                        break;
                    default:
                        // NOP: Add more notifications handlers...
                        break;
                
                break;
            default:
                // NOP: Add more message handlers...
                break;
        
        base.WndProc(ref m);
    

    public enum SelectionViewMode : int
    
        Month = MonCalView.MCMV_YEAR,
        Year = MonCalView.MCMV_DECADE,
    

    // Move to a NativeMethods class, eventually
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    internal static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

    internal const int WM_NOTIFY = 0x004E;
    internal const int MCN_VIEWCHANGE = -750;

    internal const int DTM_FIRST = 0x1000;
    internal const int DTM_GETMONTHCAL = DTM_FIRST + 8;
    internal const int DTM_SETMCSTYLE = DTM_FIRST + 11;
    internal const int DTM_GETMCSTYLE = DTM_FIRST + 12;
    internal const int DTM_CLOSEMONTHCAL = DTM_FIRST + 13;

    internal const int MCM_FIRST = 0x1000;
    internal const int MCM_GETCURRENTVIEW = MCM_FIRST + 22;
    internal const int MCM_SETCURRENTVIEW = MCM_FIRST + 32;

    [StructLayout(LayoutKind.Sequential)]
    internal struct NMHDR
    
        public IntPtr hwndFrom;
        public UIntPtr idFrom;
        public int code;
    

    [StructLayout(LayoutKind.Sequential)]
    internal struct NMVIEWCHANGE
    
        public NMHDR nmhdr;
        public MonCalView dwOldView;
        public MonCalView dwNewView;
    

    internal enum MonCalView : int
    
        MCMV_MONTH = 0,
        MCMV_YEAR = 1,
        MCMV_DECADE = 2,
        MCMV_CENTURY = 3
    

    internal enum MonCalStyles : int
    
        MCS_DAYSTATE = 0x0001,
        MCS_MULTISELECT = 0x0002,
        MCS_WEEKNUMBERS = 0x0004,
        MCS_NOTODAYCIRCLE = 0x0008,
        MCS_NOTODAY = 0x0010,
        MCS_NOTRAILINGDATES = 0x0040,
        MCS_SHORTDAYSOFWEEK = 0x0080,
        MCS_NOSELCHANGEONNAV = 0x0100
    

【讨论】:

以上是关于如何将 DateTimePicker 下拉菜单设置为仅选择年或月?的主要内容,如果未能解决你的问题,请参考以下文章

根据下拉列表中选择的值设置 datetimepicker 格式

DateTimePicker:选择日期和时间

DateTimePicker AM/PM 下拉菜单

datetimepicker的下拉日历如何放大

DropDownList下拉菜单如何多选

下拉菜单选中后还是显示默认值