유용한 정보

[C#] 사용자 정의 달력

DevReff 2025. 5. 14. 00:06
728x90
728x90
SMALL

1.  달력의 비어있는 날짜 컨트롤

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using Systehttp://m.Threading.Tasks;
using Systehttp://m.Windows.Forms;

namespace CjControls
{
    /// <summary>
    /// 빈 날짜를 표시하는 컨트롤
    /// </summary>
    public partial class CjBlank : UserControl
    {
        /// <summary>
        /// 생성자
        /// </summary>
        public CjBlank()
        {
            InitializeComponent();
        }
    }
}
 

2. 날짜 컨트롤

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Systehttp://m.ComponentModel.Design;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using Systehttp://m.Reflection.Emit;
using System.Text;
using Systehttp://m.Threading.Tasks;
using Systehttp://m.Windows.Forms;

namespace CjControls
{
     public partial class CjDay : UserControl
    {
        #region attributes
        /// <summary>
        /// 선택되었는지 여부
        /// </summary>
        private bool m_bSelected = false;
        [Browsable(true)]
        [Description("선택되었는지 여부")]
        [DefaultValue(false)]
        public bool Selected
        {
            get { return m_bSelected; }
            set 
            {
                if (value)
                {
                    m_bSelected = true;
                    this.BackColor = this.m_selectedColor;
                    this.ForeColor = this.m_selectedTxtColor;
                }
                else
                {
                    m_bSelected = false;
                    m_nIndex = -1;
                    m_nMethod = -1;
                    this.BackColor = this.m_bkColor;
                    this.ForeColor = this.m_txtColor;
                }

                this.Invalidate();
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 존재여부
        /// </summary>
        private bool m_bOutline = false;
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 존재여부")]
        public bool Outline
        {
            get { return m_bOutline; }
            set
            {
                if (value != m_bOutline)
                {
                    m_bOutline = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 선택되지 않은 상태의 배경색
        /// </summary>
        private Color m_bkColor = SystemColors.Control;
        [Browsable(true)]
        [Description("선택되지 않은 상태의 배경색")]
        public Color BkColor
        {
            get { return m_bkColor; }
            set
            {
                if (value != m_bkColor)
                {
                    m_bkColor = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 선택된 상태의 배경색
        /// </summary>
        private Color m_selectedColor = SystemColors.ActiveCaption;
        [Browsable(true)]
        [Description("선택된 상태의 배경색")]
        public Color SelectedColor
        {
            get { return m_selectedColor; }
            set
            {
                if (value != m_selectedColor)
                {
                    m_selectedColor = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 선택되지 않은 상태의 글자색
        /// </summary>
        private Color m_txtColor = SystemColors.ControlText;
        [Browsable(true)]
        [Description("선택되지 않은 상태의 글자색")]
        public Color TxtColor
        {
            get { return m_txtColor; }
            set
            {
                if (value != m_txtColor)
                {
                    m_txtColor = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 선택된 상태의 글자색
        /// </summary>
        private Color m_selectedTxtColor = SystemColors.ControlText;
        [Browsable(true)]
        [Description("선택된 상태의 글자색")]
        public Color SelectedTxtColor
        {
            get { return m_selectedTxtColor; }
            set
            {
                if (value != m_selectedTxtColor)
                {
                    m_selectedTxtColor = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 색상
        /// </summary>
        private Color m_outlineColor = SystemColors.ButtonShadow;
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 색상")]
        public Color OutlineColor
        {
            get { return m_outlineColor; }
            set
            {
                if (value != m_outlineColor)
                {
                    m_outlineColor = value;
                    this.Invalidate();
                }
            }
        }


        /// <summary>
        /// 선택된 날짜 그룹의 순번 (zero-based)
        /// - -1이면 선택되지 않음.
        /// - -1이 아니면 선택됨
        /// </summary>
        private int m_nIndex = -1;
        [Browsable(true)]
        [Description("선택된 날짜 그룹의 순번 (zero-based)")]
        [DefaultValue(-1)]
        public int Index 
        {
            get { return m_nIndex; }
            set {  m_nIndex = value; } 
        }

        /// <summary>
        /// 선택 유형 (default=0, 0=연속형, 1=단일형)
        /// </summary>
        /// <remarks>
        /// - 0 = 연속된 날짜로 구성된 그룹에 속함
        /// - 1 = 낱개로 구성된 날짜의 그룹에 속함
        /// </remarks>
        private int m_nMethod = -1;
        [Browsable(true)]
        [Description("선택 유형 (-1=선택안됨, 0=연속형, 1=단일형)")]
        [DefaultValue(-1)]
        public int Method
        {
            get { return m_nMethod; }
            set { m_nMethod = value; }
        }

        /// <summary>
        /// 년도 (1~9999)
        /// </summary>
        private int m_nYear = 0;
        [Browsable(true)]
        [Description("년도 (1~9999)")]
        [DefaultValue(0)]
        public int Year
        {
            get { return m_nYear; }
            set
            {
                m_nYear = value;

                if (m_nYear > 0 && m_nMonth > 0 && m_nDay > 0)
                {
                    m_Date = new DateTime(m_nYear, m_nMonth, m_nDay);
                    m_dateStr = $"{m_nYear.ToString("D4")}{m_nMonth.ToString("D2")}{m_nDay.ToString("D2")}";
                    WriteDay();
                }
            }
        }

        /// <summary>
        /// 월 (1~12)
        /// </summary>
        private int m_nMonth = 0;
        [Browsable(true)]
        [Description("월 (1~12)")]
        [DefaultValue(0)]
        public int Month
        {
            get { return m_nMonth; }
            set
            {
                m_nMonth = value;

                if (m_nYear > 0 && m_nMonth > 0 && m_nDay > 0)
                {
                    m_Date = new DateTime(m_nYear, m_nMonth, m_nDay);
                    m_dateStr = $"{m_nYear.ToString("D4")}{m_nMonth.ToString("D2")}{m_nDay.ToString("D2")}";
                    WriteDay();
                }
            }
        }

        /// <summary>
        /// 일 (1~31)
        /// </summary>
        private int m_nDay = 0;
        [Browsable(true)]
        [Description("일 (1~31)")]
        [DefaultValue(0)]
        public int Day
        {
            get { return m_nDay; }
            set
            {
                m_nDay = value;

                if (m_nYear > 0 && m_nMonth > 0 && m_nDay > 0)
                {
                    m_Date = new DateTime(m_nYear, m_nMonth, m_nDay);
                    m_dateStr = $"{m_nYear.ToString("D4")}{m_nMonth.ToString("D2")}{m_nDay.ToString("D2")}";

                    WriteDay();
                }
            }
        }

        /// <summary>
        /// 날짜
        /// </summary>
        private DateTime m_Date = DateTime.MinValue;
        [Browsable(true)]
        [Description("날짜")]
        [DefaultValue(0)]
        public DateTime Date
        {
            get { return m_Date; }
            set
            {
                try
                {
                    if (value != DateTime.MinValue && value != DateTime.MaxValue)
                    {
                        Year = value.Year;
                        Month = value.Month;
                        Day = value.Day;
                        m_Date = value;
                        m_dateStr = $"{m_nYear.ToString("D4")}{m_nMonth.ToString("D2")}{m_nDay.ToString("D2")}";
                        WriteDay();
                    }
                    else
                        m_Date = DateTime.MinValue;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }

        /// <summary>
        /// 날짜 문자열 (yyyyMMdd)
        /// </summary>
        private string m_dateStr = "";
        [Browsable(true)]
        [Description("날짜 문자열 (yyyyMMdd)")]
        [DefaultValue(0)]
        public string DateStr
        {
            get { return m_dateStr; }
            set
            {
                if (!string.IsNullOrWhiteSpace(value) && value.Length >= 8
                    && DateTime.TryParseExact(value, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime dt))
                {
                    m_dateStr = value;
                    m_Date = dt;
                    m_nYear = dt.Year;
                    m_nMonth = dt.Month;
                    m_nDay = dt.Day;
                    WriteDay();
                }
                else
                {
                    Debug.WriteLine($"잘못된 날짜({value})를 DateStr에 대입했습니다.");
                }
            }
        }
        #endregion

        #region 생성자
        /// <summary>
        /// 생성자
        /// </summary>
        public CjDay()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="day">일 (1~31)</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽선의 존재여부</param>
        public CjDay(int year, int month, int day, bool selected = false, bool bOutline = false)
        {
            InitializeComponent();

            Initialize(year, month, day, selected, bOutline);
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="dt">날짜</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽선의 존재여부</param>
        public CjDay(DateTime dt, bool selected = false, bool bOutline = false)
        {
            InitializeComponent();

            Initialize(dt, selected, bOutline);
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="dtStr">날짜</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽선의 존재여부</param>
        public CjDay(string dtStr, bool selected = false, bool bOutline = false)
        {
            InitializeComponent();

            Initialize(dtStr, selected, bOutline);
        }
        #endregion

        #region 컨트롤 이벤트 
        /// <summary>
        /// WM_PAINT 메시지 이벤트
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_bOutline)
                ControlPaint.DrawBorder(e.Graphics, ClientRectangle, m_outlineColor, ButtonBorderStyle.Solid);

            if (m_bSelected)
            {
                LblDay.BackColor = m_selectedColor;
                LblDay.ForeColor = m_selectedTxtColor;
            }
            else
            {
                LblDay.BackColor = m_bkColor;
                LblDay.ForeColor = m_txtColor;
            }
            //using (Graphics g = e.Graphics)
            //{
            //    Brush bs = new SolidBrush(m_bSelected ? m_selectedColor : m_bkColor);
            //    Rectangle rc = ClientRectangle;
            //    g.FillRectangle(bs, rc);

            //    g.DrawString(DateStr, this.Font, bs, rc);
            //}
        }
        #endregion

        #region 외부 함수
        /// <summary>
        /// 날짜 초기화하기
        /// </summary>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="day">일 (1~31)</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽선의 존재여부</param>
        /// <returns>성공여부</returns>
        public bool Initialize(int year, int month, int day, bool selected = false, bool bOutline = false)
        {
            m_bSelected = selected;
            m_bOutline = bOutline;

            if (0 < year && year < DateTime.MaxValue.Year 
                && 0 < month && month < 12
                && 0 < day && day < DateTime.DaysInMonth(year, month))
            {
                m_Date = new DateTime(year, month, day);
                return true;
            }

            Debug.WriteLine($"잘못된 날짜 매개변수({year}년, {month}월 {day}일) 입니다.");
            return false;
        }

        /// <summary>
        /// 날짜 초기화하기
        /// </summary>
        /// <param name="dt">날짜</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public bool Initialize(DateTime dt, bool selected = false, bool bOutline = false)
        {
            m_bSelected = selected;
            m_bOutline = bOutline;

            if (dt != DateTime.MaxValue && dt != DateTime.MinValue)
            {
                m_Date = dt;
                return true;
            }

            Debug.WriteLine($"잘못된 날짜 매개변수({dt}) 입니다.");
            return false;
        }

        /// <summary>
        /// 날짜 초기화하기
        /// </summary>
        /// <param name="dtStr">날짜 (yyyyMMdd)</param>
        /// <param name="selected">선택되었는지 여부 (default=false)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public bool Initialize(string dtStr, bool selected = false, bool bOutline = false)
        {
            m_bSelected = selected;
            m_bOutline = bOutline;

            if (!string.IsNullOrWhiteSpace(dtStr) && dtStr.Length >= 8
                && DateTime.TryParseExact(dtStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime dt))
            {
                m_Date = dt;
                return true;
            }

            Debug.WriteLine($"잘못된 날짜 매개변수({dtStr}) 입니다.");
            return false;
        }

        /// <summary>
        /// 날짜에 일수 더하기
        /// </summary>
        /// <param name="days">일수</param>
        public CjDay AddDays(int days)
        {
            if (this.Date != DateTime.MinValue && this.Date != DateTime.MaxValue)
                return new CjDay(this.Date.AddDays(days));
            return this;
        }

        /// <summary>
        /// 날짜에 월수 더하기
        /// </summary>
        /// <param name="months">월수</param>
        public CjDay AddMonths(int months)
        {
            if (this.Date != DateTime.MinValue && this.Date != DateTime.MaxValue)
                return new CjDay(this.Date.AddMonths(months));
            return this;
        }

        /// <summary>
        /// 날짜에 년수 더하기
        /// </summary>
        /// <param name="years">년수</param>
        public CjDay AddYears(int years)
        {
            if (this.Date != DateTime.MinValue && this.Date != DateTime.MaxValue)
                return new CjDay(this.Date.AddYears(years));
            return this;
        }

        /// <summary>
        /// 날짜를 컨트롤에 출력한다.
        /// </summary>
        public void WriteDay()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new EventHandler(delegate
                {
                    LblDay.Text = m_nDay.ToString("D2");
                }));
            }
            else
                LblDay.Text = m_nDay.ToString("D2");
        }
        #endregion
    }
}
 

3. 요일 표시 컨트롤

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using Systehttp://m.Threading.Tasks;
using Systehttp://m.Windows.Forms;

namespace CjControls
{
    /// <summary>
    /// 요일을 표시하는 컨트롤
    /// </summary>
    public partial class CjWeek : UserControl
    {
        #region attributes
        /// <summary>
        /// 요일 (0=일, 1=월, ..., 6=토)
        /// </summary>
        private DayOfWeek m_nWeek;
        [Browsable(true)]
        [Description("요일 (0=일, 1=월, ..., 6=토)")]
        public DayOfWeek Week
        {
            get { return m_nWeek; }
            set
            { 
                if (DayOfWeek.Sunday <= value && value <= DayOfWeek.Saturday)
                    m_nWeek = value;

                if (this.InvokeRequired)
                {
                    this.Invoke(new EventHandler(delegate
                    {
                        LblWeek.Text = WeekStr;
                    }));
                }
                else
                    LblWeek.Text = WeekStr;
            }
        }

        /// <summary>
        /// 요일 문자열
        /// </summary>
        [Browsable(true)]
        [Description("요일 (0=일, 1=월, ..., 6=토)")]
        public string WeekStr
        {
            get
            {
                if (m_nWeek == DayOfWeek.Monday)
                    return "월";
                else if (m_nWeek == DayOfWeek.Tuesday)
                    return "화";
                else if (m_nWeek == DayOfWeek.Wednesday)
                    return "수";
                else if (m_nWeek == DayOfWeek.Thursday)
                    return "목";
                else if (m_nWeek == DayOfWeek.Friday)
                    return "금";
                else if (m_nWeek == DayOfWeek.Saturday)
                    return "토";
                else if (m_nWeek == DayOfWeek.Sunday)
                    return "일";
                else
                    return "";
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 존재여부
        /// </summary>
        private bool m_bOutline = false;
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 존재여부")]
        public bool Outline
        {
            get { return m_bOutline; }
            set
            {
                if (value != m_bOutline)
                {
                    m_bOutline = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 색상
        /// </summary>
        private Color m_outlineColor = SystemColors.ButtonShadow;
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 색상")]
        public Color OutlineColor
        {
            get { return m_outlineColor; }
            set
            {
                if (value != m_outlineColor)
                {
                    m_outlineColor = value;
                    this.Invalidate();
                }
            }
        }

        /// <summary>
        /// 선택되지 않은 상태의 배경색
        /// </summary>
        private Color m_bkColor = SystemColors.Control;
        [Browsable(true)]
        [Description("선택되지 않은 상태의 배경색")]
        public Color BkColor
        {
            get { return m_bkColor; }
            set
            {
                if (value != m_bkColor)
                {
                    m_bkColor = value;

                    if (this.InvokeRequired)
                    {
                        this.Invoke(new EventHandler(delegate
                        {
                            LblWeek.BackColor = m_bkColor;
                        }));
                    }
                    else
                        LblWeek.BackColor = m_bkColor;
                }
            }
        }

        /// <summary>
        /// 선택되지 않은 상태의 글자색
        /// </summary>
        private Color m_txtColor = SystemColors.ControlText;
        [Browsable(true)]
        [Description("선택되지 않은 상태의 글자색")]
        public Color TxtColor
        {
            get { return m_txtColor; }
            set
            {
                if (value != m_txtColor)
                {
                    m_txtColor = value;

                    if (this.InvokeRequired)
                    {
                        this.Invoke(new EventHandler(delegate
                        {
                            LblWeek.ForeColor = m_txtColor;
                        }));
                    }
                    else
                        LblWeek.ForeColor = m_txtColor;
                }
            }
        }
        #endregion

        #region 생성자
        /// <summary>
        /// 생성자
        /// </summary>
        public CjWeek()
        {
            InitializeComponent();

            Week = DayOfWeek.Sunday;
        }
        #endregion

        #region 컨트롤 이벤트 
        /// <summary>
        /// WM_PAINT 메시지 이벤트
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_bOutline)
                ControlPaint.DrawBorder(e.Graphics, ClientRectangle, m_outlineColor, ButtonBorderStyle.Solid);

        }
        #endregion

    }
}

반응형

4. 달력

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Systehttp://m.ComponentModel.Design;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using Systehttp://m.Threading.Tasks;
using Systehttp://m.Windows.Forms;

namespace CjControls
{
    /// <summary>
    /// 단일 달력 컨트롤
    /// </summary>
    //[Designer("Systehttp://m.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
    public partial class CjCalendarUnit : UserControl
    {
        #region attributes
        /// <summary>
        /// 달력 순번 (zero-based)
        /// </summary>
        private int m_nIndex = 0;
        [Category("CjCalendarUnit")]
        [Browsable(false)]
        [Description("달력 순번")]
        public int Index
        {
            get { return m_nIndex; }
            set
            {
                m_nIndex = value;
            }
        }

        /// <summary>
        /// 달력이 위치한 행번호 (zero-based)
        /// </summary>
        private int m_nRow = 0;
        [Category("CjCalendarUnit")]
        [Browsable(false)]
        [Description("달력이 위치한 행번호")]
        public int Row
        {
            get { return m_nRow; }
            set
            {
                m_nRow = value;
            }
        }

        /// <summary>
        /// 달력이 위치한 열번호 (zero-based)
        /// </summary>
        private int m_nColumn = 0;
        [Category("CjCalendarUnit")]
        [Browsable(false)]
        [Description("달력이 위치한 열번호")]
        public int Column
        {
            get { return m_nColumn; }
            set
            {
                m_nColumn = value;
            }
        }


        /// <summary>
        /// 컨트롤의 외곽라인의 존재여부
        /// </summary>
        private bool m_bOutline = true;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 존재여부")]
        public bool Outline
        {
            get { return m_bOutline; }
            set
            {
                m_bOutline = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 색상
        /// </summary>
        private Color m_outlineColor = SystemColors.ButtonShadow;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("선택된 상태의 글자색")]
        public Color OutlineColor
        {
            get { return m_outlineColor; }
            set
            {
                m_outlineColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 날짜와 날짜 사이의 가로 간격 (default=0)
        /// </summary>
        private int m_xSpace = 0;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("날짜와 날짜 사이의 가로 간격")]
        public int XSpace
        {
            get { return (int)m_xSpace; }
            set
            {
                m_xSpace = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 날짜와 날짜 사이의 세로 간격 (default=0)
        /// </summary>
        private int m_ySpace = 0;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("날짜와 날짜 사이의 세로 간격")]
        public int YSpace
        {
            get { return (int)m_ySpace; }
            set
            {
                m_ySpace = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 현재 년도 (1~9999)
        /// </summary>
        private int m_nYear = DateTime.Now.Year;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("년도 (1~9999)")]
        public int Year
        {
            get { return m_nYear; }
            set
            {
                m_nYear = value;

                if (m_nYear > 0 && m_nMonth > 0)
                {
                    m_strMonth = string.Format("{0:D4}{1:D2}", m_nYear, m_nMonth);
                }
            }
        }

        /// <summary>
        /// 현재 월 (1~12)
        /// </summary>
        private int m_nMonth = DateTime.Now.Month;
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("월 (1~12)")]
        public int Month
        {
            get { return m_nMonth; }
            set
            {
                m_nMonth = value;

                if (m_nYear > 0 && m_nMonth > 0)
                {
                    m_strMonth = string.Format("{0:D4}{1:D2}", m_nYear, m_nMonth);
                }
            }
        }

        /// <summary>
        /// 현재 달력의 년월 (yyyyMM)
        /// </summary>
        private string m_strMonth = DateTime.Now.ToString("yyyyMM");
        [Category("CjCalendarUnit")]
        [Browsable(true)]
        [Description("달력의 년월 (yyyyMM)")]
        public string MonthString
        {
            get
            {
                if (string.IsNullOrWhiteSpace(m_strMonth))
                    return DateTime.Now.ToString("yyyyMM");
                else
                    return m_strMonth;
            }
            set
            {
                if (!string.IsNullOrWhiteSpace(value) && value.Length >= 6
                    && DateTime.TryParseExact(value.Substring(0, 6) + "01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime dt))
                {
                    Year = dt.Year;
                    Month = dt.Month;
                    m_strMonth = value;
                }
                else
                    Debug.WriteLine($"잘못된 년월({value})을 MonthString에 대입했습니다.");
            }
        }

        /// <summary>
        /// 달력 생성에 성공했는지 여부
        /// </summary>
        public bool m_bSuccessed = false;
        [Category("CjCalendarUnit")]
        [Browsable(false)]
        [Description("달력 생성에 성공했는지 여부")]
        public bool Successed { get { return m_bSuccessed; } }
        #endregion

        #region 생성자
        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="index">달력 순번 (zero-based)</param>
        /// <remarks>달력생성에 실패하면 순번은 0이 됨</remarks>
        public CjCalendarUnit(int index = 0)
        {
            InitializeComponent();

            if (Initialize(DateTime.Now.Year, DateTime.Now.Month))
                if (Create(Year, Month))
                {
                    m_nIndex = index;
                    m_bSuccessed = true;
                }
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="index">달력 순번 (zero-based)</param>
        /// <param name="dtStr">년월 (yyyyMM)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <remarks>달력생성에 실패하면 순번은 0이 됨</remarks>
        public CjCalendarUnit(int index, string dtStr, bool bOutline = false)
        {
            InitializeComponent();

            if (Initialize(dtStr, bOutline))
                if (Create(Year, Month))
                {
                    m_nIndex = index;
                    m_bSuccessed = true;
                }
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="index">달력 순번 (zero-based)</param>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <remarks>달력생성에 실패하면 순번은 0이 됨</remarks>
        public CjCalendarUnit(int index, int year, int month, bool bOutline = false)
        {
            InitializeComponent();

            m_bSuccessed = Initialize(year, month, bOutline);
            m_bSuccessed |= Create(Year, Month);

            if (m_bSuccessed)
                m_nIndex = index;
        }
        #endregion

        #region 컨트롤 이벤트 
        /// <summary>
        /// WM_PAINT 메시지 이벤트
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_bOutline)
                ControlPaint.DrawBorder(e.Graphics, ClientRectangle, m_outlineColor, ButtonBorderStyle.Solid);

            //using (Graphics g = e.Graphics)
            //{
            //    if (SelectionDates != null && SelectionDates.Count > 0)
            //        foreach (CjSelectionDates v in SelectionDates)
            //        {
            //            if (v.Method == 1)
            //            {
            //                if (v.Days != null && v.Days.Count > 0)
            //                    foreach (CjDay vv in v.Days)
            //                    {
            //                        vv.Selected = true;
            //                    }
            //            }
            //            else
            //            {
            //                //CjDay st = v.Start <= v.End ? v.Start : v.End;
            //                //CjDay et = v.Start <= v.End ? v.End : v.Start;
            //                //for (; st <= et; st = st.AddDays(1))
            //                //{
            //                //    st.Selected = true;
            //                //}
            //            }
            //        }
            //}
        }
        #endregion

        #region 외부 함수
        /// <summary>
        /// 달력 초기화하기
        /// </summary>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public bool Initialize(int year, int month, bool bOutline = false)
        {
            m_bOutline = bOutline;

            if (0 < year && year < DateTime.MaxValue.Year
                && 0 < month && month < 12
                && DateTime.TryParseExact($"{year.ToString("D4")}{month.ToString("D2")}01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime dt))
            {
                m_nYear = year;
                m_nMonth = month;
                m_strMonth = $"{year.ToString("D4")}{month.ToString("D2")}";
                return true;
            }

            Debug.WriteLine($"잘못된 날짜 매개변수({year}년, {month}월) 입니다.");
            return false;
        }

        /// <summary>
        /// 달력 초기화하기
        /// </summary>
        /// <param name="dtStr">년월 (yyyyMM)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public bool Initialize(string dtStr, bool bOutline = false)
        {
            m_bOutline = bOutline;

            DateTime dt;
            if ((dtStr.Length >= 8 && DateTime.TryParseExact(dtStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt))
                || (dtStr.Length >= 6 && DateTime.TryParseExact(dtStr.Substring(0, 6) + "01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt)))
            {
                m_nYear = dt.Year;
                m_nMonth = dt.Month;
                m_strMonth = dt.ToString("yyyyMM");
                return true;
            }

            Debug.WriteLine($"잘못된 년월 매개변수({dtStr}) 입니다.");
            return false;
        }

        /// <summary>
        /// 달력 생성하기
        /// </summary>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public bool Create(int year, int month, bool bOutline = false)
        {
            try
            {
                PnlBody.Controls.Clear();

                return Create($"{year.ToString("D4")}{month.ToString("D2")}", bOutline);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return false;
        }

        /// <summary>
        /// 달력 생성하기
        /// </summary>
        /// <param name="dateStr">생성할 달력의 년월(yyyyMM 또는 yyyyMMdd)</param>
        /// <param name="bOutline">외관경계선의 존재 여부</param>
        /// <returns>성공여부</returns>
        public bool Create(string dateStr, bool bOutline = false)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(dateStr) || dateStr.Length < 6)
                    return false;

                DateTime dt;
                if ((dateStr.Length >= 8 && !DateTime.TryParseExact(dateStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt))
                    || !DateTime.TryParseExact(dateStr.Substring(0, 6) + "01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt))
                {
                    Debug.WriteLine($"달력생성 오류: 잘못된 날짜 매개변수 ({dateStr})");
                    return false;
                }

                Size daySize = new Size(40, 20);
                int wi = 0, hi = 0;
                int x = 0;
                int y = 0;
                int tabIndex = 0;
                int day = 1;
                int c, r = 0;
                dt = new DateTime(m_nYear, m_nMonth, day);

                m_nYear = dt.Year;
                m_nMonth = dt.Month;
                m_strMonth = dt.ToString("yyyyMM");

                #region 년월 출력
                if (this.InvokeRequired)
                {
                    this.Invoke(new EventHandler(delegate
                    {
                        LblTitle.Text = string.Format("{0:D4}년 {1:D2}월", m_nYear, m_nMonth);
                    }));
                }
                else
                    LblTitle.Text = string.Format("{0:D4}년 {1:D2}월", m_nYear, m_nMonth);

                hi += LblTitle.Height;
                #endregion

                #region 요일 출력
                for (c = 0; c < 7; ++c)
                {
                    CjWeek cjWeek = new CjWeek();
                    cjWeek.BkColor = System.Drawing.SystemColors.Control;
                    cjWeek.Font = new Systehttp://m.Drawing.Font("바탕체", 10F, System.Drawing.FontStyle.Regular, Systehttp://m.Drawing.GraphicsUnit.Point, ((byte)(129)));
                    cjWeek.Location = new Systehttp://m.Drawing.Point(x, y);
                    cjWeek.MinimumSize = daySize;
                    cjWeek.Name = string.Format("cjWeek{0:D2}", c + 1);
                    cjWeek.Outline = false;
                    cjWeek.OutlineColor = System.Drawing.SystemColors.ButtonShadow;
                    cjWeek.Size = daySize;
                    cjWeek.TabIndex = tabIndex;
                    cjWeek.TxtColor = System.Drawing.SystemColors.ControlText;
                    cjWeek.Week = (System.DayOfWeek)c;

                    cjWeek.Click += OnClickWeeks;
                    PnlBody.Controls.Add(cjWeek);
                    x += daySize.Width;
                }

                y += daySize.Height;
                #endregion

                #region 빈 날짜 출력
                for (c = 0, x = 0; c < (int)dt.DayOfWeek; ++c)
                {
                    CjBlank cjBlank = new CjBlank();
                    cjBlank.Font = new Systehttp://m.Drawing.Font("바탕체", 10F, System.Drawing.FontStyle.Regular, Systehttp://m.Drawing.GraphicsUnit.Point, ((byte)(129)));
                    cjBlank.Location = new Systehttp://m.Drawing.Point(x, y);
                    cjBlank.Margin = new Systehttp://m.Windows.Forms.Padding(4, 3, 4, 3);
                    cjBlank.MinimumSize = daySize;
                    cjBlank.Name = string.Format("cjBlank{0:D2}", c + 1); 
                    cjBlank.Size = daySize;
                    cjBlank.TabIndex = ++tabIndex;

                    PnlBody.Controls.Add(cjBlank);
                    x += daySize.Width;
                }
                #endregion

                #region 날짜 출력
                for (int days = DateTime.DaysInMonth(m_nYear, m_nMonth); day <= days; )
                {
                    for ( ; c < 7 && day <= days; ++c, ++day)
                    {
                        dt = new DateTime(m_nYear, m_nMonth, day);
                        CjDay cjDay = new CjDay();
                        cjDay.BackgroundImageLayout = Systehttp://m.Windows.Forms.ImageLayout.Stretch;
                        cjDay.BkColor = System.Drawing.SystemColors.Control;
                        cjDay.CausesValidation = false;
                        cjDay.DateStr = dt.ToString("yyyyMMdd");
                        cjDay.Font = new Systehttp://m.Drawing.Font("바탕체", 10F);
                        cjDay.Location = new Systehttp://m.Drawing.Point(x, y);
                        cjDay.MinimumSize = daySize;
                        cjDay.Name = string.Format("cjDay{0:D2}", dt.Day);
                        cjDay.Outline = false;
                        cjDay.OutlineColor = System.Drawing.SystemColors.ButtonShadow;
                        cjDay.SelectedColor = System.Drawing.SystemColors.ActiveCaption;
                        cjDay.SelectedTxtColor = System.Drawing.SystemColors.ControlText;
                        cjDay.Size = daySize;
                        cjDay.TabIndex = ++tabIndex;
                        cjDay.TxtColor = System.Drawing.SystemColors.ControlText;

                        cjDay.Click += OnClickDays;
                        PnlBody.Controls.Add(cjDay);

                        x += daySize.Width;
                        if (wi < x)
                            wi = x;
                    }

                    ++r;
                    c = 0;
                    x = 0;
                    y += daySize.Height;
                }
                #endregion

                hi += ((r < 6)? y + daySize.Height : y) + SystemInformation.FrameBorderSize.Height;
                wi += SystemInformation.FrameBorderSize.Width;
                this.Size = new Systehttp://m.Drawing.Size(wi, hi);
                this.MinimumSize = this.Size;

                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine (ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 날짜를 클릭했을 때
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnClickDays(object sender, EventArgs e)
        {
            try
            {
                Debug.WriteLine($"{sender.ToString()}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 요일을 클릭했을 때
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnClickWeeks(object sender, EventArgs e)
        {
            try
            {
                Debug.WriteLine($"{sender.ToString()}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
        #endregion
    }
}
 

5. 다중 달력

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Systehttp://m.Threading.Tasks;
using Systehttp://m.Windows.Forms;
using static Systehttp://m.Windows.Forms.VisualStyles.VisualStyleElement.Tab;

namespace CjControls
{
    public partial class CjCalendars : UserControl
    {
        #region attributes
        /// <summary>
        /// 컨트롤의 외곽라인의 존재여부
        /// </summary>
        private bool m_bOutline = true;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("컨트롤의 외곽라인의 존재여부")]
        public bool Outline
        {
            get { return m_bOutline; }
            set
            {
                m_bOutline = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 컨트롤의 외곽라인의 색상
        /// </summary>
        private Color m_outlineColor = SystemColors.ButtonShadow;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("선택된 상태의 글자색")]
        public Color OutlineColor
        {
            get { return m_outlineColor; }
            set
            {
                m_outlineColor = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 날짜와 날짜 사이의 가로 간격 (default=0)
        /// </summary>
        private int m_xSpace = 0;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("날짜와 날짜 사이의 가로 간격")]
        public int XSpace
        {
            get { return (int)m_xSpace; }
            set
            {
                m_xSpace = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 날짜와 날짜 사이의 세로 간격 (default=0)
        /// </summary>
        private int m_ySpace = 0;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("날짜와 날짜 사이의 세로 간격")]
        public int YSpace
        {
            get { return (int)m_ySpace; }
            set
            {
                m_ySpace = value;
                this.Invalidate();
            }
        }

        /// <summary>
        /// 현재 년도 (1~9999)
        /// </summary>
        private int m_nYear = DateTime.Now.Year;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("년도 (1~9999)")]
        public int Year
        {
            get { return m_nYear; }
            set
            {
                m_nYear = value;

                if (m_nYear > 0 && m_nMonth > 0)
                {
                    m_strMonth = string.Format("{0:D4}{1:D2}", m_nYear, m_nMonth);
                }
            }
        }

        /// <summary>
        /// 현재 월 (1~12)
        /// </summary>
        private int m_nMonth = DateTime.Now.Month;
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("월 (1~12)")]
        public int Month
        {
            get { return m_nMonth; }
            set
            {
                m_nMonth = value;

                if (m_nYear > 0 && m_nMonth > 0)
                {
                    m_strMonth = string.Format("{0:D4}{1:D2}", m_nYear, m_nMonth);
                }
            }
        }

        /// <summary>
        /// 현재 달력의 년월 (yyyyMM)
        /// </summary>
        private string m_strMonth = DateTime.Now.ToString("yyyyMM");
        [Category("CjCalendars")]
        [Browsable(true)]
        [Description("달력의 년월 (yyyyMM)")]
        public string MonthString
        {
            get
            {
                if (string.IsNullOrWhiteSpace(m_strMonth))
                    return DateTime.Now.ToString("yyyyMM");
                else
                    return m_strMonth;
            }
            set
            {
                if (!string.IsNullOrWhiteSpace(value) && value.Length >= 6
                    && DateTime.TryParseExact(value.Substring(0, 6) + "01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime dt))
                {
                    Year = dt.Year;
                    Month = dt.Month;
                    m_strMonth = value;
                }
                else
                    Debug.WriteLine($"잘못된 년월({value})을 MonthString에 대입했습니다.");
            }
        }

        /// <summary>
        /// 선택된 날짜 목록
        /// </summary>
        private List<CjSelectionDates> m_SelectionDates = new List<CjSelectionDates>();
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("선택된 날짜 목록")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public List<CjSelectionDates> SelectionDates
        {
            get
            {
                return m_SelectionDates;
            }
            set
            {
                if (value == null)
                    m_SelectionDates = new List<CjSelectionDates>();
                else
                    m_SelectionDates = value;
            }
        }

        /// <summary>
        /// 선택된 날짜들로 화면에 표시해야할 달력(yyyyMM)들
        /// </summary>
        public List<string> MonthList
        {
            get
            {
                if (SelectionDates == null || SelectionDates.Count < 1)
                    return new List<string>();
                else
                {
                    List<string> list = new List<string>();

                    foreach (CjSelectionDates v in SelectionDates)
                    {
                        if (v.Method == 1)
                        {
                            list.AddRange((from CjDay vv in v.Days
                                           let month = vv.Date.ToString("yyyyMM")
                                           where !list.Contains(month)
                                           orderby vv.Date
                                           select month).Distinct().ToList());
                        }
                        else
                        {
                            for (DateTime st = v.Start.Date; st <= v.End.Date; st = st.AddMonths(1))
                            {
                                string month = st.ToString("yyyyMM");
                                if (!list.Contains(month))
                                    list.Add(month);
                            }
                        }
                    }

                    return list.Distinct().ToList();
                }
            }
        }

        /// <summary>
        /// 단위달력의 최소 폭
        /// </summary>
        private const int MinWidth = 292;
        /// <summary>
        /// 단위달력의 최소 높이
        /// </summary>
        private const int MinHight = 188;

        /// <summary>
        /// 단위달력의 폭
        /// </summary>
        private int m_nUnitWidth = 0;
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("단위달력의 폭")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int UnitWidth
        {
            get { return m_nUnitWidth; }
            set
            {
                m_nUnitWidth = value;
            }
        }

        /// <summary>
        /// 단위달력의 높이
        /// </summary>
        private int m_nUnitHeight = 0;
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("단위달력의 높이")]
        public int UnitHeight
        {
            get { return m_nUnitHeight; }
            set
            {
                m_nUnitHeight = value;
            }
        }

        /// <summary>
        /// 행 갯수
        /// </summary>
        private int m_nRowCount = 1;
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("행 갯수")]
        public int RowCount
        {
            get { return m_nRowCount; }
            set
            {
                m_nRowCount = value;
            }
        }

        /// <summary>
        /// 열 갯수
        /// </summary>
        private int m_nColCount = 1;
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("열 갯수")]
        public int ColCount
        {
            get { return m_nColCount; }
            set
            {
                m_nColCount = value;
            }
        }

        /// <summary>
        /// 수정되었는지 여부
        /// </summary>
        private bool m_bMofidied = false;
        [Category("CjCalendars")]
        [Browsable(false)]
        [Description("수정되었는지 여부")]
        public bool IsModified
        {
            get { return m_bMofidied; }   
            set
            {
                m_bMofidied = value;

                if (m_bMofidied)
                {
                    Create(m_bOutline);
                }
            }
        }
        #endregion

        #region 생성자
        /// <summary>
        /// 생성자
        /// </summary>
        public CjCalendars()
        {
            InitializeComponent();

            try
            {
                if (this.DesignMode)
                    Create();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="dtStr">년월 (yyyyMM)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        public CjCalendars(List<CjSelectionDates> cjSelections, bool bOutline = false)
        {
            InitializeComponent();

            try
            {
                if (cjSelections != null && cjSelections.Count > 0)
                    SelectionDates = cjSelections;

                if (this.DesignMode)
                    Create();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
        #endregion

        #region 컨트롤 이벤트 
        /// <summary>
        /// WM_PAINT 메시지 이벤트
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_bOutline)
                ControlPaint.DrawBorder(e.Graphics, ClientRectangle, m_outlineColor, ButtonBorderStyle.Solid);

            //using (Graphics g = e.Graphics)
            //{
            //    if (SelectionDates != null && SelectionDates.Count > 0)
            //        foreach (CjSelectionDates v in SelectionDates)
            //        {
            //            if (v.Method == 1)
            //            {
            //                if (v.Days != null && v.Days.Count > 0)
            //                    foreach (CjDay vv in v.Days)
            //                    {
            //                        vv.Selected = true;
            //                    }
            //            }
            //            else
            //            {
            //                //CjDay st = v.Start <= v.End ? v.Start : v.End;
            //                //CjDay et = v.Start <= v.End ? v.End : v.Start;
            //                //for (; st <= et; st = st.AddDays(1))
            //                //{
            //                //    st.Selected = true;
            //                //}
            //            }
            //        }
            //}
        }
        #endregion

        #region 내부함수
        /// <summary>
        /// 달력 행렬 설정하기
        /// </summary>
        private void SetLayoutCalendar()
        {
            #region 행렬 생성
            if (this.MonthList.Count > 9)
            {
                m_nRowCount = 3;
                m_nColCount = 4;
            }
            else if (this.MonthList.Count > 6)
            {
                m_nRowCount = 3;
                m_nColCount = 3;
            }
            else if (this.MonthList.Count > 4)
            {
                m_nRowCount = 2;
                m_nColCount = 3;
            }
            else if (this.MonthList.Count == 4)
            {
                m_nRowCount = 2;
                m_nColCount = 2;
            }
            else if (this.MonthList.Count == 3)
            {
                m_nRowCount = 1;
                m_nColCount = 3;
            }
            else if (this.MonthList.Count == 2)
            {
                m_nRowCount = 1;
                m_nColCount = 2;
            }
            else
            {
                m_nRowCount = 1;
                m_nColCount = 1;
            }
            #endregion

        }
        #endregion

        #region 외부 함수
        /// <summary>
        /// 달력 생성하기
        /// </summary>
        /// <param name="bOutline">달력의 외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>생성된 달력의 갯수</returns>
        public int Create(bool bOutline = false)
        {
            int countCalendars = 0;

            try
            {
                SetLayoutCalendar();

                int wi = MinWidth, hi = MinHight;

                if (this.MonthList.Count > 0)
                {
                    int idx = -1;
                    for (int r = 0; r < m_nRowCount; ++r)
                    {
                        for (int c = 0; c < m_nColCount; ++c)
                        {
                            ++idx;

                            if (idx < this.MonthList.Count)
                            {
                                string v = this.MonthList[idx].ToString();
                                int year = Convert.ToInt32(v.Substring(0, 4));
                                int month = Convert.ToInt32(v.Substring(4, 2));
                                var unit = Create(idx, v, bOutline);
                                if (unit != null)
                                {
                                    unit.Row = r;
                                    unit.Column = c;
                                    tblCalendars.Controls.Add(unit, c, r);

                                    if (wi < unit.Width)
                                        wi = unit.Width;
                                    if (hi < unit.Height)
                                        hi = unit.Height;

                                    ++countCalendars;
                                }
                            }
                        }
                    }
                }
                else
                {
                    string dateStr = DateTime.Now.ToString("yyyyMMdd");
                    var unit = Create(0, dateStr, bOutline);
                    if (unit != null)
                    {
                        unit.Row = 0;
                        unit.Column = 0;
                        tblCalendars.Controls.Add(unit, 0, 0);

                        if (wi < unit.Width)
                            wi = unit.Width;
                        if (hi < unit.Height)
                            hi = unit.Height;

                        ++countCalendars;
                        SelectionDates.Add(new CjSelectionDates(0, dateStr.Substring(0, 6)));
                    }
                }

                m_nUnitWidth = wi + SystemInformation.FrameBorderSize.Width * 2;
                m_nUnitHeight = hi + SystemInformation.FrameBorderSize.Height * 2;
                wi = m_nColCount * m_nUnitWidth + SystemInformation.FrameBorderSize.Width * 2;
                hi = m_nRowCount * m_nUnitHeight + SystemInformation.FrameBorderSize.Height * 2;

                this.Size = new Size(wi, hi);
                //tblCalendars.Size = new Size(wi, hi);
                this.tblCalendars.RowCount = m_nRowCount;
                this.tblCalendars.ColumnCount = m_nColCount;

                SetUnitSize(m_nUnitWidth, m_nUnitHeight);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return countCalendars;
        }

        /// <summary>
        /// 달력 생성하기
        /// </summary>
        /// <param name="year">년 (1~9999)</param>
        /// <param name="month">월 (1~12)</param>
        /// <param name="bOutline">외곽라인이 있는지 여부 (default=false)</param>
        /// <returns>성공여부</returns>
        public CjCalendarUnit Create(int idx, int year, int month, bool bOutline = false)
        {
            try
            {
                return Create(idx, string.Format("{0:D4}{1:D2}", year, month), bOutline); ;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return null;
        }

        /// <summary>
        /// 달력 생성하기
        /// </summary>
        /// <param name="idx">달력 순번(zero-based)</param>
        /// <param name="dateStr">생성할 달력의 년월(yyyyMM 또는 yyyyMMdd)</param>
        /// <param name="bOutline">외관경계선의 존재 여부</param>
        /// <returns>성공여부</returns>
        public CjCalendarUnit Create(int idx, string dateStr, bool bOutline = false)
        {
            try
            {
                DateTime dt;
                if ((dateStr.Length >= 8 && !DateTime.TryParseExact(dateStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt))
                    || !DateTime.TryParseExact(dateStr.Substring(0, 6) + "01", "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out dt))
                {
                    Debug.WriteLine($"달력생성 오류: 잘못된 날짜 매개변수 ({dateStr})");
                    return null;
                }

                CjCalendarUnit unit = new CjCalendarUnit(idx, dt.ToString("yyyyMMdd"), bOutline);
                if (unit.Successed)
                {
                    return unit;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            return null;
        }

        /// <summary>
        /// 단위달력의 크기 구하기
        /// </summary>
        /// <returns>Size</returns>
        public Size GetUnitSize()
        {
            return new Size(UnitWidth, UnitHeight);
        }

        /// <summary>
        /// 단위달력의 크기 설정하기
        /// </summary>
        /// <param name="wi">단위달력의 폭</param>
        /// <param name="hi">단위달력의 높이</param>
        public void SetUnitSize(int wi = MinWidth, int hi = MinHight)
        {
            try
            {
                m_nUnitWidth = wi + SystemInformation.FrameBorderSize.Width;
                m_nUnitHeight = hi + SystemInformation.FrameBorderSize.Height;

                if (this.tblCalendars.RowStyles.Count > 0)
                {
                    for (int r = 0; r < this.tblCalendars.RowStyles.Count; ++r)
                    {
                        this.tblCalendars.RowStyles[r].SizeType = SizeType.Absolute;
                        this.tblCalendars.RowStyles[r].Height = m_nUnitHeight;
                    }
                }

                if (this.tblCalendars.ColumnStyles.Count > 0)
                {
                    for (int c = 0; c < this.tblCalendars.ColumnStyles.Count; ++c)
                    {
                        this.tblCalendars.ColumnStyles[c].SizeType = SizeType.Absolute;
                        this.tblCalendars.ColumnStyles[c].Width = m_nUnitWidth;
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 선택된 날짜(기간)을 저장한다. 단, 주어진 인덱스를 가진 기존 데이터는 삭제한다.
        /// </summary>
        /// <param name="idx">선택된 날짜그룹의 인덱스</param>
        /// <param name="startDate">선택시작일</param>
        /// <param name="endDate">선택종료일</param>
        public void SetSelectionDates(int idx, string startDate, string endDate)
        {
            try
            {
                DateTime st = DateTime.ParseExact(startDate, "yyyyMMdd", null);
                DateTime et = DateTime.ParseExact(endDate, "yyyyMMdd", null);

                /// 기존 인덱스를 가진 데이터를 삭제한다.
                var lst = (from CjSelectionDates v in SelectionDates
                           where v.Index == idx
                           select v).ToList();
                if (lst != null)
                {
                    while (lst.Count > 0)
                    {
                        lst.Remove(lst.Last());
                    }
                }

                SelectionDates.Add(new CjSelectionDates(idx, st, et));
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// 선택된 날짜(기간)을 저장한다. 단, 주어진 인덱스를 가진 기존 데이터는 삭제한다.
        /// </summary>
        /// <param name="idx">선택된 날짜그룹의 인덱스</param>
        /// <param name="days">선택된 날짜들 (yyyyMMdd)</param>
        public void SetSelectionDates(int idx, string[] days)
        {
            try
            {
                if (days == null || days.Count() < 1)
                {
                    return;
                }

                /// 기존 인덱스를 가진 데이터를 삭제한다.
                var lst = (from CjSelectionDates v in SelectionDates
                           where v.Index == idx
                           select v).ToList();
                if (lst != null)
                {
                    while (lst.Count > 0)
                    {
                        lst.Remove(lst.Last());
                    }
                }

                CjSelectionDates cjSelectionDates = new CjSelectionDates(idx, days.ToString());
                cjSelectionDates.Method = 1;
                SelectionDates.Add(cjSelectionDates);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }
        #endregion

        #region 내부함수
        /// <summary>
        /// 날짜를 클릭했을 때
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnClickDays(object sender, EventArgs e)
        {
            try
            {
                Debug.WriteLine($"{sender.ToString()}");
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
        #endregion


        #region CjSelectionDates
        /// <summary>
        /// 선택된 날짜 그룹
        /// </summary>
        public class CjSelectionDates
        {
            #region attributes
            /// <summary>
            /// 선택된 날짜 그룹의 순번 (zero-based)
            /// - -1이면 선택된 날짜가 없다.
            /// - -1이 아니면 선택된 날짜가 있다.
            /// </summary>
            public int Index { get; set; } = -1;

            /// <summary>
            /// 선택된 날짜의 유형 (default=0)
            /// </summary>
            /// <remarks>
            /// - 0 = 연결된 날짜로 구성된 그룹으로 시작날짜와 종료날짜가 있다.
            /// - 1 = 낱개로 구성된 날짜의 그룹으로 시작날짜와 종료날짜가 없다.
            /// </remarks>
            public int Method { get; set; } = 0;

            /// <summary>
            /// 선택한 날짜 그룹의 시작날짜
            /// </summary>
            public CjDay Start { get; set; } = null;
            /// <summary>
            /// 선택한 날짜 그룹의 종료날짜
            /// </summary>
            public CjDay End { get; set; } = null;

            /// <summary>
            /// 개별 날짜 모음
            /// </summary>
            public List<CjDay> Days { get; set; } = new List<CjDay>();
            #endregion

            #region 생성자
            /// <summary>
            /// 생성자
            /// </summary>
            public CjSelectionDates()
            {
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="year">선택된 년도</param>
            /// <param name="month">선택된 월</param>
            public CjSelectionDates(int idx, int year, int month)
            {
                DateTime today = new DateTime(year, month, 1);
                if (idx >= 0 && today != DateTime.MinValue && today != DateTime.MaxValue)
                {
                    Index = idx;
                    Method = 0;

                    this.Start = new CjDay(today);
                    this.End = new CjDay(today);
                }
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="dateStr">선택된 날짜 문자열(yyyyMMdd)</param>
            public CjSelectionDates(int idx, string dateStr)
            {
                try
                {
                    if (!DateTime.TryParseExact(dateStr, "yyyyMMdd", null, System.Globalization.DateTimeStyles.AssumeLocal, out DateTime today))
                    {
                        Debug.WriteLine("CjSelectionDates error: 올바르지않은 날짜 형태입니다." + dateStr);
                        return;
                    }

                    Index = (idx < 0 ? -1 : idx);
                    Method = 0;

                    this.Start = new CjDay(today);
                    this.End = new CjDay(today);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="today">선택된 날짜</param>
            public CjSelectionDates(int idx, DateTime today)
            {
                if (idx >= 0 && today != DateTime.MinValue && today != DateTime.MaxValue)
                {
                    Index = idx;
                    Method = 0;

                    this.Start = new CjDay(today);
                    this.End = new CjDay(today);
                }
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="start">선택된 시작날짜</param>
            /// <param name="end">선택된 종료날짜</param>
            public CjSelectionDates(int idx, DateTime start, DateTime end)
            {
                if (idx >= 0
                    && start != DateTime.MinValue && start != DateTime.MaxValue
                    && end != DateTime.MinValue && end != DateTime.MaxValue)
                {
                    Index = idx;
                    Method = 0;

                    this.Start = (start <= end) ? new CjDay(start) : new CjDay(end);
                    this.End = (start <= end) ? new CjDay(end) : new CjDay(start);
                }
                else
                    Debug.WriteLine($"잘못된 매개변수: 순번={idx}, 시작날짜={start}, 종료날짜={end}");
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="days">선택된 날짜들(yyyyMM)</param>
            public CjSelectionDates(int idx, List<string> days)
            {
                if (idx >= 0 && days != null && days.Count > 0)
                {
                    Index = idx;
                    Method = 1;

                    if (Days == null)
                        Days = new List<CjDay>();

                    var lst = from string v in days orderby v select v;
                    foreach (string v in lst)
                    {
                        Days.Add(new CjDay(v));
                    }
                }
            }


            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="days">선택된 날짜들</param>
            public CjSelectionDates(int idx, List<DateTime> days)
            {
                if (idx >= 0 && days != null && days.Count > 0)
                {
                    Index = idx;
                    Method = 1;

                    if (Days == null)
                        Days = new List<CjDay>();

                    var lst = from DateTime v in days orderby v select v;
                    foreach (DateTime v in lst)
                    {
                        Days.Add(new CjDay(v));
                    }
                }
            }

            /// <summary>
            /// 생성자
            /// </summary>
            /// <param name="idx">순번 (zero-based)</param>
            /// <param name="days">선택된 날짜들</param>
            public CjSelectionDates(int idx, List<CjDay> days)
            {
                if (idx >= 0 && days != null && days.Count > 0)
                {
                    Index = idx;
                    Method = 1;

                    if (Days == null)
                        Days = new List<CjDay>();

                    var lst = from DateTime v in days orderby v select v;
                    foreach (DateTime v in lst)
                    {
                        Days.Add(new CjDay(v));
                    }
                }
            }
            #endregion

            /// <summary>
            /// 선택된 달력 리스트
            /// </summary>
            public List<string> getMonthList()
            {
                List<string> rt = new List<string>();

                if (Method == 0)
                {
                    if (Start != null && End != null)
                    {
                        for (DateTime dt = Start.Date; dt <= End.Date; dt.AddMonths(1))
                            rt.Add(dt.ToString("yyyyMM"));
                    }
                    else if (Start != null)
                    {
                        rt.Add(Start.Date.ToString("yyyyMM"));
                    }
                }
                else
                {
                    if (Days.Count > 0)
                    {
                        var lst = (from CjDay v in Days orderby v.Date select new DateTime(v.Year, v.Month, 1)).Distinct().ToList();
                        foreach (DateTime v in lst)
                            rt.Add(v.ToString("yyyyMM"));
                    }
                }

                return rt;
            }
        }
        #endregion
    }
}
 
 
 
예제:

CjControls.7z
0.06MB
728x90
728x90
LIST