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