Calendar.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Controls / Calendar.cs / 1305600 / Calendar.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
 
namespace System.Windows.Controls
{ 
    ///  
    /// Represents a control that enables a user to select a date by using a visual calendar display.
    ///  
    [TemplatePart(Name = Calendar.ElementRoot, Type = typeof(Panel))]
    [TemplatePart(Name = Calendar.ElementMonth, Type = typeof(CalendarItem))]
    public class Calendar : Control
    { 
        #region Constants
 
        private const string ElementRoot = "PART_Root"; 
        private const string ElementMonth = "PART_CalendarItem";
 
        private const int COLS = 7;
        private const int ROWS = 7;
        private const int YEAR_ROWS = 3;
        private const int YEAR_COLS = 4; 
        private const int YEARS_PER_DECADE = 10;
 
        #endregion Constants 

        #region Data 
        private DateTime? _hoverStart;
        private DateTime? _hoverEnd;
        private bool _isShiftPressed;
        private DateTime? _currentDate; 
        private CalendarItem _monthControl;
        private CalendarBlackoutDatesCollection _blackoutDates; 
        private SelectedDatesCollection _selectedDates; 

        #endregion Data 

        #region Public Events

        public static readonly RoutedEvent SelectedDatesChangedEvent = EventManager.RegisterRoutedEvent("SelectedDatesChanged", RoutingStrategy.Direct, typeof(EventHandler), typeof(Calendar)); 

        ///  
        /// Occurs when a date is selected. 
        /// 
        public event EventHandler SelectedDatesChanged 
        {
            add { AddHandler(SelectedDatesChangedEvent, value); }
            remove { RemoveHandler(SelectedDatesChangedEvent, value); }
        } 

        ///  
        /// Occurs when the DisplayDate property is changed. 
        /// 
        public event EventHandler DisplayDateChanged; 

        /// 
        /// Occurs when the DisplayMode property is changed.
        ///  
        public event EventHandler DisplayModeChanged;
 
        ///  
        /// Occurs when the SelectionMode property is changed.
        ///  
        public event EventHandler SelectionModeChanged;

        #endregion Public Events
 
        /// 
        /// Static constructor 
        ///  
        static Calendar()
        { 
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(typeof(Calendar)));
            KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained));
            LanguageProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnLanguageChanged))); 

            EventManager.RegisterClassHandler(typeof(Calendar), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); 
        } 

        ///  
        /// Initializes a new instance of the Calendar class.
        /// 
        public Calendar()
        { 
            this._blackoutDates = new CalendarBlackoutDatesCollection(this);
            this._selectedDates = new SelectedDatesCollection(this); 
            this.SetCurrentValueInternal(DisplayDateProperty, DateTime.Today); 
        }
 
        #region Public Properties

        #region BlackoutDates
 
        /// 
        /// Gets or sets the dates that are not selectable. 
        ///  
        public CalendarBlackoutDatesCollection BlackoutDates
        { 
            get { return _blackoutDates; }
        }

        #endregion BlackoutDates 

        #region CalendarButtonStyle 
 
        /// 
        /// Gets or sets the style for displaying a CalendarButton. 
        /// 
        public Style CalendarButtonStyle
        {
            get { return (Style)GetValue(CalendarButtonStyleProperty); } 
            set { SetValue(CalendarButtonStyleProperty, value); }
        } 
 
        /// 
        /// Identifies the CalendarButtonStyle dependency property. 
        /// 
        public static readonly DependencyProperty CalendarButtonStyleProperty =
            DependencyProperty.Register(
            "CalendarButtonStyle", 
            typeof(Style),
            typeof(Calendar)); 
 
        #endregion CalendarButtonStyle
 
        #region CalendarDayButtonStyle

        /// 
        /// Gets or sets the style for displaying a day. 
        /// 
        public Style CalendarDayButtonStyle 
        { 
            get { return (Style)GetValue(CalendarDayButtonStyleProperty); }
            set { SetValue(CalendarDayButtonStyleProperty, value); } 
        }

        /// 
        /// Identifies the DayButtonStyle dependency property. 
        /// 
        public static readonly DependencyProperty CalendarDayButtonStyleProperty = 
            DependencyProperty.Register( 
            "CalendarDayButtonStyle",
            typeof(Style), 
            typeof(Calendar));

        #endregion CalendarDayButtonStyle
 
        #region CalendarItemStyle
 
        ///  
        /// Gets or sets the style for a Month.
        ///  
        public Style CalendarItemStyle
        {
            get { return (Style)GetValue(CalendarItemStyleProperty); }
            set { SetValue(CalendarItemStyleProperty, value); } 
        }
 
        ///  
        /// Identifies the MonthStyle dependency property.
        ///  
        public static readonly DependencyProperty CalendarItemStyleProperty =
            DependencyProperty.Register(
            "CalendarItemStyle",
            typeof(Style), 
            typeof(Calendar));
 
        #endregion CalendarItemStyle 

        #region DisplayDate 

        /// 
        /// Gets or sets the date to display.
        ///  
        ///
        public DateTime DisplayDate 
        { 
            get { return (DateTime)GetValue(DisplayDateProperty); }
            set { SetValue(DisplayDateProperty, value); } 
        }

        /// 
        /// Identifies the DisplayDate dependency property. 
        /// 
        public static readonly DependencyProperty DisplayDateProperty = 
            DependencyProperty.Register( 
            "DisplayDate",
            typeof(DateTime), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateChanged, CoerceDisplayDate));

        ///  
        /// DisplayDateProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayDate. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;
            Debug.Assert(c != null);
 
            c.DisplayDateInternal = DateTimeHelper.DiscardDayTime((DateTime)e.NewValue);
            c.UpdateCellItems(); 
            c.OnDisplayDateChanged(new CalendarDateChangedEventArgs((DateTime)e.OldValue, (DateTime)e.NewValue)); 
        }
 
        private static object CoerceDisplayDate(DependencyObject d, object value)
        {
            Calendar c = d as Calendar;
 
            DateTime date = (DateTime)value;
            if (c.DisplayDateStart.HasValue && (date < c.DisplayDateStart.Value)) 
            { 
                value = c.DisplayDateStart.Value;
            } 
            else if (c.DisplayDateEnd.HasValue && (date > c.DisplayDateEnd.Value))
            {
                value = c.DisplayDateEnd.Value;
            } 

            return value; 
        } 

        #endregion DisplayDate 

        #region DisplayDateEnd

        ///  
        /// Gets or sets the last date to be displayed.
        ///  
        /// 
        public DateTime? DisplayDateEnd
        { 
            get { return (DateTime?)GetValue(DisplayDateEndProperty); }
            set { SetValue(DisplayDateEndProperty, value); }
        }
 
        /// 
        /// Identifies the DisplayDateEnd dependency property. 
        ///  
        public static readonly DependencyProperty DisplayDateEndProperty =
            DependencyProperty.Register( 
            "DisplayDateEnd",
            typeof(DateTime?),
            typeof(Calendar),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateEndChanged, CoerceDisplayDateEnd)); 

        ///  
        /// DisplayDateEndProperty property changed handler. 
        /// 
        /// Calendar that changed its DisplayDateEnd. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Calendar c = d as Calendar; 
            Debug.Assert(c != null);
 
            c.CoerceValue(DisplayDateProperty); 
            c.UpdateCellItems();
        } 

        private static object CoerceDisplayDateEnd(DependencyObject d, object value)
        {
            Calendar c = d as Calendar; 

            DateTime? date = (DateTime?)value; 
 
            if (date.HasValue)
            { 
                if (c.DisplayDateStart.HasValue && (date.Value < c.DisplayDateStart.Value))
                {
                    value = c.DisplayDateStart;
                } 

                DateTime? maxSelectedDate = c.SelectedDates.MaximumDate; 
                if (maxSelectedDate.HasValue && (date.Value < maxSelectedDate.Value)) 
                {
                    value = maxSelectedDate; 
                }
            }

            return value; 
        }
 
        #endregion DisplayDateEnd 

        #region DisplayDateStart 

        /// 
        /// Gets or sets the first date to be displayed.
        ///  
        ///
        public DateTime? DisplayDateStart 
        { 
            get { return (DateTime?)GetValue(DisplayDateStartProperty); }
            set { SetValue(DisplayDateStartProperty, value); } 
        }

        /// 
        /// Identifies the DisplayDateStart dependency property. 
        /// 
        public static readonly DependencyProperty DisplayDateStartProperty = 
            DependencyProperty.Register( 
            "DisplayDateStart",
            typeof(DateTime?), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateStartChanged, CoerceDisplayDateStart));

        ///  
        /// DisplayDateStartProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayDateStart. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;
            Debug.Assert(c != null);
 
            c.CoerceValue(DisplayDateEndProperty);
            c.CoerceValue(DisplayDateProperty); 
            c.UpdateCellItems(); 
        }
 
        private static object CoerceDisplayDateStart(DependencyObject d, object value)
        {
            Calendar c = d as Calendar;
 
            DateTime? date = (DateTime?)value;
 
            if (date.HasValue) 
            {
                DateTime? minSelectedDate = c.SelectedDates.MinimumDate; 
                if (minSelectedDate.HasValue && (date.Value > minSelectedDate.Value))
                {
                    value = minSelectedDate;
                } 
            }
 
            return value; 
        }
 
        #endregion DisplayDateStart

        #region DisplayMode
 
        /// 
        /// Gets or sets a value indicating whether the calendar is displayed in months or years. 
        ///  
        public CalendarMode DisplayMode
        { 
            get { return (CalendarMode)GetValue(DisplayModeProperty); }
            set { SetValue(DisplayModeProperty, value); }
        }
 
        /// 
        /// Identifies the DisplayMode dependency property. 
        ///  
        public static readonly DependencyProperty DisplayModeProperty =
            DependencyProperty.Register( 
            "DisplayMode",
            typeof(CalendarMode),
            typeof(Calendar),
            new FrameworkPropertyMetadata(CalendarMode.Month, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayModePropertyChanged), 
            new ValidateValueCallback(IsValidDisplayMode));
 
        ///  
        /// DisplayModeProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayMode.
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            Calendar c = d as Calendar;
            Debug.Assert(c != null); 
            CalendarMode mode = (CalendarMode)e.NewValue; 
            CalendarMode oldMode = (CalendarMode)e.OldValue;
            CalendarItem monthControl = c.MonthControl; 

            switch (mode)
            {
                case CalendarMode.Month: 
                    {
                        if (oldMode == CalendarMode.Year || oldMode == CalendarMode.Decade) 
                        { 
                            // Cancel highlight when switching to month display mode
                            c.HoverStart = c.HoverEnd = null; 
                            c.CurrentDate = c.DisplayDate;
                        }

                        c.UpdateCellItems(); 
                        break;
                    } 
 
                case CalendarMode.Year:
                case CalendarMode.Decade: 
                    if (oldMode == CalendarMode.Month)
                    {
                        c.SetCurrentValueInternal(DisplayDateProperty, c.CurrentDate);
                    } 

                    c.UpdateCellItems(); 
                    break; 

                default: 
                    Debug.Assert(false);
                    break;
            }
 
            c.OnDisplayModeChanged(new CalendarModeChangedEventArgs((CalendarMode)e.OldValue, mode));
        } 
 
        #endregion DisplayMode
 
        #region FirstDayOfWeek

        /// 
        /// Gets or sets the day that is considered the beginning of the week. 
        /// 
        public DayOfWeek FirstDayOfWeek 
        { 
            get { return (DayOfWeek)GetValue(FirstDayOfWeekProperty); }
            set { SetValue(FirstDayOfWeekProperty, value); } 
        }

        /// 
        /// Identifies the FirstDayOfWeek dependency property. 
        /// 
        public static readonly DependencyProperty FirstDayOfWeekProperty = 
            DependencyProperty.Register( 
            "FirstDayOfWeek",
            typeof(DayOfWeek), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek,
                                            OnFirstDayOfWeekChanged),
            new ValidateValueCallback(IsValidFirstDayOfWeek)); 

        ///  
        /// FirstDayOfWeekProperty property changed handler. 
        /// 
        /// Calendar that changed its FirstDayOfWeek. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnFirstDayOfWeekChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Calendar c = d as Calendar; 
            c.UpdateCellItems();
        } 
 
        #endregion FirstDayOfWeek
 
        #region IsTodayHighlighted

        /// 
        /// Gets or sets a value indicating whether the current date is highlighted. 
        /// 
        public bool IsTodayHighlighted 
        { 
            get { return (bool)GetValue(IsTodayHighlightedProperty); }
            set { SetValue(IsTodayHighlightedProperty, value); } 
        }

        /// 
        /// Identifies the IsTodayHighlighted dependency property. 
        /// 
        public static readonly DependencyProperty IsTodayHighlightedProperty = 
            DependencyProperty.Register( 
            "IsTodayHighlighted",
            typeof(bool), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(true, OnIsTodayHighlightedChanged));

        ///  
        /// IsTodayHighlightedProperty property changed handler.
        ///  
        /// Calendar that changed its IsTodayHighlighted. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnIsTodayHighlightedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;

            int i = DateTimeHelper.CompareYearMonth(c.DisplayDateInternal, DateTime.Today); 

            if (i > -2 && i < 2) 
            { 
                c.UpdateCellItems();
            } 
        }

        #endregion IsTodayHighlighted
 
        #region Language
        private static void OnLanguageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        { 
            Calendar c = d as Calendar;
            if (DependencyPropertyHelper.GetValueSource(d, Calendar.FirstDayOfWeekProperty).BaseValueSource ==  BaseValueSource.Default) 
            {
                c.SetCurrentValueInternal(FirstDayOfWeekProperty, DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(c)).FirstDayOfWeek);
                c.UpdateCellItems();
            } 
        }
        #endregion 
 
        #region SelectedDate
 
        /// 
        /// Gets or sets the currently selected date.
        /// 
        /// 
        public DateTime? SelectedDate
        { 
            get { return (DateTime?)GetValue(SelectedDateProperty); } 
            set { SetValue(SelectedDateProperty, value); }
        } 

        /// 
        /// Identifies the SelectedDate dependency property.
        ///  
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register( 
            "SelectedDate", 
            typeof(DateTime?),
            typeof(Calendar), 
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateChanged));

        /// 
        /// SelectedDateProperty property changed handler. 
        /// 
        /// Calendar that changed its SelectedDate. 
        /// DependencyPropertyChangedEventArgs. 
        private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            Calendar c = d as Calendar;
            Debug.Assert(c != null);

            if (c.SelectionMode != CalendarSelectionMode.None || e.NewValue == null) 
            {
                DateTime? addedDate; 
 
                addedDate = (DateTime?)e.NewValue;
 
                if (IsValidDateSelection(c, addedDate))
                {
                    if (!addedDate.HasValue)
                    { 
                        c.SelectedDates.ClearInternal(true /*fireChangeNotification*/);
                    } 
                    else 
                    {
                        if (addedDate.HasValue && !(c.SelectedDates.Count > 0 && c.SelectedDates[0] == addedDate.Value)) 
                        {
                            c.SelectedDates.ClearInternal();
                            c.SelectedDates.Add(addedDate.Value);
                        } 
                    }
 
                    // We update the current date for only the Single mode.For the other modes it automatically gets updated 
                    if (c.SelectionMode == CalendarSelectionMode.SingleDate)
                    { 
                        if (addedDate.HasValue)
                        {
                            c.CurrentDate = addedDate.Value;
                        } 

                        c.UpdateCellItems(); 
                    } 
                }
                else 
                {
                    throw new ArgumentOutOfRangeException("d", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue));
                }
            } 
            else
            { 
                throw new InvalidOperationException(SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidOperation)); 
            }
        } 

        #endregion SelectedDate

        #region SelectedDates 

        // 
 
        /// 
        /// Gets the dates that are currently selected. 
        /// 
        public SelectedDatesCollection SelectedDates
        {
            get { return _selectedDates; } 
        }
 
        #endregion SelectedDates 

        #region SelectionMode 

        /// 
        /// Gets or sets the selection mode for the calendar.
        ///  
        public CalendarSelectionMode SelectionMode
        { 
            get { return (CalendarSelectionMode)GetValue(SelectionModeProperty); } 
            set { SetValue(SelectionModeProperty, value); }
        } 

        /// 
        /// Identifies the SelectionMode dependency property.
        ///  
        public static readonly DependencyProperty SelectionModeProperty =
            DependencyProperty.Register( 
            "SelectionMode", 
            typeof(CalendarSelectionMode),
            typeof(Calendar), 
            new FrameworkPropertyMetadata(CalendarSelectionMode.SingleDate, OnSelectionModeChanged),
            new ValidateValueCallback(IsValidSelectionMode));

        private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar; 
            Debug.Assert(c != null); 

            c.HoverStart = c.HoverEnd = null; 
            c.SelectedDates.ClearInternal(true /*fireChangeNotification*/);
            c.OnSelectionModeChanged(EventArgs.Empty);
        }
 
        #endregion SelectionMode
 
        #endregion Public Properties 

        #region Internal Events 

        internal event MouseButtonEventHandler DayButtonMouseUp;

        internal event RoutedEventHandler DayOrMonthPreviewKeyDown; 

        #endregion Internal Events 
 
        #region Internal Properties
 
        /// 
        /// This flag is used to determine whether DatePicker should change its
        /// DisplayDate because of a SelectedDate change on its Calendar
        ///  
        internal bool DatePickerDisplayDateFlag
        { 
            get; 
            set;
        } 

        internal DateTime DisplayDateInternal
        {
            get; 
            private set;
        } 
 
        internal DateTime DisplayDateEndInternal
        { 
            get
            {
                return this.DisplayDateEnd.GetValueOrDefault(DateTime.MaxValue);
            } 
        }
 
        internal DateTime DisplayDateStartInternal 
        {
            get 
            {
                return this.DisplayDateStart.GetValueOrDefault(DateTime.MinValue);
            }
        } 

        internal DateTime CurrentDate 
        { 
            get { return _currentDate.GetValueOrDefault(this.DisplayDateInternal); }
            set { _currentDate = value; } 
        }

        internal DateTime? HoverStart
        { 
            get
            { 
                return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverStart; 
            }
 
            set
            {
                _hoverStart = value;
            } 
        }
 
        internal DateTime? HoverEnd 
        {
            get 
            {
                return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverEnd;
            }
 
            set
            { 
                _hoverEnd = value; 
            }
        } 

        internal CalendarItem MonthControl
        {
            get { return _monthControl; } 
        }
 
        internal DateTime DisplayMonth 
        {
            get 
            {
                return DateTimeHelper.DiscardDayTime(DisplayDate);
            }
        } 

        internal DateTime DisplayYear 
        { 
            get
            { 
                return new DateTime(DisplayDate.Year, 1, 1);
            }
        }
 
        #endregion Internal Properties
 
        #region Private Properties 

        #endregion Private Properties 

        #region Public Methods

        ///  
        /// Invoked whenever application code or an internal process,
        /// such as a rebuilding layout pass, calls the ApplyTemplate method. 
        ///  
        public override void OnApplyTemplate()
        { 
            if (_monthControl != null)
            {
                _monthControl.Owner = null;
            } 

            base.OnApplyTemplate(); 
 
            _monthControl = GetTemplateChild(ElementMonth) as CalendarItem;
 
            if (_monthControl != null)
            {
                _monthControl.Owner = this;
            } 

            this.CurrentDate = this.DisplayDate; 
            UpdateCellItems(); 
        }
 
        /// 
        /// Provides a text representation of the selected date.
        /// 
        /// A text representation of the selected date, or an empty string if SelectedDate is a null reference. 
        public override string ToString()
        { 
            if (this.SelectedDate != null) 
            {
                return this.SelectedDate.Value.ToString(DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this))); 
            }
            else
            {
                return string.Empty; 
            }
        } 
 
        #endregion Public Methods
 
        #region Protected Methods

        protected virtual void OnSelectedDatesChanged(SelectionChangedEventArgs e)
        { 
            RaiseEvent(e);
        } 
 
        protected virtual void OnDisplayDateChanged(CalendarDateChangedEventArgs e)
        { 
            EventHandler handler = this.DisplayDateChanged;
            if (handler != null)
            {
                handler(this, e); 
            }
        } 
 
        protected virtual void OnDisplayModeChanged(CalendarModeChangedEventArgs e)
        { 
            EventHandler handler = this.DisplayModeChanged;

            if (handler != null)
            { 
                handler(this, e);
            } 
        } 

        protected virtual void OnSelectionModeChanged(EventArgs e) 
        {
            EventHandler handler = this.SelectionModeChanged;

            if (handler != null) 
            {
                handler(this, e); 
            } 
        }
 
        /// 
        /// Creates the automation peer for this Calendar Control.
        /// 
        ///  
        protected override AutomationPeer OnCreateAutomationPeer()
        { 
            return new CalendarAutomationPeer(this); 
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (!e.Handled)
            { 
                e.Handled = ProcessCalendarKey(e);
            } 
        } 

        protected override void OnKeyUp(KeyEventArgs e) 
        {
            if (!e.Handled)
            {
                if (e.Key == Key.LeftShift || e.Key == Key.RightShift) 
                {
                    ProcessShiftKeyUp(); 
                } 
            }
        } 

        #endregion Protected Methods

        #region Internal Methods 

        internal CalendarDayButton FindDayButtonFromDay(DateTime day) 
        { 
            if (this.MonthControl != null)
            { 
                foreach (CalendarDayButton b in this.MonthControl.GetCalendarDayButtons())
                {
                    if (b.DataContext is DateTime)
                    { 
                        if (DateTimeHelper.CompareDays((DateTime)b.DataContext, day) == 0)
                        { 
                            return b; 
                        }
                    } 
                }
            }

            return null; 
        }
 
        internal static bool IsValidDateSelection(Calendar cal, object value) 
        {
            return (value == null) || (!cal.BlackoutDates.Contains((DateTime)value)); 
        }

        internal void OnDayButtonMouseUp(MouseButtonEventArgs e)
        { 
            MouseButtonEventHandler handler = this.DayButtonMouseUp;
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }

        internal void OnDayOrMonthPreviewKeyDown(RoutedEventArgs e)
        { 
            RoutedEventHandler handler = this.DayOrMonthPreviewKeyDown;
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }

        // If the day is a trailing day, Update the DisplayDate
        internal void OnDayClick(DateTime selectedDate) 
        {
            if (this.SelectionMode == CalendarSelectionMode.None) 
            { 
                this.CurrentDate = selectedDate;
            } 

            if (DateTimeHelper.CompareYearMonth(selectedDate, this.DisplayDateInternal) != 0)
            {
                MoveDisplayTo(selectedDate); 
            }
            else 
            { 
                UpdateCellItems();
                FocusDate(selectedDate); 
            }
        }

        internal void OnCalendarButtonPressed(CalendarButton b, bool switchDisplayMode) 
        {
            if (b.DataContext is DateTime) 
            { 
                DateTime d = (DateTime)b.DataContext;
 
                DateTime? newDate = null;
                CalendarMode newMode = CalendarMode.Month;

                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    { 
                        Debug.Assert(false);
                        break; 
                    }

                    case CalendarMode.Year:
                    { 
                        newDate = DateTimeHelper.SetYearMonth(this.DisplayDate, d);
                        newMode = CalendarMode.Month; 
                        break; 
                    }
 
                    case CalendarMode.Decade:
                    {
                        newDate = DateTimeHelper.SetYear(this.DisplayDate, d.Year);
                        newMode = CalendarMode.Year; 
                        break;
                    } 
 
                    default:
                        Debug.Assert(false); 
                        break;
                }

                if (newDate.HasValue) 
                {
                    this.DisplayDate = newDate.Value; 
                    if (switchDisplayMode) 
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, newMode); 
                        FocusDate(this.DisplayMode == CalendarMode.Month ? this.CurrentDate : this.DisplayDate);
                    }
                }
            } 
        }
 
        private DateTime? GetDateOffset(DateTime date, int offset, CalendarMode displayMode) 
        {
            DateTime? result = null; 
            switch (displayMode)
            {
                case CalendarMode.Month:
                { 
                    result = DateTimeHelper.AddMonths(date, offset);
                    break; 
                } 

                case CalendarMode.Year: 
                {
                    result = DateTimeHelper.AddYears(date, offset);
                    break;
                } 

                case CalendarMode.Decade: 
                { 
                    result = DateTimeHelper.AddYears(this.DisplayDate, offset * YEARS_PER_DECADE);
                    break; 
                }

                default:
                Debug.Assert(false); 
                break;
            } 
 
            return result;
        } 

        private void MoveDisplayTo(DateTime? date)
        {
            if (date.HasValue) 
            {
                DateTime d = date.Value.Date; 
                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    {
                        this.SetCurrentValueInternal(DisplayDateProperty, DateTimeHelper.DiscardDayTime(d));
                        this.CurrentDate = d;
                        UpdateCellItems(); 

                        break; 
                    } 

                    case CalendarMode.Year: 
                    case CalendarMode.Decade:
                    {
                        this.SetCurrentValueInternal(DisplayDateProperty, d);
                        UpdateCellItems(); 

                        break; 
                    } 

                    default: 
                    Debug.Assert(false);
                    break;
                }
 
                FocusDate(d);
            } 
        } 

        internal void OnNextClick() 
        {
            DateTime? nextDate = GetDateOffset(this.DisplayDate, 1, this.DisplayMode);
            if (nextDate.HasValue)
            { 
                MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value));
            } 
        } 

        internal void OnPreviousClick() 
        {
            DateTime? nextDate = GetDateOffset(this.DisplayDate, -1, this.DisplayMode);
            if (nextDate.HasValue)
            { 
                MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value));
            } 
        } 

        internal void OnSelectedDatesCollectionChanged(SelectionChangedEventArgs e) 
        {
            if (IsSelectionChanged(e))
            {
                if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) || 
                    AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) ||
                    AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) 
                { 
                    CalendarAutomationPeer peer = FrameworkElementAutomationPeer.FromElement(this) as CalendarAutomationPeer;
                    if (peer != null) 
                    {
                        peer.RaiseSelectionEvents(e);
                    }
                } 

                CoerceFromSelection(); 
                OnSelectedDatesChanged(e); 
            }
        } 

        internal void UpdateCellItems()
        {
            CalendarItem monthControl = this.MonthControl; 
            if (monthControl != null)
            { 
                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    {
                        monthControl.UpdateMonthMode();
                        break;
                    } 

                    case CalendarMode.Year: 
                    { 
                        monthControl.UpdateYearMode();
                        break; 
                    }

                    case CalendarMode.Decade:
                    { 
                        monthControl.UpdateDecadeMode();
                        break; 
                    } 

                    default: 
                        Debug.Assert(false);
                        break;
                }
            } 
        }
 
        #endregion Internal Methods 

        #region Private Methods 

        private void CoerceFromSelection()
        {
            CoerceValue(DisplayDateStartProperty); 
            CoerceValue(DisplayDateEndProperty);
            CoerceValue(DisplayDateProperty); 
        } 

        // This method adds the days that were selected by Keyboard to the SelectedDays Collection 
        private void AddKeyboardSelection()
        {
            if (this.HoverStart != null)
            { 
                this.SelectedDates.ClearInternal();
 
                // In keyboard selection, we are sure that the collection does not include any blackout days 
                this.SelectedDates.AddRange(this.HoverStart.Value, this.CurrentDate);
            } 
        }

        private static bool IsSelectionChanged(SelectionChangedEventArgs e)
        { 
            if (e.AddedItems.Count != e.RemovedItems.Count)
            { 
                return true; 
            }
 
            foreach (DateTime addedDate in e.AddedItems)
            {
                if (!e.RemovedItems.Contains(addedDate))
                { 
                    return true;
                } 
            } 

            return false; 
        }

        private static bool IsValidDisplayMode(object value)
        { 
            CalendarMode mode = (CalendarMode)value;
 
            return mode == CalendarMode.Month 
                || mode == CalendarMode.Year
                || mode == CalendarMode.Decade; 
        }

        internal static bool IsValidFirstDayOfWeek(object value)
        { 
            DayOfWeek day = (DayOfWeek)value;
 
            return day == DayOfWeek.Sunday 
                || day == DayOfWeek.Monday
                || day == DayOfWeek.Tuesday 
                || day == DayOfWeek.Wednesday
                || day == DayOfWeek.Thursday
                || day == DayOfWeek.Friday
                || day == DayOfWeek.Saturday; 
        }
 
        private static bool IsValidKeyboardSelection(Calendar cal, object value) 
        {
            if (value == null) 
            {
                return true;
            }
            else 
            {
                if (cal.BlackoutDates.Contains((DateTime)value)) 
                { 
                    return false;
                } 
                else
                {
                    return DateTime.Compare((DateTime)value, cal.DisplayDateStartInternal) >= 0 && DateTime.Compare((DateTime)value, cal.DisplayDateEndInternal) <= 0;
                } 
            }
        } 
 
        private static bool IsValidSelectionMode(object value)
        { 
            CalendarSelectionMode mode = (CalendarSelectionMode)value;

            return mode == CalendarSelectionMode.SingleDate
                || mode == CalendarSelectionMode.SingleRange 
                || mode == CalendarSelectionMode.MultipleRange
                || mode == CalendarSelectionMode.None; 
        } 

        private void OnSelectedMonthChanged(DateTime? selectedMonth) 
        {
            if (selectedMonth.HasValue)
            {
                Debug.Assert(this.DisplayMode == CalendarMode.Year); 
                this.SetCurrentValueInternal(DisplayDateProperty, selectedMonth.Value);
 
                UpdateCellItems(); 

                FocusDate(selectedMonth.Value); 
            }
        }

        private void OnSelectedYearChanged(DateTime? selectedYear) 
        {
            if (selectedYear.HasValue) 
            { 
                Debug.Assert(this.DisplayMode == CalendarMode.Decade);
                this.SetCurrentValueInternal(DisplayDateProperty, selectedYear.Value); 

                UpdateCellItems();

                FocusDate(selectedYear.Value); 
            }
        } 
 
        internal void FocusDate(DateTime date)
        { 
            if (MonthControl != null)
            {
                MonthControl.FocusDate(date);
            } 
        }
 
 
        /// 
        ///     Called when this element gets focus. 
        /// 
        private static void OnGotFocus(object sender, RoutedEventArgs e)
        {
            // When Calendar gets focus move it to the DisplayDate 
            var c = (Calendar)sender;
            if (!e.Handled && e.OriginalSource == c) 
            { 
                // This check is for the case where the DisplayDate is the first of the month
                // and the SelectedDate is in the middle of the month.  If you tab into the Calendar 
                // the focus should go to the SelectedDate, not the DisplayDate.
                if (c.SelectedDate.HasValue && DateTimeHelper.CompareYearMonth(c.SelectedDate.Value, c.DisplayDateInternal) == 0)
                {
                    c.FocusDate(c.SelectedDate.Value); 
                }
                else 
                { 
                    c.FocusDate(c.DisplayDate);
                } 

                e.Handled = true;
            }
        } 

        private bool ProcessCalendarKey(KeyEventArgs e) 
        { 
            if (this.DisplayMode == CalendarMode.Month)
            { 
                // If a blackout day is inactive, when clicked on it, the previous inactive day which is not a blackout day can get the focus.
                // In this case we should allow keyboard functions on that inactive day
                CalendarDayButton currentDayButton = (MonthControl != null) ? MonthControl.GetCalendarDayButton(this.CurrentDate) : null;
 
                if (DateTimeHelper.CompareYearMonth(this.CurrentDate, this.DisplayDateInternal) != 0 && currentDayButton != null && !currentDayButton.IsInactive)
                { 
                    return false; 
                }
            } 

            bool ctrl, shift;
            CalendarKeyboardHelper.GetMetaKeyState(out ctrl, out shift);
 
            switch (e.Key)
            { 
                case Key.Up: 
                {
                    ProcessUpKey(ctrl, shift); 
                    return true;
                }

                case Key.Down: 
                {
                    ProcessDownKey(ctrl, shift); 
                    return true; 
                }
 
                case Key.Left:
                {
                    ProcessLeftKey(shift);
                    return true; 
                }
 
                case Key.Right: 
                {
                    ProcessRightKey(shift); 
                    return true;
                }

                case Key.PageDown: 
                {
                    ProcessPageDownKey(shift); 
                    return true; 
                }
 
                case Key.PageUp:
                {
                    ProcessPageUpKey(shift);
                    return true; 
                }
 
                case Key.Home: 
                {
                    ProcessHomeKey(shift); 
                    return true;
                }

                case Key.End: 
                {
                    ProcessEndKey(shift); 
                    return true; 
                }
 
                case Key.Enter:
                case Key.Space:
                {
                    return ProcessEnterKey(); 
                }
            } 
 
            return false;
        } 

        private void ProcessDownKey(bool ctrl, bool shift)
        {
            switch (this.DisplayMode) 
            {
                case CalendarMode.Month: 
                { 
                    if (!ctrl || shift)
                    { 
                        DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, COLS), 1);
                        ProcessSelection(shift, selectedDate);
                    }
 
                    break;
                } 
 
                case CalendarMode.Year:
                { 
                    if (ctrl)
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Month);
                        FocusDate(this.DisplayDate); 
                    }
                    else 
                    { 
                        DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, YEAR_COLS);
                        OnSelectedMonthChanged(selectedMonth); 
                    }

                    break;
                } 

                case CalendarMode.Decade: 
                { 
                    if (ctrl)
                    { 
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                        FocusDate(this.DisplayDate);
                    }
                    else 
                    {
                        DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, YEAR_COLS); 
                        OnSelectedYearChanged(selectedYear); 
                    }
 
                    break;
                }
            }
        } 

        private void ProcessEndKey(bool shift) 
        { 
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                {
                    if (this.DisplayDate != null)
                    { 
                        DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1);
 
                        if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0) 
                        {
                            // since DisplayDate is not equal to DateTime.MaxValue we are sure selectedDate is not null 
                            selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value;
                            selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value;
                        }
                        else 
                        {
                            selectedDate = DateTime.MaxValue; 
                        } 

                        ProcessSelection(shift, selectedDate); 
                    }

                    break;
                } 

                case CalendarMode.Year: 
                { 
                    DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 12, 1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break;
                }

                case CalendarMode.Decade: 
                {
                    DateTime? selectedYear = new DateTime(DateTimeHelper.EndOfDecade(this.DisplayDate), 1, 1); 
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            }
        }

        private bool ProcessEnterKey() 
        {
            switch (this.DisplayMode) 
            { 
                case CalendarMode.Year:
                { 
                    this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Month);
                    FocusDate(this.DisplayDate);
                    return true;
                } 

                case CalendarMode.Decade: 
                { 
                    this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                    FocusDate(this.DisplayDate); 
                    return true;
                }
            }
 
            return false;
        } 
 
        private void ProcessHomeKey(bool shift)
        { 
            switch (this.DisplayMode)
            {
                case CalendarMode.Month:
                { 
                    //
                    DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1); 
                    ProcessSelection(shift, selectedDate); 
                    break;
                } 

                case CalendarMode.Year:
                {
                    DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 1, 1); 
                    OnSelectedMonthChanged(selectedMonth);
                    break; 
                } 

                case CalendarMode.Decade: 
                {
                    DateTime? selectedYear = new DateTime(DateTimeHelper.DecadeOfDate(this.DisplayDate), 1, 1);
                    OnSelectedYearChanged(selectedYear);
                    break; 
                }
            } 
        } 

        private void ProcessLeftKey(bool shift) 
        {
            int moveAmmount = (!this.IsRightToLeft) ? -1 : 1;
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount);
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessPageDownKey(bool shift)
        {
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, 1), 1); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, 1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, 10 );
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessPageUpKey(bool shift)
        {
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, -1), -1); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, -1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -10);
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessRightKey(bool shift)
        {
            int moveAmmount = (!this.IsRightToLeft) ? 1 : -1;
            switch (this.DisplayMode) 
            {
                case CalendarMode.Month: 
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount);
                    ProcessSelection(shift, selectedDate); 
                    break;
                }

                case CalendarMode.Year: 
                {
                    DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount); 
                    OnSelectedMonthChanged(selectedMonth); 
                    break;
                } 

                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount); 
                    OnSelectedYearChanged(selectedYear);
                    break; 
                } 
            }
        } 

        private void ProcessSelection(bool shift, DateTime? lastSelectedDate)
        {
            if (this.SelectionMode == CalendarSelectionMode.None && lastSelectedDate != null) 
            {
                OnDayClick(lastSelectedDate.Value); 
                return; 
            }
 
            if (lastSelectedDate != null && IsValidKeyboardSelection(this, lastSelectedDate.Value))
            {
                if (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange)
                { 
                    this.SelectedDates.ClearInternal();
                    if (shift) 
                    { 
                        this._isShiftPressed = true;
                        if (!this.HoverStart.HasValue) 
                        {
                            this.HoverStart = this.HoverEnd = this.CurrentDate;
                        }
 
                        // If we hit a BlackOutDay with keyboard we do not update the HoverEnd
                        CalendarDateRange range; 
 
                        if (DateTime.Compare(this.HoverStart.Value, lastSelectedDate.Value) < 0)
                        { 
                            range = new CalendarDateRange(this.HoverStart.Value, lastSelectedDate.Value);
                        }
                        else
                        { 
                            range = new CalendarDateRange(lastSelectedDate.Value, this.HoverStart.Value);
                        } 
 
                        if (!this.BlackoutDates.ContainsAny(range))
                        { 
                            this._currentDate = lastSelectedDate;
                            this.HoverEnd = lastSelectedDate;
                        }
 
                        OnDayClick(this.CurrentDate);
                    } 
                    else 
                    {
                        this.HoverStart = this.HoverEnd = this.CurrentDate = lastSelectedDate.Value; 
                        AddKeyboardSelection();
                        OnDayClick(lastSelectedDate.Value);
                    }
                } 
                else
                { 
                    // ON CLEAR 
                    this.CurrentDate = lastSelectedDate.Value;
                    this.HoverStart = this.HoverEnd = null; 
                    if (this.SelectedDates.Count > 0)
                    {
                        this.SelectedDates[0] = lastSelectedDate.Value;
                    } 
                    else
                    { 
                        this.SelectedDates.Add(lastSelectedDate.Value); 
                    }
 
                    OnDayClick(lastSelectedDate.Value);
                }

                UpdateCellItems(); 
            }
        } 
 
        private void ProcessShiftKeyUp()
        { 
            if (this._isShiftPressed && (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange))
            {
                AddKeyboardSelection();
                this._isShiftPressed = false; 
                this.HoverStart = this.HoverEnd = null;
            } 
        } 

        private void ProcessUpKey(bool ctrl, bool shift) 
        {
            switch (this.DisplayMode)
            {
                case CalendarMode.Month: 
                {
                    if (ctrl) 
                    { 
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                        FocusDate(this.DisplayDate); 
                    }
                    else
                    {
                        DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, -COLS), -1); 
                        ProcessSelection(shift, selectedDate);
                    } 
 
                    break;
                } 

                case CalendarMode.Year:
                {
                    if (ctrl) 
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Decade); 
                        FocusDate(this.DisplayDate); 
                    }
                    else 
                    {
                        DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, -YEAR_COLS);
                        OnSelectedMonthChanged(selectedMonth);
                    } 

                    break; 
                } 

                case CalendarMode.Decade: 
                {
                    if (!ctrl)
                    {
                        DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -YEAR_COLS); 
                        OnSelectedYearChanged(selectedYear);
                    } 
 
                    break;
                } 
            }
        }

        #endregion Private Methods 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
 
namespace System.Windows.Controls
{ 
    ///  
    /// Represents a control that enables a user to select a date by using a visual calendar display.
    ///  
    [TemplatePart(Name = Calendar.ElementRoot, Type = typeof(Panel))]
    [TemplatePart(Name = Calendar.ElementMonth, Type = typeof(CalendarItem))]
    public class Calendar : Control
    { 
        #region Constants
 
        private const string ElementRoot = "PART_Root"; 
        private const string ElementMonth = "PART_CalendarItem";
 
        private const int COLS = 7;
        private const int ROWS = 7;
        private const int YEAR_ROWS = 3;
        private const int YEAR_COLS = 4; 
        private const int YEARS_PER_DECADE = 10;
 
        #endregion Constants 

        #region Data 
        private DateTime? _hoverStart;
        private DateTime? _hoverEnd;
        private bool _isShiftPressed;
        private DateTime? _currentDate; 
        private CalendarItem _monthControl;
        private CalendarBlackoutDatesCollection _blackoutDates; 
        private SelectedDatesCollection _selectedDates; 

        #endregion Data 

        #region Public Events

        public static readonly RoutedEvent SelectedDatesChangedEvent = EventManager.RegisterRoutedEvent("SelectedDatesChanged", RoutingStrategy.Direct, typeof(EventHandler), typeof(Calendar)); 

        ///  
        /// Occurs when a date is selected. 
        /// 
        public event EventHandler SelectedDatesChanged 
        {
            add { AddHandler(SelectedDatesChangedEvent, value); }
            remove { RemoveHandler(SelectedDatesChangedEvent, value); }
        } 

        ///  
        /// Occurs when the DisplayDate property is changed. 
        /// 
        public event EventHandler DisplayDateChanged; 

        /// 
        /// Occurs when the DisplayMode property is changed.
        ///  
        public event EventHandler DisplayModeChanged;
 
        ///  
        /// Occurs when the SelectionMode property is changed.
        ///  
        public event EventHandler SelectionModeChanged;

        #endregion Public Events
 
        /// 
        /// Static constructor 
        ///  
        static Calendar()
        { 
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(typeof(Calendar)));
            KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained));
            LanguageProperty.OverrideMetadata(typeof(Calendar), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnLanguageChanged))); 

            EventManager.RegisterClassHandler(typeof(Calendar), UIElement.GotFocusEvent, new RoutedEventHandler(OnGotFocus)); 
        } 

        ///  
        /// Initializes a new instance of the Calendar class.
        /// 
        public Calendar()
        { 
            this._blackoutDates = new CalendarBlackoutDatesCollection(this);
            this._selectedDates = new SelectedDatesCollection(this); 
            this.SetCurrentValueInternal(DisplayDateProperty, DateTime.Today); 
        }
 
        #region Public Properties

        #region BlackoutDates
 
        /// 
        /// Gets or sets the dates that are not selectable. 
        ///  
        public CalendarBlackoutDatesCollection BlackoutDates
        { 
            get { return _blackoutDates; }
        }

        #endregion BlackoutDates 

        #region CalendarButtonStyle 
 
        /// 
        /// Gets or sets the style for displaying a CalendarButton. 
        /// 
        public Style CalendarButtonStyle
        {
            get { return (Style)GetValue(CalendarButtonStyleProperty); } 
            set { SetValue(CalendarButtonStyleProperty, value); }
        } 
 
        /// 
        /// Identifies the CalendarButtonStyle dependency property. 
        /// 
        public static readonly DependencyProperty CalendarButtonStyleProperty =
            DependencyProperty.Register(
            "CalendarButtonStyle", 
            typeof(Style),
            typeof(Calendar)); 
 
        #endregion CalendarButtonStyle
 
        #region CalendarDayButtonStyle

        /// 
        /// Gets or sets the style for displaying a day. 
        /// 
        public Style CalendarDayButtonStyle 
        { 
            get { return (Style)GetValue(CalendarDayButtonStyleProperty); }
            set { SetValue(CalendarDayButtonStyleProperty, value); } 
        }

        /// 
        /// Identifies the DayButtonStyle dependency property. 
        /// 
        public static readonly DependencyProperty CalendarDayButtonStyleProperty = 
            DependencyProperty.Register( 
            "CalendarDayButtonStyle",
            typeof(Style), 
            typeof(Calendar));

        #endregion CalendarDayButtonStyle
 
        #region CalendarItemStyle
 
        ///  
        /// Gets or sets the style for a Month.
        ///  
        public Style CalendarItemStyle
        {
            get { return (Style)GetValue(CalendarItemStyleProperty); }
            set { SetValue(CalendarItemStyleProperty, value); } 
        }
 
        ///  
        /// Identifies the MonthStyle dependency property.
        ///  
        public static readonly DependencyProperty CalendarItemStyleProperty =
            DependencyProperty.Register(
            "CalendarItemStyle",
            typeof(Style), 
            typeof(Calendar));
 
        #endregion CalendarItemStyle 

        #region DisplayDate 

        /// 
        /// Gets or sets the date to display.
        ///  
        ///
        public DateTime DisplayDate 
        { 
            get { return (DateTime)GetValue(DisplayDateProperty); }
            set { SetValue(DisplayDateProperty, value); } 
        }

        /// 
        /// Identifies the DisplayDate dependency property. 
        /// 
        public static readonly DependencyProperty DisplayDateProperty = 
            DependencyProperty.Register( 
            "DisplayDate",
            typeof(DateTime), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateChanged, CoerceDisplayDate));

        ///  
        /// DisplayDateProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayDate. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;
            Debug.Assert(c != null);
 
            c.DisplayDateInternal = DateTimeHelper.DiscardDayTime((DateTime)e.NewValue);
            c.UpdateCellItems(); 
            c.OnDisplayDateChanged(new CalendarDateChangedEventArgs((DateTime)e.OldValue, (DateTime)e.NewValue)); 
        }
 
        private static object CoerceDisplayDate(DependencyObject d, object value)
        {
            Calendar c = d as Calendar;
 
            DateTime date = (DateTime)value;
            if (c.DisplayDateStart.HasValue && (date < c.DisplayDateStart.Value)) 
            { 
                value = c.DisplayDateStart.Value;
            } 
            else if (c.DisplayDateEnd.HasValue && (date > c.DisplayDateEnd.Value))
            {
                value = c.DisplayDateEnd.Value;
            } 

            return value; 
        } 

        #endregion DisplayDate 

        #region DisplayDateEnd

        ///  
        /// Gets or sets the last date to be displayed.
        ///  
        /// 
        public DateTime? DisplayDateEnd
        { 
            get { return (DateTime?)GetValue(DisplayDateEndProperty); }
            set { SetValue(DisplayDateEndProperty, value); }
        }
 
        /// 
        /// Identifies the DisplayDateEnd dependency property. 
        ///  
        public static readonly DependencyProperty DisplayDateEndProperty =
            DependencyProperty.Register( 
            "DisplayDateEnd",
            typeof(DateTime?),
            typeof(Calendar),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateEndChanged, CoerceDisplayDateEnd)); 

        ///  
        /// DisplayDateEndProperty property changed handler. 
        /// 
        /// Calendar that changed its DisplayDateEnd. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Calendar c = d as Calendar; 
            Debug.Assert(c != null);
 
            c.CoerceValue(DisplayDateProperty); 
            c.UpdateCellItems();
        } 

        private static object CoerceDisplayDateEnd(DependencyObject d, object value)
        {
            Calendar c = d as Calendar; 

            DateTime? date = (DateTime?)value; 
 
            if (date.HasValue)
            { 
                if (c.DisplayDateStart.HasValue && (date.Value < c.DisplayDateStart.Value))
                {
                    value = c.DisplayDateStart;
                } 

                DateTime? maxSelectedDate = c.SelectedDates.MaximumDate; 
                if (maxSelectedDate.HasValue && (date.Value < maxSelectedDate.Value)) 
                {
                    value = maxSelectedDate; 
                }
            }

            return value; 
        }
 
        #endregion DisplayDateEnd 

        #region DisplayDateStart 

        /// 
        /// Gets or sets the first date to be displayed.
        ///  
        ///
        public DateTime? DisplayDateStart 
        { 
            get { return (DateTime?)GetValue(DisplayDateStartProperty); }
            set { SetValue(DisplayDateStartProperty, value); } 
        }

        /// 
        /// Identifies the DisplayDateStart dependency property. 
        /// 
        public static readonly DependencyProperty DisplayDateStartProperty = 
            DependencyProperty.Register( 
            "DisplayDateStart",
            typeof(DateTime?), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateStartChanged, CoerceDisplayDateStart));

        ///  
        /// DisplayDateStartProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayDateStart. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayDateStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;
            Debug.Assert(c != null);
 
            c.CoerceValue(DisplayDateEndProperty);
            c.CoerceValue(DisplayDateProperty); 
            c.UpdateCellItems(); 
        }
 
        private static object CoerceDisplayDateStart(DependencyObject d, object value)
        {
            Calendar c = d as Calendar;
 
            DateTime? date = (DateTime?)value;
 
            if (date.HasValue) 
            {
                DateTime? minSelectedDate = c.SelectedDates.MinimumDate; 
                if (minSelectedDate.HasValue && (date.Value > minSelectedDate.Value))
                {
                    value = minSelectedDate;
                } 
            }
 
            return value; 
        }
 
        #endregion DisplayDateStart

        #region DisplayMode
 
        /// 
        /// Gets or sets a value indicating whether the calendar is displayed in months or years. 
        ///  
        public CalendarMode DisplayMode
        { 
            get { return (CalendarMode)GetValue(DisplayModeProperty); }
            set { SetValue(DisplayModeProperty, value); }
        }
 
        /// 
        /// Identifies the DisplayMode dependency property. 
        ///  
        public static readonly DependencyProperty DisplayModeProperty =
            DependencyProperty.Register( 
            "DisplayMode",
            typeof(CalendarMode),
            typeof(Calendar),
            new FrameworkPropertyMetadata(CalendarMode.Month, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayModePropertyChanged), 
            new ValidateValueCallback(IsValidDisplayMode));
 
        ///  
        /// DisplayModeProperty property changed handler.
        ///  
        /// Calendar that changed its DisplayMode.
        /// DependencyPropertyChangedEventArgs.
        private static void OnDisplayModePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            Calendar c = d as Calendar;
            Debug.Assert(c != null); 
            CalendarMode mode = (CalendarMode)e.NewValue; 
            CalendarMode oldMode = (CalendarMode)e.OldValue;
            CalendarItem monthControl = c.MonthControl; 

            switch (mode)
            {
                case CalendarMode.Month: 
                    {
                        if (oldMode == CalendarMode.Year || oldMode == CalendarMode.Decade) 
                        { 
                            // Cancel highlight when switching to month display mode
                            c.HoverStart = c.HoverEnd = null; 
                            c.CurrentDate = c.DisplayDate;
                        }

                        c.UpdateCellItems(); 
                        break;
                    } 
 
                case CalendarMode.Year:
                case CalendarMode.Decade: 
                    if (oldMode == CalendarMode.Month)
                    {
                        c.SetCurrentValueInternal(DisplayDateProperty, c.CurrentDate);
                    } 

                    c.UpdateCellItems(); 
                    break; 

                default: 
                    Debug.Assert(false);
                    break;
            }
 
            c.OnDisplayModeChanged(new CalendarModeChangedEventArgs((CalendarMode)e.OldValue, mode));
        } 
 
        #endregion DisplayMode
 
        #region FirstDayOfWeek

        /// 
        /// Gets or sets the day that is considered the beginning of the week. 
        /// 
        public DayOfWeek FirstDayOfWeek 
        { 
            get { return (DayOfWeek)GetValue(FirstDayOfWeekProperty); }
            set { SetValue(FirstDayOfWeekProperty, value); } 
        }

        /// 
        /// Identifies the FirstDayOfWeek dependency property. 
        /// 
        public static readonly DependencyProperty FirstDayOfWeekProperty = 
            DependencyProperty.Register( 
            "FirstDayOfWeek",
            typeof(DayOfWeek), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek,
                                            OnFirstDayOfWeekChanged),
            new ValidateValueCallback(IsValidFirstDayOfWeek)); 

        ///  
        /// FirstDayOfWeekProperty property changed handler. 
        /// 
        /// Calendar that changed its FirstDayOfWeek. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnFirstDayOfWeekChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Calendar c = d as Calendar; 
            c.UpdateCellItems();
        } 
 
        #endregion FirstDayOfWeek
 
        #region IsTodayHighlighted

        /// 
        /// Gets or sets a value indicating whether the current date is highlighted. 
        /// 
        public bool IsTodayHighlighted 
        { 
            get { return (bool)GetValue(IsTodayHighlightedProperty); }
            set { SetValue(IsTodayHighlightedProperty, value); } 
        }

        /// 
        /// Identifies the IsTodayHighlighted dependency property. 
        /// 
        public static readonly DependencyProperty IsTodayHighlightedProperty = 
            DependencyProperty.Register( 
            "IsTodayHighlighted",
            typeof(bool), 
            typeof(Calendar),
            new FrameworkPropertyMetadata(true, OnIsTodayHighlightedChanged));

        ///  
        /// IsTodayHighlightedProperty property changed handler.
        ///  
        /// Calendar that changed its IsTodayHighlighted. 
        /// DependencyPropertyChangedEventArgs.
        private static void OnIsTodayHighlightedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar;

            int i = DateTimeHelper.CompareYearMonth(c.DisplayDateInternal, DateTime.Today); 

            if (i > -2 && i < 2) 
            { 
                c.UpdateCellItems();
            } 
        }

        #endregion IsTodayHighlighted
 
        #region Language
        private static void OnLanguageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        { 
            Calendar c = d as Calendar;
            if (DependencyPropertyHelper.GetValueSource(d, Calendar.FirstDayOfWeekProperty).BaseValueSource ==  BaseValueSource.Default) 
            {
                c.SetCurrentValueInternal(FirstDayOfWeekProperty, DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(c)).FirstDayOfWeek);
                c.UpdateCellItems();
            } 
        }
        #endregion 
 
        #region SelectedDate
 
        /// 
        /// Gets or sets the currently selected date.
        /// 
        /// 
        public DateTime? SelectedDate
        { 
            get { return (DateTime?)GetValue(SelectedDateProperty); } 
            set { SetValue(SelectedDateProperty, value); }
        } 

        /// 
        /// Identifies the SelectedDate dependency property.
        ///  
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register( 
            "SelectedDate", 
            typeof(DateTime?),
            typeof(Calendar), 
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateChanged));

        /// 
        /// SelectedDateProperty property changed handler. 
        /// 
        /// Calendar that changed its SelectedDate. 
        /// DependencyPropertyChangedEventArgs. 
        private static void OnSelectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        { 
            Calendar c = d as Calendar;
            Debug.Assert(c != null);

            if (c.SelectionMode != CalendarSelectionMode.None || e.NewValue == null) 
            {
                DateTime? addedDate; 
 
                addedDate = (DateTime?)e.NewValue;
 
                if (IsValidDateSelection(c, addedDate))
                {
                    if (!addedDate.HasValue)
                    { 
                        c.SelectedDates.ClearInternal(true /*fireChangeNotification*/);
                    } 
                    else 
                    {
                        if (addedDate.HasValue && !(c.SelectedDates.Count > 0 && c.SelectedDates[0] == addedDate.Value)) 
                        {
                            c.SelectedDates.ClearInternal();
                            c.SelectedDates.Add(addedDate.Value);
                        } 
                    }
 
                    // We update the current date for only the Single mode.For the other modes it automatically gets updated 
                    if (c.SelectionMode == CalendarSelectionMode.SingleDate)
                    { 
                        if (addedDate.HasValue)
                        {
                            c.CurrentDate = addedDate.Value;
                        } 

                        c.UpdateCellItems(); 
                    } 
                }
                else 
                {
                    throw new ArgumentOutOfRangeException("d", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue));
                }
            } 
            else
            { 
                throw new InvalidOperationException(SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidOperation)); 
            }
        } 

        #endregion SelectedDate

        #region SelectedDates 

        // 
 
        /// 
        /// Gets the dates that are currently selected. 
        /// 
        public SelectedDatesCollection SelectedDates
        {
            get { return _selectedDates; } 
        }
 
        #endregion SelectedDates 

        #region SelectionMode 

        /// 
        /// Gets or sets the selection mode for the calendar.
        ///  
        public CalendarSelectionMode SelectionMode
        { 
            get { return (CalendarSelectionMode)GetValue(SelectionModeProperty); } 
            set { SetValue(SelectionModeProperty, value); }
        } 

        /// 
        /// Identifies the SelectionMode dependency property.
        ///  
        public static readonly DependencyProperty SelectionModeProperty =
            DependencyProperty.Register( 
            "SelectionMode", 
            typeof(CalendarSelectionMode),
            typeof(Calendar), 
            new FrameworkPropertyMetadata(CalendarSelectionMode.SingleDate, OnSelectionModeChanged),
            new ValidateValueCallback(IsValidSelectionMode));

        private static void OnSelectionModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
        {
            Calendar c = d as Calendar; 
            Debug.Assert(c != null); 

            c.HoverStart = c.HoverEnd = null; 
            c.SelectedDates.ClearInternal(true /*fireChangeNotification*/);
            c.OnSelectionModeChanged(EventArgs.Empty);
        }
 
        #endregion SelectionMode
 
        #endregion Public Properties 

        #region Internal Events 

        internal event MouseButtonEventHandler DayButtonMouseUp;

        internal event RoutedEventHandler DayOrMonthPreviewKeyDown; 

        #endregion Internal Events 
 
        #region Internal Properties
 
        /// 
        /// This flag is used to determine whether DatePicker should change its
        /// DisplayDate because of a SelectedDate change on its Calendar
        ///  
        internal bool DatePickerDisplayDateFlag
        { 
            get; 
            set;
        } 

        internal DateTime DisplayDateInternal
        {
            get; 
            private set;
        } 
 
        internal DateTime DisplayDateEndInternal
        { 
            get
            {
                return this.DisplayDateEnd.GetValueOrDefault(DateTime.MaxValue);
            } 
        }
 
        internal DateTime DisplayDateStartInternal 
        {
            get 
            {
                return this.DisplayDateStart.GetValueOrDefault(DateTime.MinValue);
            }
        } 

        internal DateTime CurrentDate 
        { 
            get { return _currentDate.GetValueOrDefault(this.DisplayDateInternal); }
            set { _currentDate = value; } 
        }

        internal DateTime? HoverStart
        { 
            get
            { 
                return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverStart; 
            }
 
            set
            {
                _hoverStart = value;
            } 
        }
 
        internal DateTime? HoverEnd 
        {
            get 
            {
                return this.SelectionMode == CalendarSelectionMode.None ? null : _hoverEnd;
            }
 
            set
            { 
                _hoverEnd = value; 
            }
        } 

        internal CalendarItem MonthControl
        {
            get { return _monthControl; } 
        }
 
        internal DateTime DisplayMonth 
        {
            get 
            {
                return DateTimeHelper.DiscardDayTime(DisplayDate);
            }
        } 

        internal DateTime DisplayYear 
        { 
            get
            { 
                return new DateTime(DisplayDate.Year, 1, 1);
            }
        }
 
        #endregion Internal Properties
 
        #region Private Properties 

        #endregion Private Properties 

        #region Public Methods

        ///  
        /// Invoked whenever application code or an internal process,
        /// such as a rebuilding layout pass, calls the ApplyTemplate method. 
        ///  
        public override void OnApplyTemplate()
        { 
            if (_monthControl != null)
            {
                _monthControl.Owner = null;
            } 

            base.OnApplyTemplate(); 
 
            _monthControl = GetTemplateChild(ElementMonth) as CalendarItem;
 
            if (_monthControl != null)
            {
                _monthControl.Owner = this;
            } 

            this.CurrentDate = this.DisplayDate; 
            UpdateCellItems(); 
        }
 
        /// 
        /// Provides a text representation of the selected date.
        /// 
        /// A text representation of the selected date, or an empty string if SelectedDate is a null reference. 
        public override string ToString()
        { 
            if (this.SelectedDate != null) 
            {
                return this.SelectedDate.Value.ToString(DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this))); 
            }
            else
            {
                return string.Empty; 
            }
        } 
 
        #endregion Public Methods
 
        #region Protected Methods

        protected virtual void OnSelectedDatesChanged(SelectionChangedEventArgs e)
        { 
            RaiseEvent(e);
        } 
 
        protected virtual void OnDisplayDateChanged(CalendarDateChangedEventArgs e)
        { 
            EventHandler handler = this.DisplayDateChanged;
            if (handler != null)
            {
                handler(this, e); 
            }
        } 
 
        protected virtual void OnDisplayModeChanged(CalendarModeChangedEventArgs e)
        { 
            EventHandler handler = this.DisplayModeChanged;

            if (handler != null)
            { 
                handler(this, e);
            } 
        } 

        protected virtual void OnSelectionModeChanged(EventArgs e) 
        {
            EventHandler handler = this.SelectionModeChanged;

            if (handler != null) 
            {
                handler(this, e); 
            } 
        }
 
        /// 
        /// Creates the automation peer for this Calendar Control.
        /// 
        ///  
        protected override AutomationPeer OnCreateAutomationPeer()
        { 
            return new CalendarAutomationPeer(this); 
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (!e.Handled)
            { 
                e.Handled = ProcessCalendarKey(e);
            } 
        } 

        protected override void OnKeyUp(KeyEventArgs e) 
        {
            if (!e.Handled)
            {
                if (e.Key == Key.LeftShift || e.Key == Key.RightShift) 
                {
                    ProcessShiftKeyUp(); 
                } 
            }
        } 

        #endregion Protected Methods

        #region Internal Methods 

        internal CalendarDayButton FindDayButtonFromDay(DateTime day) 
        { 
            if (this.MonthControl != null)
            { 
                foreach (CalendarDayButton b in this.MonthControl.GetCalendarDayButtons())
                {
                    if (b.DataContext is DateTime)
                    { 
                        if (DateTimeHelper.CompareDays((DateTime)b.DataContext, day) == 0)
                        { 
                            return b; 
                        }
                    } 
                }
            }

            return null; 
        }
 
        internal static bool IsValidDateSelection(Calendar cal, object value) 
        {
            return (value == null) || (!cal.BlackoutDates.Contains((DateTime)value)); 
        }

        internal void OnDayButtonMouseUp(MouseButtonEventArgs e)
        { 
            MouseButtonEventHandler handler = this.DayButtonMouseUp;
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }

        internal void OnDayOrMonthPreviewKeyDown(RoutedEventArgs e)
        { 
            RoutedEventHandler handler = this.DayOrMonthPreviewKeyDown;
            if (null != handler) 
            { 
                handler(this, e);
            } 
        }

        // If the day is a trailing day, Update the DisplayDate
        internal void OnDayClick(DateTime selectedDate) 
        {
            if (this.SelectionMode == CalendarSelectionMode.None) 
            { 
                this.CurrentDate = selectedDate;
            } 

            if (DateTimeHelper.CompareYearMonth(selectedDate, this.DisplayDateInternal) != 0)
            {
                MoveDisplayTo(selectedDate); 
            }
            else 
            { 
                UpdateCellItems();
                FocusDate(selectedDate); 
            }
        }

        internal void OnCalendarButtonPressed(CalendarButton b, bool switchDisplayMode) 
        {
            if (b.DataContext is DateTime) 
            { 
                DateTime d = (DateTime)b.DataContext;
 
                DateTime? newDate = null;
                CalendarMode newMode = CalendarMode.Month;

                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    { 
                        Debug.Assert(false);
                        break; 
                    }

                    case CalendarMode.Year:
                    { 
                        newDate = DateTimeHelper.SetYearMonth(this.DisplayDate, d);
                        newMode = CalendarMode.Month; 
                        break; 
                    }
 
                    case CalendarMode.Decade:
                    {
                        newDate = DateTimeHelper.SetYear(this.DisplayDate, d.Year);
                        newMode = CalendarMode.Year; 
                        break;
                    } 
 
                    default:
                        Debug.Assert(false); 
                        break;
                }

                if (newDate.HasValue) 
                {
                    this.DisplayDate = newDate.Value; 
                    if (switchDisplayMode) 
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, newMode); 
                        FocusDate(this.DisplayMode == CalendarMode.Month ? this.CurrentDate : this.DisplayDate);
                    }
                }
            } 
        }
 
        private DateTime? GetDateOffset(DateTime date, int offset, CalendarMode displayMode) 
        {
            DateTime? result = null; 
            switch (displayMode)
            {
                case CalendarMode.Month:
                { 
                    result = DateTimeHelper.AddMonths(date, offset);
                    break; 
                } 

                case CalendarMode.Year: 
                {
                    result = DateTimeHelper.AddYears(date, offset);
                    break;
                } 

                case CalendarMode.Decade: 
                { 
                    result = DateTimeHelper.AddYears(this.DisplayDate, offset * YEARS_PER_DECADE);
                    break; 
                }

                default:
                Debug.Assert(false); 
                break;
            } 
 
            return result;
        } 

        private void MoveDisplayTo(DateTime? date)
        {
            if (date.HasValue) 
            {
                DateTime d = date.Value.Date; 
                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    {
                        this.SetCurrentValueInternal(DisplayDateProperty, DateTimeHelper.DiscardDayTime(d));
                        this.CurrentDate = d;
                        UpdateCellItems(); 

                        break; 
                    } 

                    case CalendarMode.Year: 
                    case CalendarMode.Decade:
                    {
                        this.SetCurrentValueInternal(DisplayDateProperty, d);
                        UpdateCellItems(); 

                        break; 
                    } 

                    default: 
                    Debug.Assert(false);
                    break;
                }
 
                FocusDate(d);
            } 
        } 

        internal void OnNextClick() 
        {
            DateTime? nextDate = GetDateOffset(this.DisplayDate, 1, this.DisplayMode);
            if (nextDate.HasValue)
            { 
                MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value));
            } 
        } 

        internal void OnPreviousClick() 
        {
            DateTime? nextDate = GetDateOffset(this.DisplayDate, -1, this.DisplayMode);
            if (nextDate.HasValue)
            { 
                MoveDisplayTo(DateTimeHelper.DiscardDayTime(nextDate.Value));
            } 
        } 

        internal void OnSelectedDatesCollectionChanged(SelectionChangedEventArgs e) 
        {
            if (IsSelectionChanged(e))
            {
                if (AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected) || 
                    AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementAddedToSelection) ||
                    AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection)) 
                { 
                    CalendarAutomationPeer peer = FrameworkElementAutomationPeer.FromElement(this) as CalendarAutomationPeer;
                    if (peer != null) 
                    {
                        peer.RaiseSelectionEvents(e);
                    }
                } 

                CoerceFromSelection(); 
                OnSelectedDatesChanged(e); 
            }
        } 

        internal void UpdateCellItems()
        {
            CalendarItem monthControl = this.MonthControl; 
            if (monthControl != null)
            { 
                switch (this.DisplayMode) 
                {
                    case CalendarMode.Month: 
                    {
                        monthControl.UpdateMonthMode();
                        break;
                    } 

                    case CalendarMode.Year: 
                    { 
                        monthControl.UpdateYearMode();
                        break; 
                    }

                    case CalendarMode.Decade:
                    { 
                        monthControl.UpdateDecadeMode();
                        break; 
                    } 

                    default: 
                        Debug.Assert(false);
                        break;
                }
            } 
        }
 
        #endregion Internal Methods 

        #region Private Methods 

        private void CoerceFromSelection()
        {
            CoerceValue(DisplayDateStartProperty); 
            CoerceValue(DisplayDateEndProperty);
            CoerceValue(DisplayDateProperty); 
        } 

        // This method adds the days that were selected by Keyboard to the SelectedDays Collection 
        private void AddKeyboardSelection()
        {
            if (this.HoverStart != null)
            { 
                this.SelectedDates.ClearInternal();
 
                // In keyboard selection, we are sure that the collection does not include any blackout days 
                this.SelectedDates.AddRange(this.HoverStart.Value, this.CurrentDate);
            } 
        }

        private static bool IsSelectionChanged(SelectionChangedEventArgs e)
        { 
            if (e.AddedItems.Count != e.RemovedItems.Count)
            { 
                return true; 
            }
 
            foreach (DateTime addedDate in e.AddedItems)
            {
                if (!e.RemovedItems.Contains(addedDate))
                { 
                    return true;
                } 
            } 

            return false; 
        }

        private static bool IsValidDisplayMode(object value)
        { 
            CalendarMode mode = (CalendarMode)value;
 
            return mode == CalendarMode.Month 
                || mode == CalendarMode.Year
                || mode == CalendarMode.Decade; 
        }

        internal static bool IsValidFirstDayOfWeek(object value)
        { 
            DayOfWeek day = (DayOfWeek)value;
 
            return day == DayOfWeek.Sunday 
                || day == DayOfWeek.Monday
                || day == DayOfWeek.Tuesday 
                || day == DayOfWeek.Wednesday
                || day == DayOfWeek.Thursday
                || day == DayOfWeek.Friday
                || day == DayOfWeek.Saturday; 
        }
 
        private static bool IsValidKeyboardSelection(Calendar cal, object value) 
        {
            if (value == null) 
            {
                return true;
            }
            else 
            {
                if (cal.BlackoutDates.Contains((DateTime)value)) 
                { 
                    return false;
                } 
                else
                {
                    return DateTime.Compare((DateTime)value, cal.DisplayDateStartInternal) >= 0 && DateTime.Compare((DateTime)value, cal.DisplayDateEndInternal) <= 0;
                } 
            }
        } 
 
        private static bool IsValidSelectionMode(object value)
        { 
            CalendarSelectionMode mode = (CalendarSelectionMode)value;

            return mode == CalendarSelectionMode.SingleDate
                || mode == CalendarSelectionMode.SingleRange 
                || mode == CalendarSelectionMode.MultipleRange
                || mode == CalendarSelectionMode.None; 
        } 

        private void OnSelectedMonthChanged(DateTime? selectedMonth) 
        {
            if (selectedMonth.HasValue)
            {
                Debug.Assert(this.DisplayMode == CalendarMode.Year); 
                this.SetCurrentValueInternal(DisplayDateProperty, selectedMonth.Value);
 
                UpdateCellItems(); 

                FocusDate(selectedMonth.Value); 
            }
        }

        private void OnSelectedYearChanged(DateTime? selectedYear) 
        {
            if (selectedYear.HasValue) 
            { 
                Debug.Assert(this.DisplayMode == CalendarMode.Decade);
                this.SetCurrentValueInternal(DisplayDateProperty, selectedYear.Value); 

                UpdateCellItems();

                FocusDate(selectedYear.Value); 
            }
        } 
 
        internal void FocusDate(DateTime date)
        { 
            if (MonthControl != null)
            {
                MonthControl.FocusDate(date);
            } 
        }
 
 
        /// 
        ///     Called when this element gets focus. 
        /// 
        private static void OnGotFocus(object sender, RoutedEventArgs e)
        {
            // When Calendar gets focus move it to the DisplayDate 
            var c = (Calendar)sender;
            if (!e.Handled && e.OriginalSource == c) 
            { 
                // This check is for the case where the DisplayDate is the first of the month
                // and the SelectedDate is in the middle of the month.  If you tab into the Calendar 
                // the focus should go to the SelectedDate, not the DisplayDate.
                if (c.SelectedDate.HasValue && DateTimeHelper.CompareYearMonth(c.SelectedDate.Value, c.DisplayDateInternal) == 0)
                {
                    c.FocusDate(c.SelectedDate.Value); 
                }
                else 
                { 
                    c.FocusDate(c.DisplayDate);
                } 

                e.Handled = true;
            }
        } 

        private bool ProcessCalendarKey(KeyEventArgs e) 
        { 
            if (this.DisplayMode == CalendarMode.Month)
            { 
                // If a blackout day is inactive, when clicked on it, the previous inactive day which is not a blackout day can get the focus.
                // In this case we should allow keyboard functions on that inactive day
                CalendarDayButton currentDayButton = (MonthControl != null) ? MonthControl.GetCalendarDayButton(this.CurrentDate) : null;
 
                if (DateTimeHelper.CompareYearMonth(this.CurrentDate, this.DisplayDateInternal) != 0 && currentDayButton != null && !currentDayButton.IsInactive)
                { 
                    return false; 
                }
            } 

            bool ctrl, shift;
            CalendarKeyboardHelper.GetMetaKeyState(out ctrl, out shift);
 
            switch (e.Key)
            { 
                case Key.Up: 
                {
                    ProcessUpKey(ctrl, shift); 
                    return true;
                }

                case Key.Down: 
                {
                    ProcessDownKey(ctrl, shift); 
                    return true; 
                }
 
                case Key.Left:
                {
                    ProcessLeftKey(shift);
                    return true; 
                }
 
                case Key.Right: 
                {
                    ProcessRightKey(shift); 
                    return true;
                }

                case Key.PageDown: 
                {
                    ProcessPageDownKey(shift); 
                    return true; 
                }
 
                case Key.PageUp:
                {
                    ProcessPageUpKey(shift);
                    return true; 
                }
 
                case Key.Home: 
                {
                    ProcessHomeKey(shift); 
                    return true;
                }

                case Key.End: 
                {
                    ProcessEndKey(shift); 
                    return true; 
                }
 
                case Key.Enter:
                case Key.Space:
                {
                    return ProcessEnterKey(); 
                }
            } 
 
            return false;
        } 

        private void ProcessDownKey(bool ctrl, bool shift)
        {
            switch (this.DisplayMode) 
            {
                case CalendarMode.Month: 
                { 
                    if (!ctrl || shift)
                    { 
                        DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, COLS), 1);
                        ProcessSelection(shift, selectedDate);
                    }
 
                    break;
                } 
 
                case CalendarMode.Year:
                { 
                    if (ctrl)
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Month);
                        FocusDate(this.DisplayDate); 
                    }
                    else 
                    { 
                        DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, YEAR_COLS);
                        OnSelectedMonthChanged(selectedMonth); 
                    }

                    break;
                } 

                case CalendarMode.Decade: 
                { 
                    if (ctrl)
                    { 
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                        FocusDate(this.DisplayDate);
                    }
                    else 
                    {
                        DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, YEAR_COLS); 
                        OnSelectedYearChanged(selectedYear); 
                    }
 
                    break;
                }
            }
        } 

        private void ProcessEndKey(bool shift) 
        { 
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                {
                    if (this.DisplayDate != null)
                    { 
                        DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1);
 
                        if (DateTimeHelper.CompareYearMonth(DateTime.MaxValue, selectedDate.Value) > 0) 
                        {
                            // since DisplayDate is not equal to DateTime.MaxValue we are sure selectedDate is not null 
                            selectedDate = DateTimeHelper.AddMonths(selectedDate.Value, 1).Value;
                            selectedDate = DateTimeHelper.AddDays(selectedDate.Value, -1).Value;
                        }
                        else 
                        {
                            selectedDate = DateTime.MaxValue; 
                        } 

                        ProcessSelection(shift, selectedDate); 
                    }

                    break;
                } 

                case CalendarMode.Year: 
                { 
                    DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 12, 1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break;
                }

                case CalendarMode.Decade: 
                {
                    DateTime? selectedYear = new DateTime(DateTimeHelper.EndOfDecade(this.DisplayDate), 1, 1); 
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            }
        }

        private bool ProcessEnterKey() 
        {
            switch (this.DisplayMode) 
            { 
                case CalendarMode.Year:
                { 
                    this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Month);
                    FocusDate(this.DisplayDate);
                    return true;
                } 

                case CalendarMode.Decade: 
                { 
                    this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                    FocusDate(this.DisplayDate); 
                    return true;
                }
            }
 
            return false;
        } 
 
        private void ProcessHomeKey(bool shift)
        { 
            switch (this.DisplayMode)
            {
                case CalendarMode.Month:
                { 
                    //
                    DateTime? selectedDate = new DateTime(this.DisplayDateInternal.Year, this.DisplayDateInternal.Month, 1); 
                    ProcessSelection(shift, selectedDate); 
                    break;
                } 

                case CalendarMode.Year:
                {
                    DateTime selectedMonth = new DateTime(this.DisplayDate.Year, 1, 1); 
                    OnSelectedMonthChanged(selectedMonth);
                    break; 
                } 

                case CalendarMode.Decade: 
                {
                    DateTime? selectedYear = new DateTime(DateTimeHelper.DecadeOfDate(this.DisplayDate), 1, 1);
                    OnSelectedYearChanged(selectedYear);
                    break; 
                }
            } 
        } 

        private void ProcessLeftKey(bool shift) 
        {
            int moveAmmount = (!this.IsRightToLeft) ? -1 : 1;
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount);
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessPageDownKey(bool shift)
        {
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, 1), 1); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, 1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, 10 );
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessPageUpKey(bool shift)
        {
            switch (this.DisplayMode)
            { 
                case CalendarMode.Month:
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddMonths(this.CurrentDate, -1), -1); 
                    ProcessSelection(shift, selectedDate);
                    break; 
                }

                case CalendarMode.Year:
                { 
                    DateTime? selectedMonth = DateTimeHelper.AddYears(this.DisplayDate, -1);
                    OnSelectedMonthChanged(selectedMonth); 
                    break; 
                }
 
                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -10);
                    OnSelectedYearChanged(selectedYear); 
                    break;
                } 
            } 
        }
 
        private void ProcessRightKey(bool shift)
        {
            int moveAmmount = (!this.IsRightToLeft) ? 1 : -1;
            switch (this.DisplayMode) 
            {
                case CalendarMode.Month: 
                { 
                    DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, moveAmmount), moveAmmount);
                    ProcessSelection(shift, selectedDate); 
                    break;
                }

                case CalendarMode.Year: 
                {
                    DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, moveAmmount); 
                    OnSelectedMonthChanged(selectedMonth); 
                    break;
                } 

                case CalendarMode.Decade:
                {
                    DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, moveAmmount); 
                    OnSelectedYearChanged(selectedYear);
                    break; 
                } 
            }
        } 

        private void ProcessSelection(bool shift, DateTime? lastSelectedDate)
        {
            if (this.SelectionMode == CalendarSelectionMode.None && lastSelectedDate != null) 
            {
                OnDayClick(lastSelectedDate.Value); 
                return; 
            }
 
            if (lastSelectedDate != null && IsValidKeyboardSelection(this, lastSelectedDate.Value))
            {
                if (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange)
                { 
                    this.SelectedDates.ClearInternal();
                    if (shift) 
                    { 
                        this._isShiftPressed = true;
                        if (!this.HoverStart.HasValue) 
                        {
                            this.HoverStart = this.HoverEnd = this.CurrentDate;
                        }
 
                        // If we hit a BlackOutDay with keyboard we do not update the HoverEnd
                        CalendarDateRange range; 
 
                        if (DateTime.Compare(this.HoverStart.Value, lastSelectedDate.Value) < 0)
                        { 
                            range = new CalendarDateRange(this.HoverStart.Value, lastSelectedDate.Value);
                        }
                        else
                        { 
                            range = new CalendarDateRange(lastSelectedDate.Value, this.HoverStart.Value);
                        } 
 
                        if (!this.BlackoutDates.ContainsAny(range))
                        { 
                            this._currentDate = lastSelectedDate;
                            this.HoverEnd = lastSelectedDate;
                        }
 
                        OnDayClick(this.CurrentDate);
                    } 
                    else 
                    {
                        this.HoverStart = this.HoverEnd = this.CurrentDate = lastSelectedDate.Value; 
                        AddKeyboardSelection();
                        OnDayClick(lastSelectedDate.Value);
                    }
                } 
                else
                { 
                    // ON CLEAR 
                    this.CurrentDate = lastSelectedDate.Value;
                    this.HoverStart = this.HoverEnd = null; 
                    if (this.SelectedDates.Count > 0)
                    {
                        this.SelectedDates[0] = lastSelectedDate.Value;
                    } 
                    else
                    { 
                        this.SelectedDates.Add(lastSelectedDate.Value); 
                    }
 
                    OnDayClick(lastSelectedDate.Value);
                }

                UpdateCellItems(); 
            }
        } 
 
        private void ProcessShiftKeyUp()
        { 
            if (this._isShiftPressed && (this.SelectionMode == CalendarSelectionMode.SingleRange || this.SelectionMode == CalendarSelectionMode.MultipleRange))
            {
                AddKeyboardSelection();
                this._isShiftPressed = false; 
                this.HoverStart = this.HoverEnd = null;
            } 
        } 

        private void ProcessUpKey(bool ctrl, bool shift) 
        {
            switch (this.DisplayMode)
            {
                case CalendarMode.Month: 
                {
                    if (ctrl) 
                    { 
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Year);
                        FocusDate(this.DisplayDate); 
                    }
                    else
                    {
                        DateTime? selectedDate = this._blackoutDates.GetNonBlackoutDate(DateTimeHelper.AddDays(this.CurrentDate, -COLS), -1); 
                        ProcessSelection(shift, selectedDate);
                    } 
 
                    break;
                } 

                case CalendarMode.Year:
                {
                    if (ctrl) 
                    {
                        this.SetCurrentValueInternal(DisplayModeProperty, CalendarMode.Decade); 
                        FocusDate(this.DisplayDate); 
                    }
                    else 
                    {
                        DateTime? selectedMonth = DateTimeHelper.AddMonths(this.DisplayDate, -YEAR_COLS);
                        OnSelectedMonthChanged(selectedMonth);
                    } 

                    break; 
                } 

                case CalendarMode.Decade: 
                {
                    if (!ctrl)
                    {
                        DateTime? selectedYear = DateTimeHelper.AddYears(this.DisplayDate, -YEAR_COLS); 
                        OnSelectedYearChanged(selectedYear);
                    } 
 
                    break;
                } 
            }
        }

        #endregion Private Methods 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK