Question

I have a WinForm app with a MonthCalendar control on it. The MaxSelectionCount property is set to 1 day.

There is a certain behavior that I would like to change. If the view is such that the control displays 12 months and the user clicks on a month, it will expand that month and the selected date becomes the last day of that month. I would like to change that to the first day of that month. How can I do this?

Also, what event is raised when I expand a month in this manner? Does it have a specific event?

Thanks.

Was it helpful?

Solution

This is technically possible, since Vista the native control sends a notification (MCM_VIEWCHANGE) when the view changes. You can capture this notification and turn it into an event. Add a new class to your project and paste the code shown below. I pre-cooked the code that selects the first day. Compile. Drop the new control from the top of the toolbox onto your form. Tested on Windows 8, you'll need to check if it works okay on Vista and Win7.

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

class MonthCalendarEx : MonthCalendar {
    public enum View { Month, Year, Decade, Century };
    public class ViewEventArgs : EventArgs {
        public ViewEventArgs(View newv, View oldv) { NewView = newv; OldView = oldv; }
        public View NewView { get; private set; }
        public View OldView { get; private set; }
    }
    public event EventHandler<ViewEventArgs> ViewChange;

    protected virtual void OnViewChange(ViewEventArgs e) {
        if (ViewChange == null) return;
        // NOTE: I saw painting problems if this is done when MCM_VIEWCHANGE fires, delay it
        this.BeginInvoke(new Action(() => {
            // Select first day when switching to Month view:
            if (e.NewView == View.Month) {
                this.SetDate(this.GetDisplayRange(true).Start);
            } 
            ViewChange(this, e);
        }));
    }

    protected override void WndProc(ref Message m) {
        if (m.Msg == 0x204e) {         // Trap WMREFLECT + WM_NOTIFY
            var hdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
            if (hdr.code == -750) {    // Trap MCM_VIEWCHANGE
                var vc = (NMVIEWCHANGE)Marshal.PtrToStructure(m.LParam, typeof(NMVIEWCHANGE));
                OnViewChange(new ViewEventArgs(vc.dwNewView, vc.dwOldView));
            }
        }
        base.WndProc(ref m);
    }
    private struct NMHDR {
        public IntPtr hwndFrom;
        public IntPtr idFrom;
        public int code;
    }
    private struct NMVIEWCHANGE {
        public NMHDR hdr;
        public View dwOldView;
        public View dwNewView;
    } 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top