覆盖DateTimePicker时设置日历大小以添加周数

时间:2013-01-28 09:34:17

标签: c# winforms datetimepicker

我正在尝试创建一个显示周数的DateTimePicker as shown here (Code project example)

除了一个小细节外,它的效果相当不错;尝试选择日期时弹出的日历尺寸不正确。如您所见,日历区域有点“狭窄”,尤其是沿着右边缘。

enter image description here

我可以点击这里的右下角,然后将其拖出一点 - 只需将其展开以使其看起来正确:

enter image description here

我似乎找不到任何方法强制日历从一开始就是正确/完整的大小,或者调整它的大小。任何想法都将不胜感激。

3 个答案:

答案 0 :(得分:7)

最后找到了一个似乎有效的解决方案 - 至少目前是这样。

DateTimePicker的日历部分似乎有两个窗口。显然我的代码会自动找到内部的大小(或多或少至少?),而不是外部大小。

一些研究导致了下面的代码。以下链接提供了一些有用且相关的信息:

诀窍是在(内部)窗口的高度和宽度上添加一点,然后将相同的高度和宽度应用于外部窗口(我使用GetParrent()函数访问)。我通过反复试验找到了“正确”的尺寸:当尺寸与日历内容所需的尺寸相匹配时,它无法再调整大小。

是的,这感觉有点像黑客,不,我还没有能够验证它在其他计算机上的运行情况还不如我自己。我有点担心必须给出高度和宽度的具体值,但我希望这不会受到屏幕分辨率或其他任何因素的影响。

希望处于类似情况的其他人会发现代码有用。
以下内容可直接替换常规DateTimePicker以显示日历中的周数

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

public class DatePickerWithWeekNumbers : DateTimePicker
{
    [DllImport("User32.dll")]
    private static extern int GetWindowLong(IntPtr handleToWindow, 
                                            int offsetToValueToGet);

    [DllImport("User32.dll")]
    private static extern int SetWindowLong(IntPtr h, 
                                            int index, 
                                            int value);

    private const int McmFirst = 0x1000;
    private const int McmGetminreqrect = (McmFirst + 9);
    private const int McsWeeknumbers = 0x4;
    private const int DtmFirst = 0x1000;
    private const int DtmGetmonthcal = (DtmFirst + 8);


    [DllImport("User32.dll")]
    private static extern IntPtr SendMessage(IntPtr h,
                                             int msg, 
                                             int param, 
                                             int data);

    [DllImport("User32.dll")]
    private static extern IntPtr GetParent(IntPtr h);

    [DllImport("User32.dll")]
    private static extern int SendMessage(IntPtr h, 
                                          int msg,
                                          int param, 
                                          ref Rectangle data);

    [DllImport("User32.dll")]
    private static extern int MoveWindow(IntPtr h, 
                                         int x, 
                                         int y,
                                         int width, 
                                         int height, 
                                         bool repaint);

    [Browsable(true), DesignerSerializationVisibility(
        DesignerSerializationVisibility.Visible)]
    public bool DisplayWeekNumbers { get; set; }

    protected override void OnDropDown(EventArgs e)
    {
        // Hex value to specify that we want the style-attributes
        // for the window:
        const int offsetToGetWindowsStyles = (-16);

        IntPtr pointerToCalenderWindow = SendMessage(Handle, 
                                                     DtmGetmonthcal,  
                                                     0,  
                                                     0);
        int styleForWindow = GetWindowLong(pointerToCalenderWindow, 
                                           offsetToGetWindowsStyles);

        // Check properties for the control - matches available 
        // property in the graphical properties for the DateTimePicker:
        if (DisplayWeekNumbers)
        {
            styleForWindow = styleForWindow | McsWeeknumbers;
        }
        else
        {
            styleForWindow = styleForWindow & ~McsWeeknumbers;
        }

        // Get the size needed to display the calendar (inner window)
        var rect = new Rectangle();
        SendMessage(pointerToCalenderWindow, McmGetminreqrect, 0, ref rect);

        // Add to size as needed (I don't know why 
        // this was not correct initially!)
        rect.Width = rect.Width + 28;
        rect.Height = rect.Height + 6;

        // Set window styles..
        SetWindowLong(pointerToCalenderWindow, 
                      offsetToGetWindowsStyles, 
                      styleForWindow);

        // Dont move the window - just resize it as needed:
        MoveWindow(pointerToCalenderWindow, 
                   0, 
                   0, 
                   rect.Right, 
                   rect.Bottom, 
                   true);

        // Now access the parrent window..
        var parentWindow = GetParent(pointerToCalenderWindow);
        // ...and resize that the same way:
        MoveWindow(parentWindow, 0, 0, rect.Right, rect.Bottom, true);

        base.OnDropDown(e);
    }
}

答案 1 :(得分:2)

对我来说,通过DateTimePicker的MCS_WEEKNUMBERS消息设置DTM_SETMCSTYLE会自动产生正确大小的MonthCal控件:

SendMessage(Handle, DTM_FIRST + 11, 0, SendMessage(Handle, DTM_FIRST + 12, 0, 0) | MCS_WEEKNUMBERS);

如Kjartan的解决方案中的DTM_FIRST = 0x1000MCS_WEEKNUMBERS = 0x4所在的位置。在Microsoft文档中,DTM_FIRST + 11DTM_SETMCSTYLEDTM_FIRST + 12DTM_GETMCSTYLE

与Kjartan的解决方案不同,此调用必须在第一个下拉列表之前使用,但是在某些情况下,正确的表单初始化在某些情况下对我不起作用,因此我将其延迟到已创建表单且在这些情况下可见。一个调用就足够了,DateTimePicker将保存样式以供将来使用。

答案 2 :(得分:1)

好的,尝试在Program.cs中注释一行

Application.EnableVisualStyles();

然后尝试执行。