如何将datetimepicker下拉菜单设置为仅显示月份

14

所以,不是像这样点击下拉菜单会出现:

输入图像描述

我希望点击后下拉菜单是这样的:

输入图像描述

非常感谢您的任何帮助。 :)


哦,抱歉。我忘了提到。这是在Windows上。 - Bigboss
Windows窗体还是Windows WPF?我假设Windows = Windows窗体。 - Tyress
是的,它是Windows窗体。 - Bigboss
https://dev59.com/a1XTa4cB1Zd3GeqPyh0n#5271042 - Hans Passant
5个回答

6
使用Windows消息方法,您可以检测月历控件的显示并强制进行月视图,您还可以检测视图更改并在选择月份后将月历控件关闭以进行月到日视图更改。
实现它的最简单方法是覆盖DateTimePicker。
public class MonthPicker : DateTimePicker
{
    // initialize Format/CustomFormat to display only month and year.
    public MonthPicker()
    {
        Format = DateTimePickerFormat.Custom;
        CustomFormat = "MMMM yyyy";
    }

    // override Format to redefine default value (used by designer)
    [DefaultValue(DateTimePickerFormat.Custom)]
    public new DateTimePickerFormat Format
    {
        get => base.Format;
        set => base.Format = value;
    }

    // override CustomFormat to redefine default value (used by designer)
    [DefaultValue("MMM yyyy")]
    public new string CustomFormat
    {
        get => base.CustomFormat;
        set => base.CustomFormat = value;
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_NOFITY)
        {
            var nmhdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
            switch (nmhdr.code)
            {
                // detect pop-up display and switch view to month selection
                case -950:
                {
                    var cal = SendMessage(Handle, DTM_GETMONTHCAL, IntPtr.Zero, IntPtr.Zero);
                    SendMessage(cal, MCM_SETCURRENTVIEW, IntPtr.Zero, (IntPtr)1);
                    break;
                }

                // detect month selection and close the pop-up
                case MCN_VIEWCHANGE:
                {
                    var nmviewchange = (NMVIEWCHANGE)Marshal.PtrToStructure(m.LParam, typeof(NMVIEWCHANGE));
                    if (nmviewchange.dwOldView == 1 && nmviewchange.dwNewView == 0)
                    {
                        SendMessage(Handle, DTM_CLOSEMONTHCAL, IntPtr.Zero, IntPtr.Zero);
                    }

                    break;
                }
            }
        }
        base.WndProc(ref m);
    }

    private const int WM_NOFITY = 0x004e;
    private const int DTM_CLOSEMONTHCAL = 0x1000 + 13;
    private const int DTM_GETMONTHCAL = 0x1000 + 8;
    private const int MCM_SETCURRENTVIEW = 0x1000 + 32;
    private const int MCN_VIEWCHANGE = -750;

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential)]
    private struct NMHDR
    {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct NMVIEWCHANGE
    {
        public NMHDR nmhdr;
        public uint dwOldView;
        public uint dwNewView;
    }
}

@ZulqarnainJalil,你能提供更多的信息和背景吗?是什么没有起作用?你使用了哪个平台等等... - Orace
我正在使用Visualstudio 2017上的C# Windows表单应用程序。我已经将您的代码复制到我的form1.cs类中,但是对于我添加到form1中的日历控件没有任何反应。 - Zulqarnain Jalil
1
这不是它的工作方式。这是一个自定义用户控件。尝试获取有关此主题的信息。 - Orace
当然,我会尝试 :) - Zulqarnain Jalil
2
@ZulqarnainJalil,将代码放在一个单独的MonthPicker.cs文件中。编译。打开Form1设计器。该控件将出现在工具箱窗口的“*{AppName}组件”部分,并命名为“MonthPicker*”。将其作为新组件添加到主表单中。Et Voilà。 - Orace

4

因为我只需要用户选择一个月份,而不想让他们误以为日期也应该准确无误,因此唯一重要的是月份。 - Bigboss
2
@Bigboss 在 ComboBox 中列出月份的列表是您所需的标准解决方案。 - Jeremy Thompson
1
@JeremyThompson,但如果我想要一年呢? - Bigboss
@Bigboss,你有机会看一下这个链接吗?简单地解释一下,它讲的是WPF Toolkit中的日历控件,它有三种显示模式。其中一个是十年模式,可能适合你的需求。顺便说一句,我还没有尝试过,但我猜值得一试。否则,再用Combox。 - Dk358

1
尝试以下代码:

DateTime newDateValue = new DateTime(dateTimePicker_month.Value.Year, 1, 1);
dateTimePicker_month.Value = newDateValue;
dateTimePicker_month.Format = DateTimePickerFormat.Custom;
dateTimePicker_month.CustomFormat = "MMM-yyyy";
dateTimePicker_month.ShowUpDown = true;

要确保所有月份的值正确,对于有28/29天的二月份,您需要添加(1,1)。如果您想查询某个月份,则可以按照以下示例进行操作:

string month = dateTimePicker_month.Value.Month.ToString();
string year = dateTimePicker_month.Value.Year.ToString();

使用以下查询来选择月份:
select CAST(date AS DATE) from table where DATEPART(month, date)  = '" + month + "' and DATEPART(year,date) = '" + year + "' 

1

由于在我的情况下没有起作用,我修改了Orance的答案,并将其放入从DateTimePicker继承的类中:

    protected override void WndProc(ref Message m)
{
    if (_MonthSelectStyle)
    {
        if (m.Msg == 0X204E) // = Win32Messages.WM_REFLECT_NOTIFY
        {
            var nmhdrI = (NMHDR)(Marshal.PtrToStructure(m.LParam, typeof(NMHDR)));
            switch (nmhdrI.code)
            {
                case -754: // Win32Messages.DTN_DROPDOWN
                    var cal = SendMessage(m.HWnd, WinApi.Win32Messages.DTM_GETMONTHCAL, IntPtr.Zero, IntPtr.Zero);
                    SendMessage(cal, WinApi.Win32Messages.MCM_SETCURRENTVIEW, IntPtr.Zero, (IntPtr)1);
                    break;
                case -759: // Win32Messages.DTN_DATETIMECHANGE
                    WinApi.SendMessage(Handle, WinApi.Win32Messages.DTM_CLOSEMONTHCAL, IntPtr.Zero, IntPtr.Zero);
                    break;
            }
        }
    }
    base.WndProc(ref m);
}

而VB的等效语句:

    Protected Overrides Sub WndProc(ByRef m As Message)
    If _MonthSelectStyle Then
        If m.Msg = &H204E Then ' WM_REFLECT_NOTIFY  '&H204E   
          Dim nmhdrI = CType(Marshal.PtrToStructure(m.LParam, GetType(NMHDR)), NMHDR)
            Select Case nmhdrI.code
                Case -754 ' Win32Messages.DTN_DROPDOWN '-754  
                    Dim cal = SendMessage(m.HWnd, WinApi.Win32Messages.DTM_GETMONTHCAL, IntPtr.Zero, IntPtr.Zero)
                    SendMessage(cal, WinApi.Win32Messages.MCM_SETCURRENTVIEW, IntPtr.Zero, CType(1, IntPtr))
                Case -759 ' Win32Messages.DTN_DATETIMECHANGE '-759  
                    WinApi.SendMessage(Handle, WinApi.Win32Messages.DTM_CLOSEMONTHCAL, IntPtr.Zero, IntPtr.Zero)
            End Select
        End If
    End If
    MyBase.WndProc(m)
End Sub

0

尝试使用格式属性:

 dateTimePicker.Format = DateTimePickerFormat.Custom;
 dateTimePicker.CustomFormat = "MM";

已经尝试过了。唯一改变的是显示,但当单击下拉菜单时,它仍然显示天数。 - Bigboss
1
请查看我的更新答案。如果您想显示没有日期的日历,我认为您需要用MonthCalendar替换DatePicker。 - FrozenFire
我已经阅读了您的编辑,但是在MonthCalendar类中应该更改哪些选项呢?我相信这个类是datetimepicker在单击下拉菜单时使用的类。 - Bigboss
我找到了这个解决方案:https://netprogrammingodyssey.wordpress.com/2010/11/14/monthyear-only-datepicker/,但是你需要将Silverlight添加到你的项目中才能实现它。 - FrozenFire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接