本文介绍了如何将DateTimePicker下拉列表设置为仅选择Years或Months?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

就像



在Visual Studio 2017上进行了测试。

.Net Framework 4.8(仅)。

 使用System.ComponentModel; 
使用System.Runtime.InteropServices;
使用System.Windows.Forms;

[DesignerCategory( Code)]
公共类MonthYearPicker:DateTimePicker
{
私有字符串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),说明(将当前选择模式设置为月或年)]
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)]
[类别(外观),说明(在日历控件底部显示或隐藏\ Today\日期))]
public bool ShowToday {
get => m_ShowToday;
set {
if(value!= m_ShowToday){
m_ShowToday = value;
ShowMonCalToday(m_ShowToday);
}
}
}

[DefaultValue( yyyy)]
[Browsable(false),EditorBrowsable(EditorBrowsableState.Never)]
公共新字符串CustomFormat {
get => base.CustomFormat;
set => base.CustomFormat = m_CustomFormat;
}

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

受保护的覆盖无效OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
ShowMonCalToday(m_ShowToday);
}

受保护的覆盖无效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样式= SendMessage(this.Handle,DTM_GETMCSTYLE,0,0).ToInt32();
样式=显示?样式&〜(int)MonCalStyles.MCS_NOTODAY:样式| (int)MonCalStyles.MCS_NOTODAY;
SendMessage(this.Handle,DTM_SETMCSTYLE,0,styles);
}

受保护的覆盖无效WndProc(ref Message m)
{
switch(m.Msg){
case WM_NOTIFY:
var nmh =(NMHDR)m.GetLParam(typeof(NMHDR));
开关(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);
}
休息;
默认值:
// NOP:添加更多通知处理程序...
中断;
}
休息;
默认值:
// NOP:添加更多消息处理程序...
break;
}
base.WndProc(ref m);
}

公共枚举SelectionViewMode:int
{
月= MonCalView.MCMV_YEAR,
年= MonCalView.MCMV_DECADE,
}

//移至NativeMethods类,最终
[DllImport( user32.dll,SetLastError = true,CharSet = CharSet.Auto)]
内部静态外部IntPtr SendMessage(IntPtr hWnd,int Msg,int wParam,int lParam);

内部常量int WM_NOTIFY = 0x004E;
internal const int MCN_VIEWCHANGE = -750;

内部常量int DTM_FIRST = 0x1000;
内部常量int DTM_GETMONTHCAL = DTM_FIRST + 8;
内部常量int DTM_SETMCSTYLE = DTM_FIRST + 11;
内部常量int DTM_GETMCSTYLE = DTM_FIRST + 12;
内部常量int DTM_CLOSEMONTHCAL = DTM_FIRST + 13;

内部常量int MCM_FIRST = 0x1000;
内部常量int MCM_GETCURRENTVIEW = MCM_FIRST + 22;
内部常量int MCM_SETCURRENTVIEW = MCM_FIRST + 32;

[StructLayout(LayoutKind.Sequential)]
内部结构NMHDR
{
public IntPtr hwndFrom;
public UIntPtr idFrom;
public int code;
}

[StructLayout(LayoutKind.Sequential)]
内部结构NMVIEWCHANGE
{
public NMHDR nmhdr;
公共MonCalView dwOldView;
公共MonCalView dwNewView;
}

内部枚举MonCalView:int
{
MCMV_MONTH = 0,
MCMV_YEAR = 1,
MCMV_DECADE = 2,
MCMV_CENTURY = 3
}

内部枚举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 $ b}


Like discussed in How can I set the datetimepicker dropdown to show Months only there was a possibility to overwrite DateTimePicker to get a MonthPicker.

I have read a lot of sites but didn't figure out how to do similar to get a YearPicker.
Maybe someone can help out.

解决方案

This Custom Control tweaks a little the standard DateTimePicker to get a Year or Month selection style only.

► The standard DateTimePicker's CustomFormat and Format properties are disabled, setting internally the former to yyyy or MMMM (a simple modification can add different formats) and the latter to DateTimePickerFormat.Custom. These properties are hidden from the PropertyGrid an cannot be changed.

► The browsing functionality is maintained, but limited to the Decade/Year or Decade/Year/Month selection.
Clicking the DTP's Title Area, brings on the Decade selector and the previous and next Buttons are of course functional (these can only show years).

► The DTP is closed and the current value set when a MCN_VIEWCHANGE notification reveals, passing the current selection level in a NMVIEWCHANGE structure, that the current selection has reached the View Mode set by the SelectionMode property.
This property value is an enumerator which, in turn, reflects the MonthCalendar's MCM_SETCURRENTVIEW message values.

► The current View is set sending a MCM_SETCURRENTVIEW message to the MonthCalendar control, changing the default View to MCMV_DECADE or MCMV_YEAR (depending on the current SelectionMode) each time the MonthCalendar control is shown. The opening animation is then preserved.

► The only style changed is MCS_NOTODAY, set in the OnHandleCreated method. It can be switched on/off at any time, calling the ShowMonCalToday() method.
This style shows the Today date, at the bottom of the DateTimerPicker. It sets the current Year or Month value when clicked.


This is how it works:

Tested on VisualStudio 2017.
.Net Framework 4.8 (only).

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下拉列表设置为仅选择Years或Months?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 03:11
查看更多