Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9707

サイズ変更可能な簡易MonthCalendarを作ってみた

$
0
0

.NETのMonthCalendarコンポーネントがパワフルで使いやすいですが、サイズがなかなか調整できないのが欠点です。特にタッチパネル上で操作する時、サイズがやや小さい気がします。
機能限定ですが、サイズ変更可能なMonthCalendarを自作してみた。

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace utils.calendar
{
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    [ComVisible(true)]
    [DefaultEvent("Load")]
    [DesignerCategory("UserControl")]
    public class MonthCalendar : UserControl
    {
        private DateTime currentDate;

    [Browsable(true)]
    [DefaultValue(12)]
    public int FontSize {
        get
        {
            return (int)this.Font.Size;
        }
        set
        {
            this.Font = new Font("Meiryo UI", value, FontStyle.Regular, GraphicsUnit.Point, ((byte)(128)));
        }
    }
    private DayLabel[,,] days = new DayLabel[2,7,7];
    private DayLabel rightButton;
    private DayLabel leftButton;
    private Label title;
    private DayLabel todayLabel;
    private Panel[] bodyPanel = new Panel[2];
    public const string MONTH_FORMAT  = "yyyy年M月";
    public const string DATE_FORMAT  = "yyyy年M月d日";

    private DayLabel selectDayLabel;
    [Browsable(false)]
    [DefaultValue(typeof(DateTime))]
    public DateTime SelectDate
    {
        get
        {
            return selectDayLabel == null ? new DateTime() : selectDayLabel.Date;
        }
        set
        {
            if (value.Ticks > 0L)
            {
                currentDate = value.Date;
                setDayLabelPreferredDate(curIndex, currentDate, false);
                if (selectDayLabel != null && selectDayLabel.Selected)
                {
                    selectDayLabel.Selected = false;
                }
                selectDayLabel = getCurrentDayLabel(value.Day);
                if (selectDayLabel != null)
                {
                    selectDayLabel.Selected = true;
                }
                title.Text = currentDate.Date.ToString(MONTH_FORMAT);
            }
        }
    }
    [Browsable(true)]
    [DefaultValue(26)]
    public int TilteHeight
    {
        get { return title.Height; }
        set {
            title.Height = value;
            Invalidate();
        }
    }
    private int curIndex = 0;
    private volatile bool moving = false;
    public MonthCalendar() : base()
    {
        this.MinimumSize = new Size(240,200);
        currentDate = DateTime.Now.Date;
        title = new Label();
        todayLabel = new DayLabel();
        rightButton = new DayLabel();
        leftButton = new DayLabel();
        bodyPanel[0] = new Panel();
        bodyPanel[1] = new Panel();
        string[] WEEKS = { "日", "月", "火", "水", "木", "金", "土", };
        this.SuspendLayout();
        for (int n = 0; n < 2; n++)
        {
            Panel p = new Panel();//曜日、各日の分割ライン
            p.Location = new Point(0,0);
            p.Size = new Size(1, 1);
            p.BackColor = Color.DeepSkyBlue;
            p.Anchor = AnchorStyles.Left | AnchorStyles.Right;
            bodyPanel[n].Controls.Add(p);
            for (int i = 0; i < 7; i++)
            {
                for (int ii = 0; ii < 7; ii++)
                {
                    DayLabel d = new DayLabel();
                    d.Size = new Size(40, 30);
                    if (i == 0)
                    {
                        d.Text = WEEKS[ii];
                        d.staticLabel = true;
                    }
                    else
                    {
                        d.Date = currentDate;
                        d.Click += DayLabel_Click;
                    }
                    d.Location = new Point(ii*40+5, 30*i+5);
                    days[n, i, ii] = d;
                    bodyPanel[n].Controls.Add(d);
                }
            }
        }
        this.Font = new Font("Meiryo UI", 12, FontStyle.Regular, GraphicsUnit.Point, ((byte)(128)));
        this.AutoScaleMode = AutoScaleMode.Dpi;
        int w = 320;
        int h = 240;
        int th = 26;
        int tw = 240;
        this.Size = new Size(w, h);
        this.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;

        title.AutoSize = false;
        title.Size = new Size(tw, th);
        title.Location = new Point((w-tw)>>1, 0);
        title.TextAlign = ContentAlignment.MiddleCenter;
        title.Text = currentDate.ToString(MONTH_FORMAT);
        title.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;

        todayLabel.AutoSize = true;
        todayLabel.Date = currentDate;
        todayLabel.Text = "□ 今日:" + currentDate.ToString(DATE_FORMAT);
        int todaywidth = TextRenderer.MeasureText(todayLabel.Text, this.Font).Width;
        todayLabel.Location = new Point((w - Padding.Left - Padding.Right - todaywidth) / 2, h - th - Padding.Bottom);
        todayLabel.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
        todayLabel.CurrentMonth = true;
        todayLabel.Click += Today_Click;
        rightButton.Font = new Font("Meiryo UI", FontSize + 2, FontStyle.Regular, GraphicsUnit.Point, ((byte)(128)));
        rightButton.Text = "▶";
        rightButton.ForeColor = Color.Black;
        rightButton.Size = new Size(th, th);
        rightButton.Location = new Point(w - th - 5, 0);
        rightButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
        rightButton.CurrentMonth = true;
        rightButton.Click += RightButton_Click;

        leftButton.Font = new Font("Meiryo UI", FontSize + 2, FontStyle.Regular, GraphicsUnit.Point, ((byte)(128)));
        leftButton.Text = "◀";
        leftButton.ForeColor = Color.Black;
        leftButton.Size = new Size(th, th);
        leftButton.Location = new Point(5, 0);
        leftButton.Anchor = AnchorStyles.Top | AnchorStyles.Left;
        leftButton.CurrentMonth = true;
        leftButton.Click += LeftButton_Click;

        bodyPanel[0].Size = new Size(w, h - th - th - 1);
        bodyPanel[0].Location = new Point(0, th);
        bodyPanel[0].Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
        bodyPanel[0].SizeChanged += (s, e) => {
            setDayLabelPreferredSize((Control)s);
        };
        bodyPanel[1].Size = new Size(w - tw, h - th - 1);
        bodyPanel[1].Location = new Point(0, th);
        bodyPanel[1].Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
        bodyPanel[1].Visible = false;
        bodyPanel[1].SizeChanged += (s, e) => {
            setDayLabelPreferredSize((Control)s);
        };

        this.Controls.Add(title);
        this.Controls.Add(todayLabel);
        this.Controls.Add(rightButton);
        this.Controls.Add(leftButton);
        this.Controls.Add(bodyPanel[0]);
        this.Controls.Add(bodyPanel[1]);
        setDayLabelPreferredSize(bodyPanel[0]);
        this.ResumeLayout(false);
        this.PerformLayout();
    }
    private void changeToNextMonth(DateTime nextDate, Action callback=null)
    {
        if(currentDate.Year==nextDate.Year && currentDate.Month == nextDate.Month)
        {
            moving = false;
            return;
        }
        int nextIndex = (curIndex + 1) & 0x00000001;
        Panel np = bodyPanel[nextIndex];
        Panel cp = bodyPanel[curIndex];
        DayLabel firstday = setDayLabelPreferredDate(nextIndex, nextDate, (selectDayLabel!=null));
        Point pt = np.Location;

        int dx = cp.Width + 2;
        int n = 5;
        int xStep = (dx + n - 1) / n;

        if (nextDate>currentDate)    //[cur][next]<=左に移動
        {
            dx = -dx;
            xStep = -xStep;
        }
        pt.X = cp.Location.X - dx;
        pt.Y = cp.Location.Y;
        np.Location = pt;
        np.Visible = true;

        int orgCX = cp.Location.X;
        int orgNX = np.Location.X;

        Timer t = new Timer();
        t.Interval = (200 + n - 1) / n;
        int cnt = 0;
        t.Tick += (s, e)=> {
            Point npt = np.Location;
            Point cpt = cp.Location;
            cnt++;
            if(cnt>=n)
            {
                npt.X = orgCX;
                cpt.X = orgNX;
                t.Stop();
                moving = false;
                cp.Visible = false;
                curIndex = nextIndex;
                currentDate = nextDate;
                if (selectDayLabel != null)
                {
                    selectDayLabel = firstday;
                }
                title.Text = currentDate.ToString(MONTH_FORMAT);
                if (callback != null)
                {
                    try { callback(); } catch (Exception) { }
                }
            }
            else
            {
                npt.Offset(xStep, 0);
                cpt.Offset(xStep, 0);
            }
            cp.Location = cpt;
            np.Location = npt;
        };
        t.Start();
    }

    private void LeftButton_Click(object sender, EventArgs e)
    {
        if (!moving)
        {
            moving = true;
            DateTime t = currentDate.AddMonths(-1);
            changeToNextMonth(t);
            title.Text = t.ToString(MONTH_FORMAT);
        }
    }

    private void RightButton_Click(object sender, EventArgs e)
    {
        if (!moving)
        {
            moving = true;
            DateTime t = currentDate.AddMonths(1);
            changeToNextMonth(t);
            title.Text = t.ToString(MONTH_FORMAT);
        }
    }

    private void Today_Click(object sender, EventArgs e)
    {
        if (!moving)
        {
            moving = true;
            changeToNextMonth(todayLabel.Date, () =>
            {
                DayLabel next = getCurrentDayLabel(currentDate.Day);
                if (next != null)
                {
                    if (selectDayLabel != null)
                    {
                        selectDayLabel.Selected = false;
                    }
                    selectDayLabel = next;
                    selectDayLabel.Selected = true;
                }
            });
            title.Text = todayLabel.Date.ToString(MONTH_FORMAT);
        }
    }

    private void DayLabel_Click(object sender, EventArgs e)
    {
        DayLabel d = (DayLabel)sender;
        if (d.CurrentMonth)
        {
            if (selectDayLabel == null)
            {
                selectDayLabel = d;
                selectDayLabel.Selected = true;
            }
            else if (selectDayLabel != d)
            {
                selectDayLabel.Selected = false;
                selectDayLabel = d;
                selectDayLabel.Selected = true;
            }
        }
        else
        {
            if (!moving)
            {
                moving = true;
                changeToNextMonth(d.Date, () =>
                {
                    DayLabel next = getCurrentDayLabel(d.Date.Day);
                    if (next != null)
                    {
                        if (selectDayLabel != null)
                        {
                            selectDayLabel.Selected = false;
                        }
                        selectDayLabel = next;
                        selectDayLabel.Selected = true;
                    }
                });
                title.Text = d.Date.ToString(MONTH_FORMAT);
            }
        }
    }
    private DayLabel getCurrentDayLabel(int day)
    {
        int m = currentDate.Month;
        for(int i=1;i<7;i++)
        {
            for(int ii=0;ii<7;ii++)
            {
                DayLabel d =  days[curIndex,i,ii];
                if (d.Date.Month == m && d.Date.Day == day)
                {
                    return d;
                }
            }
        }
        return null;
    }
    private void setDayLabelPreferredSize(Control parent)
    {
        int pw = parent.Width;
        int ph = parent.Height;
        int left = parent.Padding.Left;
        int right = parent.Padding.Right;
        int top = parent.Padding.Top;
        int bottom = parent.Padding.Bottom;
        int dy = 3;             //間隔
        int dx = 3;             //間隔
        int n = 7;              //行数
        int separater = 10;     //分割ライン高さ
        int w = (pw - left - right - dx * (n - 1)) / n;
        int h = (ph - top - bottom - dy * (n - 1) - separater) / n;
        parent.SuspendLayout();
        for(int i=0; i<2;i++)
        {
            int y = top;
            for(int row=0;row<n;row++)
            {
                int x = left;
                for (int col = 0; col < n; col++)
                {
                    days[i, row, col].Size = new Size(w, h);
                    days[i, row, col].Location = new Point(x, y);
                    x += dx;
                    x += w;
                }
                y += dy;
                y += h;
                if (row==0)
                {
                    bodyPanel[i].Controls[0].Size = new Size(pw-dx-dx, 2);
                    bodyPanel[i].Controls[0].Location = new Point(dx, y + 3);
                    y += separater;
                }
            }
        }
        setDayLabelPreferredDate(curIndex, currentDate, false);
        setDayLabelPreferredDate(0x00000001&(curIndex+1), currentDate.AddMonths(1), false);
        parent.ResumeLayout(true);
    }
    private DayLabel setDayLabelPreferredDate(int curIndex, DateTime date, bool selectFirstday)
    {
        DateTime d = new DateTime(date.Year, date.Month, 1);
        int m = d.Month;
        int w = (int)d.DayOfWeek;
        if(w==0)
        {
            w = 7;
        }
        d = d.AddDays(-w);
        DayLabel firstday = null;
        int curMonth = todayLabel.Date.Month;
        int curDay = todayLabel.Date.Day;
        for (int i = 1; i < 7; i++)
        {
            for(int ii = 0; ii < 7; ii++)
            {
                DayLabel dd = days[curIndex, i, ii];
                dd.Date = d;
                dd.CurrentMonth = (d.Month == m);
                dd.Today = (d.Month == curMonth && d.Day == curDay);
                bool selected = false;
                if (firstday==null && d.Day==1)
                {
                    firstday = dd;
                    if (selectFirstday)
                    {
                        selected = true;
                    }
                }
                dd.Selected = selected;
                FontStyle s;
                if (d.DayOfWeek==DayOfWeek.Sunday|| d.DayOfWeek == DayOfWeek.Saturday)
                {
                    s = FontStyle.Bold;
                }
                else
                {
                    s = FontStyle.Regular;
                }
                if (dd.Font.Style != s)
                {
                    dd.Font = new Font(dd.Font, s);
                }
                d = d.AddDays(1);
            }
        }
        return firstday;
    }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        int w = Width;
        int h = Height;
        int th = title.Height;
        bodyPanel[0].Size = new Size(w, h - th - th - 1);
        bodyPanel[1].Size = new Size(w, h - th - th - 1);
        setDayLabelPreferredSize(bodyPanel[0]);
        if (selectDayLabel != null)
        {
            selectDayLabel.Selected = true;
        }
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        base.OnSizeChanged(e);
        if(selectDayLabel!=null)
        {
            selectDayLabel.Selected = true;
        }
    }
    class DayLabel : Label
    {
        private Color[] foreColeos = { Color.Black,       Color.Orange,    Color.Black,       Color.Black };
        private Color[] backColeos = { Color.Transparent, Color.LightCyan, Color.Transparent, Color.Orange };
        private Color BODY_LINE_COLOR  = Color.DeepSkyBlue;
        private Color SELECTED_COLOR   = Color.Red;
        private const int MOUSE_NONE   = 0;
        private const int MOUSE_ENTER  = 1;
        private const int MOUSE_LEAVE  = 2;
        private const int MOUSE_DOWN   = 3;
        private const int MOUSE_UP     = MOUSE_NONE;
        private const int MOUSE_SELECT = 4;
        public bool CurrentMonth {
            get { return curMonth; }
            set {
                curMonth = value;
                ForeColor = value ? Color.Black : Color.LightGray;
            }
        }
        private bool curMonth = true;
        public bool Today
        {
            get { return toDay; }
            set { toDay = value; }
        }
        private bool toDay = false;
        private DateTime date;
        public bool Selected {
            get { return selectd; }
            set {
                selectd = value;
                BackColor = value ? SELECTED_COLOR : BackColor = backColeos[state];
            }
        }
        private bool selectd;
        private int radius = 3;
        private int state = MOUSE_NONE;
        public bool staticLabel { get; set; }
        public DateTime Date
        {
            get { return date; }
            set
            {
                date = value;
                int day = date.Day;
                if(day==0)
                {
                    Text = "";
                }
                else if(day<10)
                {
                    Text = " " + day;
                }
                else
                {
                    Text = day.ToString();
                }
            }
        }
        public DayLabel() : base()
        {
            this.AutoSize = false;
            this.BackColor = Color.Transparent;
            this.TabIndex = 0;
            this.TabStop = false;
            this.TextAlign = ContentAlignment.MiddleCenter;
        }
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_LEAVE;
            setCurrentColor();
        }
        private void setCurrentColor()
        {
            if (!curMonth &&( state == MOUSE_NONE||state==MOUSE_LEAVE))
            {
                ForeColor = Color.LightGray;
            }
            else
            {
                ForeColor = foreColeos[state];
            }
            if(state!=MOUSE_DOWN && selectd)
            {
                BackColor = SELECTED_COLOR;
            }
            else
            {
                BackColor = backColeos[state];
            }
        }
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_ENTER;
            setCurrentColor();
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_DOWN;
            setCurrentColor();
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_DOWN;
            setCurrentColor();
        }
        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_UP;
            setCurrentColor();
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (staticLabel)
            {
                return;
            }
            state = MOUSE_UP;
            setCurrentColor();
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (!toDay)
            {
                return;
            }
            int x = 0;
            int y = 0;
            int w = Width - 1;
            int h = Height - 1;
            using (Pen pen = new Pen(BODY_LINE_COLOR))
            {
                if (selectd)
                {
                    pen.DashStyle = DashStyle.Dash;
                }
                using (GraphicsPath Path = generateGraphicsPath(x, y, w, h, radius))
                {
                    e.Graphics.DrawPath(pen, Path);
                }
            }
        }
        private GraphicsPath generateGraphicsPath(int x, int y, int w, int h, int radius)
        {
            radius <<= 1;
            GraphicsPath path = new GraphicsPath();
            path.AddArc(x + w - radius, y, radius, radius, 270, 90);
            path.AddArc(x + w - radius, y + h - radius, radius, radius, 0, 90);
            path.AddArc(x, y + h - radius, radius, radius, 90, 90);
            path.AddArc(x, y, radius, radius, 180, 90);
            path.CloseFigure();
            return path;
        }
    }
}


}

Viewing all articles
Browse latest Browse all 9707

Trending Articles