Setting calendar size when overriding DateTimePicker to add week numbers

前端 未结 3 1272
花落未央
花落未央 2020-12-11 06:29

I\'m trying to create a DateTimePicker with week numbers displayed, as shown here (Code project example).

It works fairly well, except for one minor de

相关标签:
3条回答
  • 2020-12-11 06:59

    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:

    • GetWindowLong function (Used for getting info about the window to edit)
    • GetParent function (Finding the outer window, so we could apply settings to that too)

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-11 07:02

    For me, setting MCS_WEEKNUMBERS via the DateTimePicker's DTM_SETMCSTYLE message automatically resulted in the correct size of the MonthCal control:

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

    Where DTM_FIRST = 0x1000 and MCS_WEEKNUMBERS = 0x4 as in Kjartan's solution. DTM_FIRST + 11 is DTM_SETMCSTYLE and DTM_FIRST + 12 is DTM_GETMCSTYLE in Microsoft's documentation.

    Unlike Kjartan's solution, this call must be used before the first dropdown, but right at form initialization didn't work for me in some cases, so I delayed it to when the form was already created and visible in these cases. One call is enough, the DateTimePicker will save the style for future dropdowns.

    0 讨论(0)
  • 2020-12-11 07:23

    Ok, Try to comment line in Program.cs

    Application.EnableVisualStyles();
    

    and then try to execute.

    0 讨论(0)
提交回复
热议问题