Question

On my form, I have a System.Windows.Forms.TableLayoutPanel in which I have a System.Windows.Forms.Panel set to DockStyle.Fill, and in the panel, a System.Windows.Forms.MonthCalendar control (also set to fill).

The calendar is an inherited control, simply to allow overriding the theme so I can set colors, etc. The inherited part came from another SO question that I've since lost track of.

The code:

public class NativeMethods
{
    [DllImport("uxtheme.dll")]
    public static extern Int32 SetWindowTheme(IntPtr hWnd, String appname, String idlist);
}

public class CustomMonthCalendar : MonthCalendar
{
    public CustomMonthCalendar()
    {
        InitializeComponent();
        Margin = new Padding(0);
    }

    private void InitializeComponent()
    {
        SuspendLayout();
        ResumeLayout(false);
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        NativeMethods.SetWindowTheme(Handle, String.Empty, String.Empty);
        base.OnHandleCreated(e);
    }
}

public class CustomCalendar : UserControl
{
    DayPickerControl = new CustomMonthCalendar();
    DayPickerControl.ForeColor = SystemColors.ControlText;
    DayPickerControl.BackColor = BackgroundColor;
    DayPickerControl.TitleForeColor = TextColor.Lighten(1.2f);
    DayPickerControl.TitleBackColor = BackgroundColor;
    DayPickerControl.TrailingForeColor = Color.Gray;
    DayPickerControl.Dock = DockStyle.Fill;
    DayPickerControl.FirstDayOfWeek = Day.Sunday;
    DayPickerControl.Font = new Font("Microsoft Sans Serif", 9f, FontStyle.Regular);
    DayPickerControl.MaxDate = new DateTime(2999, 12, 31, 0, 0, 0, 0);
    DayPickerControl.MaxSelectionCount = 7;
    DayPickerControl.MinDate = new DateTime(1900, 1, 1, 0, 0, 0, 0);
    DayPickerControl.ScrollChange = 1;
    DayPickerControl.SetCalendarDimensions(1, 1);
    DayPickerControl.ShowToday = true;
    DayPickerControl.ShowTodayCircle = true;
    DayPickerControl.Text = null;

    DayPickerPanel = new Panel();
    DayPickerPanel.BorderStyle = BorderStyle.None;
    DayPickerPanel.Dock = DockStyle.Fill;
    DayPickerPanel.Location = new Point(0, 0);
    DayPickerPanel.Padding = new Padding(2);
    Size prefSize = DayPickerControl.GetPreferredSize(new Size(200, 200));
    DayPickerPanel.Size = new Size(prefSize.Width + 5, prefSize.Height + 5);
    DayPickerPanel.Paint += panelBorder_Paint;
    DayPickerPanel.Controls.Add(DayPickerControl);
}

So the question is about determining what size the calendar control wants to be. No matter what I do, the size I get back from GetPreferredSize() is always 178, 155. On-screen, however, the right-hand edge of the calendar is cut-off, meaning it's larger than the panel in which it's docked. That suggests to me that it might also not care much about being docked. Not sure.

I have fiddled around with various docked/undocked vs anchored settings, but always the same results.

Why am I getting a seemingly incorrect size? And how do I determine the correct size to fit it to?

EDIT: Using Hans Passant's suggestion, I arrived at this solution:

//A new event in the CustomMonthCalendar class
public event EventHandler<AfterHandleCreatedArgs> AfterHandleCreated;

//A modified version of the OnHandleCreated() method shown above (add event call)
protected override void OnHandleCreated(EventArgs e)
{
    NativeMethods.SetWindowTheme(Handle, String.Empty, String.Empty);
    base.OnHandleCreated(e);
    if (this.IsHandleCreated && AfterHandleCreated != null)
        AfterHandleCreated(this, new AfterHandleCreatedArgs(new Size(this.Size.Width + 5, this.Size.Height + 5)));  //cosmetic padding
}

//A new class for the event args it passes...
public class AfterHandleCreatedArgs : EventArgs
{
    private Size _newSize = Size.Empty;
    public Size NewSize { get { return _newSize; } set { _newSize = value; } }

    public AfterHandleCreatedArgs(Size newSize)
    {
        _newSize = newSize;
    }
}

//And a handler to attach to that new event (in CustomCalendar class)...
private void DayPickerControl_AfterHandleCreated(Object sender, AfterHandleCreatedArgs e)
{
    if (!e.NewSize.Equals(Size.Empty))
        DayPickerPanel.Size = e.NewSize;
}
Was it helpful?

Solution

MonthCalendar is a "difficult" control. It is used in a highly visible area in Windows (the clock) and has therefore been tinkered with quite a bit between Windows version. It is not resizable and its size varies between versions.

So, no, using Dock = Fill certainly can't work, that requires a control to be resizable. The GetPreferredSize() can only return a guestimate, one that might have once been correct on an old Windows version but is not today.

You will need to deal with its behavior. You can't know its true size until after the native window for the control was created. Typically in the form's Load event or the control's OnCreateHandle() method (in case you derived from it). At which point its Size property will be reliable. You'll need to adjust its parent control accordingly.

OTHER TIPS

You can force the calendar to size itself by asking for it's handle:

IntPtr h = calendar.Handle;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top