C# 如何将DateTimePicker下拉列表设置为仅选择年或月?

C# 如何将DateTimePicker下拉列表设置为仅选择年或月?,c#,winforms,datetimepicker,C#,Winforms,Datetimepicker,如中所述,有可能覆盖DateTimePicker以获得MonthPicker 我读过很多网站,但没有想到如何做类似的事情来获得一个年鉴 也许有人能帮上忙。这个自定义控件稍微调整了标准的日期时间选择器,使其只能选择年份或月份 ► 标准DateTimePicker的CustomFormat和Format属性被禁用,在内部将前者设置为yyyy或MMMM(简单的修改可以添加不同的格式),后者设置为DateTimePickPerformat.Custom。这些属性对PropertyGrid隐藏,无法更改

如中所述,有可能覆盖
DateTimePicker
以获得
MonthPicker

我读过很多网站,但没有想到如何做类似的事情来获得一个
年鉴


也许有人能帮上忙。

这个自定义控件稍微调整了标准的日期时间选择器,使其只能选择年份或月份

► 标准DateTimePicker的
CustomFormat
Format
属性被禁用,在内部将前者设置为
yyyy
MMMM
(简单的修改可以添加不同的格式),后者设置为
DateTimePickPerformat.Custom
。这些属性对PropertyGrid隐藏,无法更改

► 浏览功能将得到维护,但仅限于十年/年或十年/年/月选择。
单击DTP的标题区域,将显示“十年选择器”,当然,“上一个”和“下一个”按钮可以正常工作(这些按钮只能显示年份)

► 当通知通过结构中的当前选择级别显示当前选择已达到由
SelectionMode
属性设置的查看模式时,DTP将关闭并设置当前值。
此属性值是一个枚举数,它反过来反映MonthCalendar的消息值

► 当前视图设置为每次显示MonthCalendar控件时,向MonthCalendar控件发送一条
MCM\u SETCURRENTVIEW
消息,将默认视图更改为
MCMV\u Deced
MCMV\u YEAR
(取决于当前的
选择模式
)。然后将保留打开的动画

► 唯一更改的样式是,在
OnHandleCreated
方法中设置。它可以随时打开/关闭,调用
ShowMonCalToday()
方法。
此样式在DateTimerPicker的底部显示今天的日期。它设置单击时的当前年份或月份值


这就是它的工作原理:

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

使用System.ComponentModel;
使用System.Runtime.InteropServices;
使用System.Windows.Forms;
[设计分类(“代码”)]
公共类MonthYearPicker:DateTimePicker
{
私有字符串m_CustomFormat=“yyyy”;
私有DateTimePickPerformat m_格式=DateTimePickPerformat.Custom;
private SelectionViewMode m_SelectionMode=SelectionViewMode.Year;
private bool m_ShowToday=假;
私有IntPtr hWndCal=IntPtr.Zero;
公共蒙蒂耳机(){
base.CustomFormat=m_CustomFormat;
base.Format=m_格式;
}
[默认值(选择ViewMode.Year)]
[可浏览(true)、可编辑可浏览(editorbrowsebleState.Always)]
[类别(“外观”)、说明(“将当前选择模式设置为月份或年份”)]
公共选择查看模式选择模式{
get=>m_选择模式;
设置{
如果(值!=m_选择模式){
m_SelectionMode=值;
m_CustomFormat=m_SelectionMode==SelectionViewMode.Year?“yyyy”:“MMMM”;
base.CustomFormat=m_CustomFormat;
}
}
}
[默认值(false)]
[可浏览(true)、可编辑可浏览(editorbrowsebleState.Always)]
[类别(“外观”)、说明(“在日历控件底部显示或隐藏\“今天\”日期”)]
今天的公众集会{
get=>m_ShowToday;
设置{
如果(值!=m_ShowToday){
m_showtody=价值;
ShowMonCalToday(m_ShowToday);
}
}
}
[默认值(“yyyy”)]
[可浏览(false)、可编辑可浏览(editorbrowseblestate.Never)]
公共新字符串格式{
get=>base.CustomFormat;
set=>base.CustomFormat=m_CustomFormat;
}
[默认值(DateTimePickPerformat.Custom)]
[可浏览(false)、可编辑可浏览(editorbrowseblestate.Never)]
公共新DateTimePickPerformat格式{
get=>base.Format;
set=>base.Format=m_格式;
}
已创建受保护的重写无效OnHandleCreated(EventArgs e)
{
碱基。根据HandleCreated(e);
ShowMonCalToday(m_ShowToday);
}
受保护的覆盖无效OnDropDown(事件参数e)
{
hWndCal=SendMessage(this.Handle,DTM_GETMONTHCAL,0,0);
如果(hWndCal!=IntPtr.Zero){
发送消息(hWndCal,MCM_设置当前视图,0,(int)(MonCalStyles)m_选择模式);
}
基础.降速(e);
}
今天的私人void show(bool show)
{
int styles=SendMessage(this.Handle,DTM_GETMCSTYLE,0,0).ToInt32();
styles=show?styles&~(int)MonCalStyles.MCS_notaday:styles |(int)MonCalStyles.MCS_notaday;
SendMessage(this.Handle,DTM_SETMCSTYLE,0,style);
}
受保护的覆盖无效WndProc(参考消息m)
{
开关(m.Msg){
案件通知:
var nmh=(NMHDR)m.GetLParam(typeof(NMHDR));
交换机(nmh.代码){
案例MCN_视图更改:
var nmView=(NMVIEWCHANGE)m.GetLParam(typeof(NMVIEWCHANGE));
如果(nmView.dwNewView<(MonCalView)m_选择模式){
SendMessage(this.Handle,DTM_CLOSEMONTHCAL,0,0);
}
打破
违约:
//否:添加更多通知处理程序。。。
打破
}
打破
违约:
//否:添加更多消息处理程序。。。
打破
}
基准WndProc(参考m);
}
公共枚举选择视图模式:int
{
月=MonCalView.MCMV炭年,
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
    }
}