A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1263 lines
49 KiB

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
using System;
using System.Diagnostics;
using System.Globalization;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Represents the currently displayed month or year on a
/// <see cref="T:Avalonia.Controls.Calendar" />.
/// </summary>
public sealed class CalendarItem : TemplatedControl
{
/// <summary>
/// The number of days per week.
/// </summary>
private const int NumberOfDaysPerWeek = 7;
private const string PART_ElementHeaderButton = "HeaderButton";
private const string PART_ElementPreviousButton = "PreviousButton";
private const string PART_ElementNextButton = "NextButton";
private const string PART_ElementMonthView = "MonthView";
private const string PART_ElementYearView = "YearView";
private Button _headerButton;
private Button _nextButton;
private Button _previousButton;
private Grid _monthView;
private Grid _yearView;
private ITemplate<IControl> _dayTitleTemplate;
private CalendarButton _lastCalendarButton;
private CalendarDayButton _lastCalendarDayButton;
private DateTime _currentMonth;
private bool _isMouseLeftButtonDown = false;
private bool _isMouseLeftButtonDownYearView = false;
private bool _isControlPressed = false;
private System.Globalization.Calendar _calendar = new System.Globalization.GregorianCalendar();
private PointerPressedEventArgs _downEventArg;
private PointerPressedEventArgs _downEventArgYearView;
internal Calendar Owner { get; set; }
internal CalendarDayButton CurrentButton { get; set; }
public static readonly StyledProperty<IBrush> HeaderBackgroundProperty = Calendar.HeaderBackgroundProperty.AddOwner<CalendarItem>();
public IBrush HeaderBackground
{
get { return GetValue(HeaderBackgroundProperty); }
set { SetValue(HeaderBackgroundProperty, value); }
}
public static readonly DirectProperty<CalendarItem, ITemplate<IControl>> DayTitleTemplateProperty =
AvaloniaProperty.RegisterDirect<CalendarItem, ITemplate<IControl>>(
nameof(DayTitleTemplate),
o => o.DayTitleTemplate,
(o,v) => o.DayTitleTemplate = v,
defaultBindingMode: BindingMode.OneTime);
public ITemplate<IControl> DayTitleTemplate
{
get { return _dayTitleTemplate; }
set { SetAndRaise(DayTitleTemplateProperty, ref _dayTitleTemplate, value); }
}
/// <summary>
/// Gets the button that allows switching between month mode, year mode,
/// and decade mode.
/// </summary>
internal Button HeaderButton
{
get { return _headerButton; }
private set
{
if (_headerButton != null)
_headerButton.Click -= HeaderButton_Click;
_headerButton = value;
if (_headerButton != null)
{
_headerButton.Click += HeaderButton_Click;
_headerButton.Focusable = false;
}
}
}
/// <summary>
/// Gets the button that displays the next page of the calendar when it
/// is clicked.
/// </summary>
internal Button NextButton
{
get { return _nextButton; }
private set
{
if (_nextButton != null)
_nextButton.Click -= NextButton_Click;
_nextButton = value;
if (_nextButton != null)
{
// If the user does not provide a Content value in template,
// we provide a helper text that can be used in
// Accessibility this text is not shown on the UI, just used
// for Accessibility purposes
if (_nextButton.Content == null)
{
_nextButton.Content = "next button";
}
_nextButton.IsVisible = true;
_nextButton.Click += NextButton_Click;
_nextButton.Focusable = false;
}
}
}
/// <summary>
/// Gets the button that displays the previous page of the calendar when
/// it is clicked.
/// </summary>
internal Button PreviousButton
{
get { return _previousButton; }
private set
{
if (_previousButton != null)
_previousButton.Click -= PreviousButton_Click;
_previousButton = value;
if (_previousButton != null)
{
// If the user does not provide a Content value in template,
// we provide a helper text that can be used in
// Accessibility this text is not shown on the UI, just used
// for Accessibility purposes
if (_previousButton.Content == null)
{
_previousButton.Content = "previous button";
}
_previousButton.IsVisible = true;
_previousButton.Click += PreviousButton_Click;
_previousButton.Focusable = false;
}
}
}
/// <summary>
/// Gets the Grid that hosts the content when in month mode.
/// </summary>
internal Grid MonthView
{
get { return _monthView; }
private set
{
if (_monthView != null)
_monthView.PointerLeave -= MonthView_MouseLeave;
_monthView = value;
if (_monthView != null)
_monthView.PointerLeave += MonthView_MouseLeave;
}
}
/// <summary>
/// Gets the Grid that hosts the content when in year or decade mode.
/// </summary>
internal Grid YearView
{
get { return _yearView; }
private set
{
if (_yearView != null)
_yearView.PointerLeave -= YearView_MouseLeave;
_yearView = value;
if (_yearView != null)
_yearView.PointerLeave += YearView_MouseLeave;
}
}
private void PopulateGrids()
{
if (MonthView != null)
{
for (int i = 0; i < Calendar.RowsPerMonth; i++)
{
if (_dayTitleTemplate != null)
{
var cell = _dayTitleTemplate.Build();
cell.DataContext = string.Empty;
cell.SetValue(Grid.RowProperty, 0);
cell.SetValue(Grid.ColumnProperty, i);
MonthView.Children.Add(cell);
}
}
for (int i = 1; i < Calendar.RowsPerMonth; i++)
{
for (int j = 0; j < Calendar.ColumnsPerMonth; j++)
{
CalendarDayButton cell = new CalendarDayButton();
if (Owner != null)
{
cell.Owner = Owner;
}
cell.SetValue(Grid.RowProperty, i);
cell.SetValue(Grid.ColumnProperty, j);
cell.CalendarDayButtonMouseDown += Cell_MouseLeftButtonDown;
cell.CalendarDayButtonMouseUp += Cell_MouseLeftButtonUp;
cell.PointerEnter += Cell_MouseEnter;
cell.PointerLeave += Cell_MouseLeave;
cell.Click += Cell_Click;
MonthView.Children.Add(cell);
}
}
}
if (YearView != null)
{
CalendarButton month;
for (int i = 0; i < Calendar.RowsPerYear; i++)
{
for (int j = 0; j < Calendar.ColumnsPerYear; j++)
{
month = new CalendarButton();
if (Owner != null)
{
month.Owner = Owner;
}
month.SetValue(Grid.RowProperty, i);
month.SetValue(Grid.ColumnProperty, j);
month.CalendarLeftMouseButtonDown += Month_CalendarButtonMouseDown;
month.CalendarLeftMouseButtonUp += Month_CalendarButtonMouseUp;
month.PointerEnter += Month_MouseEnter;
month.PointerLeave += Month_MouseLeave;
YearView.Children.Add(month);
}
}
}
}
/// <summary>
/// Builds the visual tree for the
/// <see cref="T:System.Windows.Controls.Primitives.CalendarItem" />
/// when a new template is applied.
/// </summary>
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
HeaderButton = e.NameScope.Find<Button>(PART_ElementHeaderButton);
PreviousButton = e.NameScope.Find<Button>(PART_ElementPreviousButton);
NextButton = e.NameScope.Find<Button>(PART_ElementNextButton);
MonthView = e.NameScope.Find<Grid>(PART_ElementMonthView);
YearView = e.NameScope.Find<Grid>(PART_ElementYearView);
if (Owner != null)
{
UpdateDisabled(Owner.IsEnabled);
}
PopulateGrids();
if (MonthView != null && YearView != null)
{
if (Owner != null)
{
Owner.SelectedMonth = Owner.DisplayDateInternal;
Owner.SelectedYear = Owner.DisplayDateInternal;
if (Owner.DisplayMode == CalendarMode.Year)
{
UpdateYearMode();
}
else if (Owner.DisplayMode == CalendarMode.Decade)
{
UpdateDecadeMode();
}
if (Owner.DisplayMode == CalendarMode.Month)
{
UpdateMonthMode();
MonthView.IsVisible = true;
YearView.IsVisible = false;
}
else
{
YearView.IsVisible = true;
MonthView.IsVisible = false;
}
}
else
{
UpdateMonthMode();
MonthView.IsVisible = true;
YearView.IsVisible = false;
}
}
}
private void SetDayTitles()
{
for (int childIndex = 0; childIndex < Calendar.ColumnsPerMonth; childIndex++)
{
var daytitle = MonthView.Children[childIndex];
if (daytitle != null)
{
if (Owner != null)
{
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)Owner.FirstDayOfWeek) % NumberOfDaysPerWeek];
}
else
{
daytitle.DataContext = DateTimeHelper.GetCurrentDateFormat().ShortestDayNames[(childIndex + (int)DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek) % NumberOfDaysPerWeek];
}
}
}
}
/// <summary>
/// How many days of the previous month need to be displayed.
/// </summary>
private int PreviousMonthDays(DateTime firstOfMonth)
{
DayOfWeek day = _calendar.GetDayOfWeek(firstOfMonth);
int i;
if (Owner != null)
{
i = ((day - Owner.FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
}
else
{
i = ((day - DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek + NumberOfDaysPerWeek) % NumberOfDaysPerWeek);
}
if (i == 0)
{
return NumberOfDaysPerWeek;
}
else
{
return i;
}
}
internal void UpdateMonthMode()
{
if (Owner != null)
{
Debug.Assert(Owner.DisplayDate != null, "The Owner Calendar's DisplayDate should not be null!");
_currentMonth = Owner.DisplayDateInternal;
}
else
{
_currentMonth = DateTime.Today;
}
if (_currentMonth != null)
{
SetMonthModeHeaderButton();
SetMonthModePreviousButton(_currentMonth);
SetMonthModeNextButton(_currentMonth);
if (MonthView != null)
{
SetDayTitles();
SetCalendarDayButtons(_currentMonth);
}
}
}
private void SetMonthModeHeaderButton()
{
if (HeaderButton != null)
{
if (Owner != null)
{
HeaderButton.Content = Owner.DisplayDateInternal.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
HeaderButton.IsEnabled = true;
}
else
{
HeaderButton.Content = DateTime.Today.ToString("Y", DateTimeHelper.GetCurrentDateFormat());
}
}
}
private void SetMonthModeNextButton(DateTime firstDayOfMonth)
{
if (Owner != null && NextButton != null)
{
// DisplayDate is equal to DateTime.MaxValue
if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MaxValue) == 0)
{
NextButton.IsEnabled = false;
}
else
{
// Since we are sure DisplayDate is not equal to
// DateTime.MaxValue, it is safe to use AddMonths
DateTime firstDayOfNextMonth = _calendar.AddMonths(firstDayOfMonth, 1);
NextButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeEnd, firstDayOfNextMonth) > -1);
}
}
}
private void SetMonthModePreviousButton(DateTime firstDayOfMonth)
{
if (Owner != null && PreviousButton != null)
{
PreviousButton.IsEnabled = (DateTimeHelper.CompareDays(Owner.DisplayDateRangeStart, firstDayOfMonth) < 0);
}
}
private void SetButtonState(CalendarDayButton childButton, DateTime dateToAdd)
{
if (Owner != null)
{
childButton.Opacity = 1;
// If the day is outside the DisplayDateStart/End boundary, do
// not show it
if (DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareDays(dateToAdd, Owner.DisplayDateRangeEnd) > 0)
{
childButton.IsEnabled = false;
childButton.IsToday = false;
childButton.IsSelected = false;
childButton.Opacity = 0;
}
else
{
// SET IF THE DAY IS SELECTABLE OR NOT
if (Owner.BlackoutDates.Contains(dateToAdd))
{
childButton.IsBlackout = true;
}
else
{
childButton.IsBlackout = false;
}
childButton.IsEnabled = true;
// SET IF THE DAY IS INACTIVE OR NOT: set if the day is a
// trailing day or not
childButton.IsInactive = (DateTimeHelper.CompareYearMonth(dateToAdd, Owner.DisplayDateInternal) != 0);
// SET IF THE DAY IS TODAY OR NOT
childButton.IsToday = (Owner.IsTodayHighlighted && dateToAdd == DateTime.Today);
// SET IF THE DAY IS SELECTED OR NOT
childButton.IsSelected = false;
foreach (DateTime item in Owner.SelectedDates)
{
// Since we should be comparing the Date values not
// DateTime values, we can't use
// Owner.SelectedDates.Contains(dateToAdd) directly
childButton.IsSelected |= (DateTimeHelper.CompareDays(dateToAdd, item) == 0);
}
// SET THE FOCUS ELEMENT
if (Owner.LastSelectedDate != null)
{
if (DateTimeHelper.CompareDays(Owner.LastSelectedDate.Value, dateToAdd) == 0)
{
if (Owner.FocusButton != null)
{
Owner.FocusButton.IsCurrent = false;
}
Owner.FocusButton = childButton;
if (Owner.HasFocusInternal)
{
Owner.FocusButton.IsCurrent = true;
}
}
else
{
childButton.IsCurrent = false;
}
}
}
}
}
private void SetCalendarDayButtons(DateTime firstDayOfMonth)
{
int lastMonthToDisplay = PreviousMonthDays(firstDayOfMonth);
DateTime dateToAdd;
if (DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MinValue) > 0)
{
// DisplayDate is not equal to DateTime.MinValue we can subtract
// days from the DisplayDate
dateToAdd = _calendar.AddDays(firstDayOfMonth, -lastMonthToDisplay);
}
else
{
dateToAdd = firstDayOfMonth;
}
if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
{
Owner.HoverEndIndex = null;
Owner.HoverStartIndex = null;
}
int count = Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
for (int childIndex = Calendar.ColumnsPerMonth; childIndex < count; childIndex++)
{
CalendarDayButton childButton = MonthView.Children[childIndex] as CalendarDayButton;
Contract.Requires<ArgumentNullException>(childButton != null);
childButton.Index = childIndex;
SetButtonState(childButton, dateToAdd);
// Update the indexes of hoverStart and hoverEnd
if (Owner != null && Owner.HoverEnd != null && Owner.HoverStart != null)
{
if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverEnd.Value) == 0)
{
Owner.HoverEndIndex = childIndex;
}
if (DateTimeHelper.CompareDays(dateToAdd, Owner.HoverStart.Value) == 0)
{
Owner.HoverStartIndex = childIndex;
}
}
//childButton.Focusable = false;
childButton.Content = dateToAdd.Day.ToString(DateTimeHelper.GetCurrentDateFormat());
childButton.DataContext = dateToAdd;
if (DateTime.Compare((DateTime)DateTimeHelper.DiscardTime(DateTime.MaxValue), dateToAdd) > 0)
{
// Since we are sure DisplayDate is not equal to
// DateTime.MaxValue, it is safe to use AddDays
dateToAdd = _calendar.AddDays(dateToAdd, 1);
}
else
{
// DisplayDate is equal to the DateTime.MaxValue, so there
// are no trailing days
childIndex++;
for (int i = childIndex; i < count; i++)
{
childButton = MonthView.Children[i] as CalendarDayButton;
Contract.Requires<ArgumentNullException>(childButton != null);
// button needs a content to occupy the necessary space
// for the content presenter
childButton.Content = i.ToString(DateTimeHelper.GetCurrentDateFormat());
childButton.IsEnabled = false;
childButton.Opacity = 0;
}
return;
}
}
// If the HoverStart or HoverEndInternal could not be found on the
// DisplayMonth set the values of the HoverStartIndex or
// HoverEndIndex to be the first or last day indexes on the current
// month
if (Owner != null && Owner.HoverStart.HasValue && Owner.HoverEndInternal.HasValue)
{
if (!Owner.HoverEndIndex.HasValue)
{
if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
{
Owner.HoverEndIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
}
else
{
Owner.HoverEndIndex = Calendar.ColumnsPerMonth;
}
}
if (!Owner.HoverStartIndex.HasValue)
{
if (DateTimeHelper.CompareDays(Owner.HoverEndInternal.Value, Owner.HoverStart.Value) > 0)
{
Owner.HoverStartIndex = Calendar.ColumnsPerMonth;
}
else
{
Owner.HoverStartIndex = Calendar.ColumnsPerMonth * Calendar.RowsPerMonth - 1;
}
}
}
}
internal void UpdateYearMode()
{
if (Owner != null)
{
Debug.Assert(Owner.SelectedMonth != null, "The Owner Calendar's SelectedMonth should not be null!");
_currentMonth = (DateTime)Owner.SelectedMonth;
}
else
{
_currentMonth = DateTime.Today;
}
if (_currentMonth != null)
{
SetYearModeHeaderButton();
SetYearModePreviousButton();
SetYearModeNextButton();
if (YearView != null)
{
SetMonthButtonsForYearMode();
}
}
}
private void SetYearModeHeaderButton()
{
if (HeaderButton != null)
{
HeaderButton.IsEnabled = true;
HeaderButton.Content = _currentMonth.Year.ToString(DateTimeHelper.GetCurrentDateFormat());
}
}
private void SetYearModePreviousButton()
{
if (Owner != null && PreviousButton != null)
{
PreviousButton.IsEnabled = (Owner.DisplayDateRangeStart.Year != _currentMonth.Year);
}
}
private void SetYearModeNextButton()
{
if (Owner != null && NextButton != null)
{
NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year != _currentMonth.Year);
}
}
private void SetMonthButtonsForYearMode()
{
int count = 0;
foreach (object child in YearView.Children)
{
CalendarButton childButton = child as CalendarButton;
Contract.Requires<ArgumentNullException>(childButton != null);
// There should be no time component. Time is 12:00 AM
DateTime day = new DateTime(_currentMonth.Year, count + 1, 1);
childButton.DataContext = day;
childButton.Content = DateTimeHelper.GetCurrentDateFormat().AbbreviatedMonthNames[count];
childButton.IsVisible = true;
if (Owner != null)
{
if (day.Year == _currentMonth.Year && day.Month == _currentMonth.Month && day.Day == _currentMonth.Day)
{
Owner.FocusCalendarButton = childButton;
childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
}
else
{
childButton.IsCalendarButtonFocused = false;
}
Debug.Assert(Owner.DisplayDateInternal != null, "The Owner Calendar's DisplayDateInternal should not be null!");
childButton.IsSelected = (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateInternal) == 0);
if (DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeStart) < 0 || DateTimeHelper.CompareYearMonth(day, Owner.DisplayDateRangeEnd) > 0)
{
childButton.IsEnabled = false;
childButton.Opacity = 0;
}
else
{
childButton.IsEnabled = true;
childButton.Opacity = 1;
}
}
childButton.IsInactive = false;
count++;
}
}
internal void UpdateDecadeMode()
{
DateTime selectedYear;
if (Owner != null)
{
Debug.Assert(Owner.SelectedYear != null, "The owning Calendar's selected year should not be null!");
selectedYear = Owner.SelectedYear;
_currentMonth = (DateTime)Owner.SelectedMonth;
}
else
{
_currentMonth = DateTime.Today;
selectedYear = DateTime.Today;
}
if (_currentMonth != null)
{
int decade = DateTimeHelper.DecadeOfDate(selectedYear);
int decadeEnd = DateTimeHelper.EndOfDecade(selectedYear);
SetDecadeModeHeaderButton(decade, decadeEnd);
SetDecadeModePreviousButton(decade);
SetDecadeModeNextButton(decadeEnd);
if (YearView != null)
{
SetYearButtons(decade, decadeEnd);
}
}
}
internal void UpdateYearViewSelection(CalendarButton calendarButton)
{
if (Owner != null && calendarButton != null && calendarButton.DataContext != null)
{
Owner.FocusCalendarButton.IsCalendarButtonFocused = false;
Owner.FocusCalendarButton = calendarButton;
calendarButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
if (Owner.DisplayMode == CalendarMode.Year)
{
Owner.SelectedMonth = (DateTime)calendarButton.DataContext;
}
else
{
Owner.SelectedYear = (DateTime)calendarButton.DataContext;
}
}
}
private void SetYearButtons(int decade, int decadeEnd)
{
int year;
int count = -1;
foreach (object child in YearView.Children)
{
CalendarButton childButton = child as CalendarButton;
Contract.Requires<ArgumentNullException>(childButton != null);
year = decade + count;
if (year <= DateTime.MaxValue.Year && year >= DateTime.MinValue.Year)
{
// There should be no time component. Time is 12:00 AM
DateTime day = new DateTime(year, 1, 1);
childButton.DataContext = day;
childButton.Content = year.ToString(DateTimeHelper.GetCurrentDateFormat());
childButton.IsVisible = true;
if (Owner != null)
{
if (year == Owner.SelectedYear.Year)
{
Owner.FocusCalendarButton = childButton;
childButton.IsCalendarButtonFocused = Owner.HasFocusInternal;
}
else
{
childButton.IsCalendarButtonFocused = false;
}
childButton.IsSelected = (Owner.DisplayDate.Year == year);
if (year < Owner.DisplayDateRangeStart.Year || year > Owner.DisplayDateRangeEnd.Year)
{
childButton.IsEnabled = false;
childButton.Opacity = 0;
}
else
{
childButton.IsEnabled = true;
childButton.Opacity = 1;
}
}
// SET IF THE YEAR IS INACTIVE OR NOT: set if the year is a
// trailing year or not
childButton.IsInactive = (year < decade || year > decadeEnd);
}
else
{
childButton.IsEnabled = false;
childButton.Opacity = 0;
}
count++;
}
}
private void SetDecadeModeHeaderButton(int decade, int decadeEnd)
{
if (HeaderButton != null)
{
HeaderButton.Content = decade.ToString(CultureInfo.CurrentCulture) + "-" + decadeEnd.ToString(CultureInfo.CurrentCulture);
HeaderButton.IsEnabled = false;
}
}
private void SetDecadeModeNextButton(int decadeEnd)
{
if (Owner != null && NextButton != null)
{
NextButton.IsEnabled = (Owner.DisplayDateRangeEnd.Year > decadeEnd);
}
}
private void SetDecadeModePreviousButton(int decade)
{
if (Owner != null && PreviousButton != null)
{
PreviousButton.IsEnabled = (decade > Owner.DisplayDateRangeStart.Year);
}
}
internal void HeaderButton_Click(object sender, RoutedEventArgs e)
{
if (Owner != null)
{
if (!Owner.HasFocusInternal)
{
Owner.Focus();
}
Button b = (Button)sender;
DateTime d;
if (b.IsEnabled)
{
if (Owner.DisplayMode == CalendarMode.Month)
{
if (Owner.DisplayDate != null)
{
d = Owner.DisplayDateInternal;
Owner.SelectedMonth = new DateTime(d.Year, d.Month, 1);
}
Owner.DisplayMode = CalendarMode.Year;
}
else
{
Debug.Assert(Owner.DisplayMode == CalendarMode.Year, "The Owner Calendar's DisplayMode should be Year!");
if (Owner.SelectedMonth != null)
{
d = Owner.SelectedMonth;
Owner.SelectedYear = new DateTime(d.Year, d.Month, 1);
}
Owner.DisplayMode = CalendarMode.Decade;
}
}
}
}
internal void PreviousButton_Click(object sender, RoutedEventArgs e)
{
if (Owner != null)
{
if (!Owner.HasFocusInternal)
{
Owner.Focus();
}
Button b = (Button)sender;
if (b.IsEnabled)
{
Owner.OnPreviousClick();
}
}
}
internal void NextButton_Click(object sender, RoutedEventArgs e)
{
if (Owner != null)
{
if (!Owner.HasFocusInternal)
{
Owner.Focus();
}
Button b = (Button)sender;
if (b.IsEnabled)
{
Owner.OnNextClick();
}
}
}
internal void Cell_MouseEnter(object sender, PointerEventArgs e)
{
if (Owner != null)
{
if (_isMouseLeftButtonDown && sender is CalendarDayButton b && b.IsEnabled && !b.IsBlackout)
{
// Update the states of all buttons to be selected starting
// from HoverStart to b
switch (Owner.SelectionMode)
{
case CalendarSelectionMode.SingleDate:
{
DateTime selectedDate = (DateTime)b.DataContext;
Owner.DatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0)
{
Owner.SelectedDates.Add(selectedDate);
}
else
{
Owner.SelectedDates[0] = selectedDate;
}
return;
}
case CalendarSelectionMode.SingleRange:
case CalendarSelectionMode.MultipleRange:
{
Debug.Assert(b.DataContext != null, "The DataContext should not be null!");
Owner.UnHighlightDays();
Owner.HoverEndIndex = b.Index;
Owner.HoverEnd = (DateTime?)b.DataContext;
// Update the States of the buttons
Owner.HighlightDays();
return;
}
}
}
}
}
internal void Cell_MouseLeave(object sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDown)
{
CalendarDayButton b = (CalendarDayButton)sender;
// The button is in Pressed state. Change the state to normal.
if (e.Device.Captured == b)
e.Device.Capture(null);
// null check is added for unit tests
if (_downEventArg != null)
{
var arg =
new PointerReleasedEventArgs()
{
Device = _downEventArg.Device,
MouseButton = _downEventArg.MouseButton,
Handled = _downEventArg.Handled,
InputModifiers = _downEventArg.InputModifiers,
Route = _downEventArg.Route,
Source = _downEventArg.Source
};
b.SendMouseLeftButtonUp(arg);
}
_lastCalendarDayButton = b;
}
}
internal void Cell_MouseLeftButtonDown(object sender, PointerPressedEventArgs e)
{
if (Owner != null)
{
if (!Owner.HasFocusInternal)
{
Owner.Focus();
}
bool ctrl, shift;
CalendarExtensions.GetMetaKeyState(e.InputModifiers, out ctrl, out shift);
CalendarDayButton b = sender as CalendarDayButton;
if (b != null)
{
_isControlPressed = ctrl;
if (b.IsEnabled && !b.IsBlackout)
{
DateTime selectedDate = (DateTime)b.DataContext;
Contract.Requires<ArgumentNullException>(selectedDate != null);
_isMouseLeftButtonDown = true;
// null check is added for unit tests
if (e != null)
{
_downEventArg = e;
}
switch (Owner.SelectionMode)
{
case CalendarSelectionMode.None:
{
return;
}
case CalendarSelectionMode.SingleDate:
{
Owner.DatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0)
{
Owner.SelectedDates.Add(selectedDate);
}
else
{
Owner.SelectedDates[0] = selectedDate;
}
return;
}
case CalendarSelectionMode.SingleRange:
{
// Set the start or end of the selection
// range
if (shift)
{
Owner.UnHighlightDays();
Owner.HoverEnd = selectedDate;
Owner.HoverEndIndex = b.Index;
Owner.HighlightDays();
}
else
{
Owner.UnHighlightDays();
Owner.HoverStart = selectedDate;
Owner.HoverStartIndex = b.Index;
}
return;
}
case CalendarSelectionMode.MultipleRange:
{
if (shift)
{
if (!ctrl)
{
// clear the list, set the states to
// default
foreach (DateTime item in Owner.SelectedDates)
{
Owner.RemovedItems.Add(item);
}
Owner.SelectedDates.ClearInternal();
}
Owner.HoverEnd = selectedDate;
Owner.HoverEndIndex = b.Index;
Owner.HighlightDays();
}
else
{
if (!ctrl)
{
// clear the list, set the states to
// default
foreach (DateTime item in Owner.SelectedDates)
{
Owner.RemovedItems.Add(item);
}
Owner.SelectedDates.ClearInternal();
Owner.UnHighlightDays();
}
Owner.HoverStart = selectedDate;
Owner.HoverStartIndex = b.Index;
}
return;
}
}
}
else
{
// If a click occurs on a BlackOutDay we set the
// HoverStart to be null
Owner.HoverStart = null;
}
}
else
{
_isControlPressed = false;
}
}
}
private void AddSelection(CalendarDayButton b)
{
if (Owner != null)
{
Owner.HoverEndIndex = b.Index;
Owner.HoverEnd = (DateTime)b.DataContext;
if (Owner.HoverEnd != null && Owner.HoverStart != null)
{
// this is selection with Mouse, we do not guarantee the
// range does not include BlackOutDates. AddRange method
// will throw away the BlackOutDates based on the
// SelectionMode
Owner.IsMouseSelection = true;
Owner.SelectedDates.AddRange(Owner.HoverStart.Value, Owner.HoverEnd.Value);
Owner.OnDayClick((DateTime)b.DataContext);
}
}
}
internal void Cell_MouseLeftButtonUp(object sender, PointerReleasedEventArgs e)
{
if (Owner != null)
{
CalendarDayButton b = sender as CalendarDayButton;
if (b != null && !b.IsBlackout)
{
Owner.OnDayButtonMouseUp(e);
}
_isMouseLeftButtonDown = false;
if (b != null && b.DataContext != null)
{
if (Owner.SelectionMode == CalendarSelectionMode.None || Owner.SelectionMode == CalendarSelectionMode.SingleDate)
{
Owner.OnDayClick((DateTime)b.DataContext);
return;
}
if (Owner.HoverStart.HasValue)
{
switch (Owner.SelectionMode)
{
case CalendarSelectionMode.SingleRange:
{
// Update SelectedDates
foreach (DateTime item in Owner.SelectedDates)
{
Owner.RemovedItems.Add(item);
}
Owner.SelectedDates.ClearInternal();
AddSelection(b);
return;
}
case CalendarSelectionMode.MultipleRange:
{
// add the selection (either single day or
// SingleRange day)
AddSelection(b);
return;
}
}
}
else
{
// If the day is Disabled but a trailing day we should
// be able to switch months
if (b.IsInactive && b.IsBlackout)
{
Owner.OnDayClick((DateTime)b.DataContext);
}
}
}
}
}
private void Cell_Click(object sender, RoutedEventArgs e)
{
if (Owner != null)
{
if (_isControlPressed && Owner.SelectionMode == CalendarSelectionMode.MultipleRange)
{
CalendarDayButton b = sender as CalendarDayButton;
Contract.Requires<ArgumentNullException>(b != null);
if (b.IsSelected)
{
Owner.HoverStart = null;
_isMouseLeftButtonDown = false;
b.IsSelected = false;
if (b.DataContext != null)
{
Owner.SelectedDates.Remove((DateTime)b.DataContext);
}
}
}
}
_isControlPressed = false;
}
private void Month_CalendarButtonMouseDown(object sender, PointerPressedEventArgs e)
{
CalendarButton b = sender as CalendarButton;
Contract.Requires<ArgumentNullException>(b != null);
_isMouseLeftButtonDownYearView = true;
if (e != null)
{
_downEventArgYearView = e;
}
UpdateYearViewSelection(b);
}
internal void Month_CalendarButtonMouseUp(object sender, PointerReleasedEventArgs e)
{
_isMouseLeftButtonDownYearView = false;
if (Owner != null)
{
DateTime newmonth = (DateTime)((CalendarButton)sender).DataContext;
if (Owner.DisplayMode == CalendarMode.Year)
{
Owner.DisplayDate = newmonth;
Owner.DisplayMode = CalendarMode.Month;
}
else
{
Debug.Assert(Owner.DisplayMode == CalendarMode.Decade, "The owning Calendar should be in decade mode!");
Owner.SelectedMonth = newmonth;
Owner.DisplayMode = CalendarMode.Year;
}
}
}
private void Month_MouseEnter(object sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDownYearView)
{
CalendarButton b = sender as CalendarButton;
Contract.Requires<ArgumentNullException>(b != null);
UpdateYearViewSelection(b);
}
}
private void Month_MouseLeave(object sender, PointerEventArgs e)
{
if (_isMouseLeftButtonDownYearView)
{
CalendarButton b = (CalendarButton)sender;
// The button is in Pressed state. Change the state to normal.
if (e.Device.Captured == b)
e.Device.Capture(null);
//b.ReleaseMouseCapture();
if (_downEventArgYearView != null)
{
var args =
new PointerReleasedEventArgs()
{
Device = _downEventArgYearView.Device,
MouseButton = _downEventArgYearView.MouseButton,
Handled = _downEventArgYearView.Handled,
InputModifiers = _downEventArgYearView.InputModifiers,
Route = _downEventArgYearView.Route,
Source = _downEventArgYearView.Source
};
b.SendMouseLeftButtonUp(args);
}
_lastCalendarButton = b;
}
}
private void MonthView_MouseLeave(object sender, PointerEventArgs e)
{
if (_lastCalendarDayButton != null)
{
e.Device.Capture(_lastCalendarDayButton);
}
}
private void YearView_MouseLeave(object sender, PointerEventArgs e)
{
if (_lastCalendarButton != null)
{
e.Device.Capture(_lastCalendarButton);
}
}
internal void UpdateDisabled(bool isEnabled)
{
PseudoClasses.Set(":calendardisabled", !isEnabled);
}
}
}