Setting calendar size when overriding DateTimePicker to add week numbers

放肆的年华 提交于 2019-11-28 12:38:54

Finally found a solution that seems to work - at least for now.

It seems there are two windows in the calendar part of the DateTimePicker. Apparently my code would automatically find the correct size for the inner one (more or less at least?), but not the outer one.

A bit of research has led to the code below. The following links provide some useful and relevant info:

The trick was to add a little to the height and width of the (inner) window, then apply the same height and width to the outer window (which I access using the GetParrent() function). I found the "correct" size by trial and error: When the size matched what was needed for the contents of the calendar, it could not be resized any longer.

Yes, this feels a little like a hack, and no, I haven't been able to verify that it works perfectly on other computers than my own yet. I'm a little worried about having to give specific values for height and width, but I'm hoping this won't be affected by screen resolutions or whatever else.

Hope someone else in a similar situation will find the code useful.
(The following can directly replace a regular DateTimePicker to show week numbers in the calendar)

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);
    }
}

Ok, Try to comment line in Program.cs

Application.EnableVisualStyles();

and then try to execute.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!