DotNet 4.8.1 build of DotNetBar
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
public class AppointmentCollection : Collection<Appointment>
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentCollection class.
|
||||
/// </summary>
|
||||
/// <param name="calendar"></param>
|
||||
public AppointmentCollection(CalendarModel calendar)
|
||||
{
|
||||
_Calendar = calendar;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar collection is associated with.
|
||||
/// </summary>
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set { _Calendar = value; }
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, Appointment item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
OnAfterInsert(index, item);
|
||||
}
|
||||
|
||||
private void OnAfterInsert(int index, Appointment item)
|
||||
{
|
||||
_Calendar.InternalAppointmentAdded(item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, Appointment item)
|
||||
{
|
||||
item.Calendar = _Calendar;
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, Appointment item)
|
||||
{
|
||||
Appointment app = this[index];
|
||||
OnBeforeSetItem(index, app, item);
|
||||
base.SetItem(index, item);
|
||||
OnAfterSetItem(index, app, item);
|
||||
}
|
||||
|
||||
private void OnAfterSetItem(int index, Appointment oldItem, Appointment newItem)
|
||||
{
|
||||
_Calendar.InternalAppointmentRemoved(oldItem, false);
|
||||
_Calendar.InternalAppointmentAdded(newItem);
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, Appointment oldItem, Appointment newItem)
|
||||
{
|
||||
oldItem.Calendar = null;
|
||||
newItem.Calendar = _Calendar;
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
Appointment app = this[index];
|
||||
OnBeforeRemove(index);
|
||||
base.RemoveItem(index);
|
||||
OnAfterRemove(index, app);
|
||||
}
|
||||
|
||||
private void OnAfterRemove(int index, Appointment app)
|
||||
{
|
||||
_Calendar.InternalAppointmentRemoved(app, false);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index)
|
||||
{
|
||||
this[index].Calendar = null;
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (Appointment item in this)
|
||||
{
|
||||
item.Calendar = null;
|
||||
_Calendar.InternalAppointmentRemoved(item, true);
|
||||
}
|
||||
base.ClearItems();
|
||||
_Calendar.InternalAppointmentsCleared();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,378 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using DevComponents.Schedule.Model.Primitives;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents appointment recurrence definition.
|
||||
/// </summary>
|
||||
public class AppointmentRecurrence : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
public AppointmentRecurrence()
|
||||
{
|
||||
_SkippedRecurrences = new CustomCollection<DateTime>();
|
||||
_SkippedRecurrences.CollectionChanged += new NotifyCollectionChangedEventHandler(SkippedRecurrencesCollectionChanged);
|
||||
}
|
||||
|
||||
private eRecurrenceRangeLimitType _RangeType = eRecurrenceRangeLimitType.NoEndDate;
|
||||
/// <summary>
|
||||
/// Gets or sets the range type for the recurrence. Default value is no end date for recurrence.
|
||||
/// </summary>
|
||||
[DefaultValue(eRecurrenceRangeLimitType.NoEndDate)]
|
||||
public eRecurrenceRangeLimitType RangeLimitType
|
||||
{
|
||||
get { return _RangeType; }
|
||||
set
|
||||
{
|
||||
if (_RangeType != value)
|
||||
{
|
||||
eRecurrenceRangeLimitType oldValue = _RangeType;
|
||||
_RangeType = value;
|
||||
OnRangeTypeChanged(oldValue, _RangeType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRangeTypeChanged(eRecurrenceRangeLimitType oldValue, eRecurrenceRangeLimitType newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RangeLimitType"));
|
||||
}
|
||||
|
||||
private DateTime _RangeEndDate = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets the recurrence end date. To specify the end date for recurrence set this property and RangeLimitType property to RangeEndDate.
|
||||
/// </summary>
|
||||
public DateTime RangeEndDate
|
||||
{
|
||||
get { return _RangeEndDate; }
|
||||
set
|
||||
{
|
||||
if (_RangeEndDate != value)
|
||||
{
|
||||
DateTime oldValue = _RangeEndDate;
|
||||
_RangeEndDate = value;
|
||||
OnRangeEndDateChanged(oldValue, _RangeEndDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRangeEndDateChanged(DateTime oldValue, DateTime newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RangeEndDate"));
|
||||
}
|
||||
|
||||
private int _RangeNumberOfOccurrences = 0;
|
||||
/// <summary>
|
||||
/// Gets or sets number of occurrences after which recurrence ends. To specify limited number of recurrences
|
||||
/// set this property and set RangeLimitType to RangeNumberOfOccurrences.
|
||||
/// </summary>
|
||||
[DefaultValue(0)]
|
||||
public int RangeNumberOfOccurrences
|
||||
{
|
||||
get { return _RangeNumberOfOccurrences; }
|
||||
set
|
||||
{
|
||||
if (_RangeNumberOfOccurrences != value)
|
||||
{
|
||||
if (value < 0) throw new ArgumentException("Negative values not allows for range limit.");
|
||||
|
||||
int oldValue = _RangeNumberOfOccurrences;
|
||||
_RangeNumberOfOccurrences = value;
|
||||
OnRangeNumberOfOccurrencesChanged(oldValue, _RangeNumberOfOccurrences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRangeNumberOfOccurrencesChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RangeNumberOfOccurrences"));
|
||||
}
|
||||
|
||||
private object _Tag = null;
|
||||
/// <summary>
|
||||
/// Gets or sets additional data associated with the object.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public object Tag
|
||||
{
|
||||
get { return _Tag; }
|
||||
set
|
||||
{
|
||||
if (value != _Tag)
|
||||
{
|
||||
object oldValue = _Tag;
|
||||
_Tag = value;
|
||||
OnTagChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTagChanged(object oldValue, object newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Tag"));
|
||||
|
||||
}
|
||||
|
||||
private eRecurrencePatternType _RecurrenceType = eRecurrencePatternType.Daily;
|
||||
/// <summary>
|
||||
/// Gets or sets the recurring frequency for appointment i.e. daily, weekly, monthly or yearly.
|
||||
/// Default value is Daily.
|
||||
/// </summary>
|
||||
[DefaultValue(eRecurrencePatternType.Daily)]
|
||||
public eRecurrencePatternType RecurrenceType
|
||||
{
|
||||
get { return _RecurrenceType; }
|
||||
set
|
||||
{
|
||||
if (value != _RecurrenceType)
|
||||
{
|
||||
eRecurrencePatternType oldValue = _RecurrenceType;
|
||||
_RecurrenceType = value;
|
||||
OnRecurrenceTypeChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRecurrenceTypeChanged(eRecurrencePatternType oldValue, eRecurrencePatternType newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RecurrenceType"));
|
||||
}
|
||||
|
||||
internal void GenerateSubset(AppointmentSubsetCollection subsetCollection, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
IRecurrenceGenerator generator = GetRecurrenceGenerator();
|
||||
|
||||
if (_RecurrenceType == eRecurrencePatternType.Daily)
|
||||
{
|
||||
generator.GenerateDailyRecurrence(subsetCollection, this, startDate, endDate);
|
||||
}
|
||||
else if (_RecurrenceType == eRecurrencePatternType.Weekly)
|
||||
{
|
||||
generator.GenerateWeeklyRecurrence(subsetCollection, this, startDate, endDate);
|
||||
}
|
||||
else if (_RecurrenceType == eRecurrencePatternType.Monthly)
|
||||
{
|
||||
generator.GenerateMonthlyRecurrence(subsetCollection, this, startDate, endDate);
|
||||
}
|
||||
else if (_RecurrenceType == eRecurrencePatternType.Yearly)
|
||||
{
|
||||
generator.GenerateYearlyRecurrence(subsetCollection, this, startDate, endDate);
|
||||
}
|
||||
}
|
||||
|
||||
private IRecurrenceGenerator _Generator = null;
|
||||
private IRecurrenceGenerator GetRecurrenceGenerator()
|
||||
{
|
||||
if (_Generator == null) _Generator = new RecurrenceGenerator();
|
||||
return _Generator;
|
||||
}
|
||||
|
||||
private Appointment _Appointment;
|
||||
/// <summary>
|
||||
/// Gets reference to appointment recurrence is assigned to.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Appointment Appointment
|
||||
{
|
||||
get { return _Appointment; }
|
||||
internal set { _Appointment = value; }
|
||||
}
|
||||
|
||||
private DailyRecurrenceSettings _Daily;
|
||||
/// <summary>
|
||||
/// Gets the settings for Daily recurrence type.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public DailyRecurrenceSettings Daily
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Daily == null)
|
||||
{
|
||||
_Daily = new DailyRecurrenceSettings(this);
|
||||
_Daily.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
}
|
||||
return _Daily;
|
||||
}
|
||||
}
|
||||
|
||||
private WeeklyRecurrenceSettings _Weekly;
|
||||
/// <summary>
|
||||
/// Gets the settings for Weekly recurrence type.
|
||||
/// </summary>
|
||||
public WeeklyRecurrenceSettings Weekly
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Weekly == null)
|
||||
{
|
||||
_Weekly = new WeeklyRecurrenceSettings(this);
|
||||
_Weekly.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
}
|
||||
return _Weekly;
|
||||
}
|
||||
}
|
||||
|
||||
private MonthlyRecurrenceSettings _Monthly;
|
||||
/// <summary>
|
||||
/// Gets the settings for monthly recurrence type.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public MonthlyRecurrenceSettings Monthly
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Monthly == null)
|
||||
{
|
||||
_Monthly = new MonthlyRecurrenceSettings(this);
|
||||
_Monthly.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
}
|
||||
return _Monthly;
|
||||
}
|
||||
}
|
||||
|
||||
private YearlyRecurrenceSettings _Yearly = null;
|
||||
/// <summary>
|
||||
/// Gets the settings for yearly recurrence type.
|
||||
/// </summary>
|
||||
public YearlyRecurrenceSettings Yearly
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Yearly == null)
|
||||
{
|
||||
_Yearly = new YearlyRecurrenceSettings(this);
|
||||
_Yearly.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
}
|
||||
return _Yearly;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _RecurrenceStartDate = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets the recurrence start date. Default value is DateTime.MinValue which indicates that recurrence starts after
|
||||
/// the appointment ends.
|
||||
/// </summary>
|
||||
public DateTime RecurrenceStartDate
|
||||
{
|
||||
get { return _RecurrenceStartDate; }
|
||||
set
|
||||
{
|
||||
if (value != _RecurrenceStartDate)
|
||||
{
|
||||
DateTime oldValue = _RecurrenceStartDate;
|
||||
_RecurrenceStartDate = value;
|
||||
OnRecurrenceStartDateChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRecurrenceStartDateChanged(DateTime oldValue, DateTime newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RecurrenceStartDate"));
|
||||
}
|
||||
|
||||
private SubPropertyChangedEventHandler _ChildPropertyChangedEventHandler = null;
|
||||
private SubPropertyChangedEventHandler ChildPropertyChangedEventHandler
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ChildPropertyChangedEventHandler == null) _ChildPropertyChangedEventHandler = new SubPropertyChangedEventHandler(ChildPropertyChanged);
|
||||
return _ChildPropertyChangedEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChildPropertyChanged(object sender, SubPropertyChangedEventArgs e)
|
||||
{
|
||||
OnSubPropertyChanged(e);
|
||||
}
|
||||
|
||||
private CustomCollection<DateTime> _SkippedRecurrences = null;
|
||||
/// <summary>
|
||||
/// Gets or set the list of dates on which the recurrences are skipped.
|
||||
/// </summary>
|
||||
public CustomCollection<DateTime> SkippedRecurrences
|
||||
{
|
||||
get
|
||||
{
|
||||
return _SkippedRecurrences;
|
||||
}
|
||||
}
|
||||
private void SkippedRecurrencesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("SkippedRecurrences"));
|
||||
}
|
||||
|
||||
private bool _IndependentVisibility = false;
|
||||
/// <summary>
|
||||
/// Gets or sets whether generated recurring appointments have independent Visible property setting from root Appointment.
|
||||
/// When set to true recurring appointment instances will not by default have Visible property set to the Visible property of root appointment.
|
||||
/// Default value is false which means recurring instances by default have Visible property set to the root appointment Visible property.
|
||||
/// </summary>
|
||||
public bool IndependentVisibility
|
||||
{
|
||||
get { return _IndependentVisibility; }
|
||||
set
|
||||
{
|
||||
if (value != _IndependentVisibility)
|
||||
{
|
||||
bool oldValue = _IndependentVisibility;
|
||||
_IndependentVisibility = value;
|
||||
OnIndependentVisibilityChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when IndependentVisibility property has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old property value</param>
|
||||
/// <param name="newValue">New property value</param>
|
||||
protected virtual void OnIndependentVisibilityChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IndependentVisibility"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,232 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using DevComponents.Schedule.Model.Primitives;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents subset of appointments collection.
|
||||
/// </summary>
|
||||
public class AppointmentSubsetCollection : CustomCollection<Appointment>
|
||||
{
|
||||
#region Private Variables
|
||||
private DateTime _StartDate = DateTime.MinValue;
|
||||
private DateTime _EndDate = DateTime.MinValue;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentSubsetCollection class with appointments between given start and end date.
|
||||
/// </summary>
|
||||
/// <param name="calendar"></param>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="end"></param>
|
||||
public AppointmentSubsetCollection(CalendarModel calendar, DateTime start, DateTime end)
|
||||
{
|
||||
_Calendar = calendar;
|
||||
_StartDate = start;
|
||||
_EndDate = end;
|
||||
PopulateCollection();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar collection is associated with this collection.
|
||||
/// </summary>
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set { _Calendar = value; }
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, Appointment item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, Appointment item)
|
||||
{
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
item.Calendar = _Calendar;
|
||||
if (item.IsRecurringInstance && _Calendar != null)
|
||||
_Calendar.InternalAppointmentAdded(item);
|
||||
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, Appointment item)
|
||||
{
|
||||
OnBeforeSetItem(index, item);
|
||||
base.SetItem(index, item);
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, Appointment item)
|
||||
{
|
||||
Appointment app = this[index];
|
||||
app.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
app.Calendar = null;
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
app.Calendar = _Calendar;
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
OnBeforeRemove(index);
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index)
|
||||
{
|
||||
Appointment item = this[index];
|
||||
OnAppointmentRemoved(item);
|
||||
}
|
||||
|
||||
private void OnAppointmentRemoved(Appointment item)
|
||||
{
|
||||
item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
if (item.IsRecurringInstance && _Calendar != null)
|
||||
_Calendar.InternalAppointmentRemoved(item, false);
|
||||
if (item.IsRecurringInstance)
|
||||
item.Calendar = null;
|
||||
}
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (Appointment item in this.GetItemsDirect())
|
||||
{
|
||||
OnAppointmentRemoved(item);
|
||||
}
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
private void PopulateCollection()
|
||||
{
|
||||
this.Clear();
|
||||
|
||||
foreach (Appointment app in _Calendar.Appointments)
|
||||
{
|
||||
if (app.LocalStartTime >= _StartDate && app.LocalStartTime <= _EndDate || app.LocalEndTime > _StartDate && (app.LocalEndTime <= _EndDate || app.LocalStartTime < _StartDate))
|
||||
{
|
||||
this.Add(app);
|
||||
}
|
||||
if (app.Recurrence != null && IsRecurrenceInRange(app, _StartDate, _EndDate))
|
||||
{
|
||||
int count = this.GetItemsDirect().Count;
|
||||
app.Recurrence.GenerateSubset(this, _StartDate, _EndDate);
|
||||
if (count == this.GetItemsDirect().Count && TotalDaysDuration(app.StartTime, app.EndTime) >= 1 &&
|
||||
app.LocalEndTime < _StartDate)
|
||||
{
|
||||
// Nothing generated lets wind back and look to see is there an recurrence that needs to be captured in this view
|
||||
int daysLookBack = TotalDaysDuration(app.StartTime, app.EndTime);
|
||||
DateTime start = _StartDate;
|
||||
for (int i = 0; i < daysLookBack; i++)
|
||||
{
|
||||
start = start.AddDays(-1);
|
||||
AppointmentSubsetCollection dayCollection = _Calendar.GetDay(start).Appointments;
|
||||
foreach (Appointment subAppointment in dayCollection)
|
||||
{
|
||||
if (subAppointment.IsRecurringInstance && subAppointment.RootAppointment == app &&
|
||||
(subAppointment.LocalStartTime >= _StartDate && subAppointment.LocalStartTime <= _EndDate ||
|
||||
subAppointment.LocalEndTime > _StartDate && (subAppointment.LocalEndTime <= _EndDate ||
|
||||
subAppointment.LocalStartTime < _StartDate)))
|
||||
{
|
||||
this.Add(subAppointment);
|
||||
daysLookBack = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_IsCollectionUpToDate = true;
|
||||
}
|
||||
|
||||
private int TotalDaysDuration(DateTime startTime, DateTime endTime)
|
||||
{
|
||||
if (endTime.Hour == 0 && endTime.Minute == 0 && endTime.Second == 0)
|
||||
endTime = endTime.AddMinutes(-1);
|
||||
return (int)Math.Max(0, Math.Ceiling(endTime.Date.Subtract(startTime.Date).TotalDays));
|
||||
}
|
||||
|
||||
private bool IsRecurrenceInRange(Appointment app, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
if (app.Recurrence.RecurrenceType == eRecurrencePatternType.Yearly)
|
||||
{
|
||||
// Simple range check on number of occurrences
|
||||
if (app.Recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && app.LocalEndTime.Subtract(endDate).TotalDays / 365 > app.Recurrence.RangeNumberOfOccurrences)
|
||||
return false;
|
||||
// Date check based on next expected recurrence date
|
||||
if (app.Recurrence.Yearly.RepeatInterval > 1)
|
||||
{
|
||||
DateTime nextRecurrence = DateTimeHelper.MaxDate(app.Recurrence.RecurrenceStartDate, app.EndTime.AddDays(1).Date);
|
||||
while (nextRecurrence < startDate)
|
||||
nextRecurrence = RecurrenceGenerator.GetNextYearlyRecurrence(app.Recurrence, nextRecurrence);
|
||||
if (nextRecurrence > endDate || nextRecurrence.Add(endDate.Subtract(startDate)) < startDate)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
DateTime nextRecurrence = RecurrenceGenerator.GetNextYearlyRecurrence(app.Recurrence, startDate.Date.AddDays(-startDate.DayOfYear));
|
||||
if (nextRecurrence > endDate || nextRecurrence.Add(endDate.Subtract(startDate)) < startDate)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (app.Recurrence.RangeEndDate == DateTime.MinValue || app.Recurrence.RangeEndDate >= _EndDate || app.Recurrence.RangeEndDate < _EndDate && app.Recurrence.RangeEndDate > _StartDate)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private SubPropertyChangedEventHandler _ChildPropertyChangedEventHandler = null;
|
||||
private SubPropertyChangedEventHandler ChildPropertyChangedEventHandler
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ChildPropertyChangedEventHandler == null) _ChildPropertyChangedEventHandler = new SubPropertyChangedEventHandler(ChildPropertyChanged);
|
||||
return _ChildPropertyChangedEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChildPropertyChanged(object sender, SubPropertyChangedEventArgs e)
|
||||
{
|
||||
InvalidateCollection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates collection content due to the change to appointments or some other condition. Invalidating collection
|
||||
/// content causes the collection elements to be re-generated on next collection read access.
|
||||
/// </summary>
|
||||
public virtual void InvalidateCollection()
|
||||
{
|
||||
_IsCollectionUpToDate = false;
|
||||
}
|
||||
|
||||
protected override void OnCollectionReadAccess()
|
||||
{
|
||||
if (!_IsCollectionUpToDate) PopulateCollection();
|
||||
base.OnCollectionReadAccess();
|
||||
}
|
||||
|
||||
private bool _IsCollectionUpToDate = false;
|
||||
internal bool IsCollectionUpToDate
|
||||
{
|
||||
get { return _IsCollectionUpToDate; }
|
||||
set
|
||||
{
|
||||
_IsCollectionUpToDate = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a working day.
|
||||
/// </summary>
|
||||
public abstract class BaseWorkDay : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
protected WorkTime _WorkStartTime = new WorkTime(8, 0);
|
||||
/// <summary>
|
||||
/// Gets or sets the work start time.
|
||||
/// </summary>
|
||||
public WorkTime WorkStartTime
|
||||
{
|
||||
get { return _WorkStartTime; }
|
||||
set
|
||||
{
|
||||
WorkTime oldValue = _WorkStartTime;
|
||||
_WorkStartTime = value;
|
||||
OnWorkStartTimeChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when WorkStartTime has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old property value.</param>
|
||||
/// <param name="newValue">New property value.</param>
|
||||
protected virtual void OnWorkStartTimeChanged(WorkTime oldValue, WorkTime newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("WorkStartTime"));
|
||||
}
|
||||
|
||||
protected WorkTime _WorkEndTime = new WorkTime(17, 0);
|
||||
/// <summary>
|
||||
/// Gets or sets the work end time.
|
||||
/// </summary>
|
||||
public WorkTime WorkEndTime
|
||||
{
|
||||
get { return _WorkEndTime; }
|
||||
set
|
||||
{
|
||||
WorkTime oldValue = _WorkEndTime;
|
||||
_WorkEndTime = value;
|
||||
OnWorkEndTimeChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when WorkEndTime has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old property value.</param>
|
||||
/// <param name="newValue">New property value.</param>
|
||||
protected virtual void OnWorkEndTimeChanged(WorkTime oldValue, WorkTime newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("WorkEndTime"));
|
||||
|
||||
}
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar work day is associated with.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set
|
||||
{
|
||||
if (_Calendar != value)
|
||||
{
|
||||
_Calendar = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,595 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the calendar model control.
|
||||
/// </summary>
|
||||
public class CalendarModel : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Occurs when an appointment has been added to the model.
|
||||
/// </summary>
|
||||
public event AppointmentEventHandler AppointmentAdded;
|
||||
/// <summary>
|
||||
/// Occurs when an appointment has been removed from the model.
|
||||
/// </summary>
|
||||
public event AppointmentEventHandler AppointmentRemoved;
|
||||
/// <summary>
|
||||
/// Occurs when AppointmentStartTime has been reached. This event can be used to trigger appointment reminders. Note that event handler will be called on the thread of System.Timer which is different
|
||||
/// than UI thread. You should use BeginInvoke calls to marshal the calls to your UI thread.
|
||||
/// </summary>
|
||||
public event AppointmentEventHandler AppointmentStartTimeReached;
|
||||
/// <summary>
|
||||
/// Occurs when Reminder's ReminderTime has been reached. Note that event handler will be called on the thread of System.Timer which is different
|
||||
/// than UI thread. You should use BeginInvoke calls to marshal the calls to your UI thread.
|
||||
/// </summary>
|
||||
[Description("Occurs when Reminder's ReminderTime has been reached.")]
|
||||
public event ReminderEventHandler ReminderNotification;
|
||||
/// <summary>
|
||||
/// Occurs when Appointments collection has been cleared.
|
||||
/// </summary>
|
||||
public event EventHandler AppointmentsCleared;
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CalendarModel class.
|
||||
/// </summary>
|
||||
public CalendarModel()
|
||||
{
|
||||
_Appointments = new AppointmentCollection(this);
|
||||
_Owners = new OwnerCollection(this);
|
||||
_WorkDays = new WorkDayCollection(this);
|
||||
_CalendarWorkDays = new CalendarWorkDayCollection(this);
|
||||
// Initialize default work-days
|
||||
_WorkDays.Add(new WorkDay(DayOfWeek.Monday));
|
||||
_WorkDays.Add(new WorkDay(DayOfWeek.Tuesday));
|
||||
_WorkDays.Add(new WorkDay(DayOfWeek.Wednesday));
|
||||
_WorkDays.Add(new WorkDay(DayOfWeek.Thursday));
|
||||
_WorkDays.Add(new WorkDay(DayOfWeek.Friday));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
private AppointmentCollection _Appointments;
|
||||
/// <summary>
|
||||
/// Gets appointments associated with this calendar.
|
||||
/// </summary>
|
||||
public AppointmentCollection Appointments
|
||||
{
|
||||
get { return _Appointments; }
|
||||
}
|
||||
|
||||
private OwnerCollection _Owners;
|
||||
/// <summary>
|
||||
/// Gets owners of appointments associated with this calendar.
|
||||
/// </summary>
|
||||
public OwnerCollection Owners
|
||||
{
|
||||
get { return _Owners; }
|
||||
}
|
||||
|
||||
private WorkDayCollection _WorkDays;
|
||||
/// <summary>
|
||||
/// Gets working days associated with this calendar.
|
||||
/// </summary>
|
||||
public WorkDayCollection WorkDays
|
||||
{
|
||||
get { return _WorkDays; }
|
||||
}
|
||||
|
||||
private CalendarWorkDayCollection _CalendarWorkDays = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar/date based working days collection. This collection allows you to specify working time for specific dates. Values specified here take precedence over working hours set through WorkDays collection.
|
||||
/// </summary>
|
||||
public CalendarWorkDayCollection CalendarWorkDays
|
||||
{
|
||||
get { return _CalendarWorkDays; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets reference to the Day object which represents day in calendar.
|
||||
/// </summary>
|
||||
/// <param name="date">Date to retrieve day for.</param>
|
||||
/// <returns>Returns reference to Day object.</returns>
|
||||
public Day GetDay(DateTime date)
|
||||
{
|
||||
Year year = null;
|
||||
if(!_Years.TryGetValue(date.Year, out year))
|
||||
year = CreateYear(date.Year);
|
||||
|
||||
return year.Months[date.Month - 1].Days[date.Day - 1];
|
||||
//return new Day(date, this);
|
||||
}
|
||||
|
||||
private Year CreateYear(int y)
|
||||
{
|
||||
Year year = new Year(y, this);
|
||||
_Years.Add(y, year);
|
||||
return year;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if appointment overlapps with one or more of the appointments in the model.
|
||||
/// </summary>
|
||||
/// <param name="app">Appointment to check overlap for.</param>
|
||||
/// <returns>true if there are appointments overlapping appointment otherwise false.</returns>
|
||||
public bool ContainsOverlappingAppointments(Appointment app)
|
||||
{
|
||||
int duration = (int)Math.Max(1, app.EndTime.Subtract(app.StartTime).TotalDays);
|
||||
for (int i = 0; i < duration; i++)
|
||||
{
|
||||
DateTime date = app.StartTime.Date.AddDays(i);
|
||||
Day day = GetDay(date);
|
||||
foreach (Appointment item in day.Appointments)
|
||||
{
|
||||
if (item != app && DateTimeHelper.TimePeriodsOverlap(item.StartTime, item.EndTime, app.StartTime, app.EndTime))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds appointments that overlap with the parameter appointment.
|
||||
/// </summary>
|
||||
/// <param name="app">Appointment to use to find overlapps</param>
|
||||
/// <returns>Array of appointments that overlap parameter.</returns>
|
||||
public Appointment[] FindOverlappingAppointments(Appointment app)
|
||||
{
|
||||
List<Appointment> overlaps = new List<Appointment>();
|
||||
int duration = (int)Math.Max(1, app.EndTime.Subtract(app.StartTime).TotalDays);
|
||||
for (int i = 0; i < duration; i++)
|
||||
{
|
||||
DateTime date = app.StartTime.Date.AddDays(i);
|
||||
Day day = GetDay(date);
|
||||
foreach (Appointment item in day.Appointments)
|
||||
{
|
||||
if (item != app && DateTimeHelper.TimePeriodsOverlap(item.StartTime, item.EndTime, app.StartTime, app.EndTime))
|
||||
overlaps.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
return overlaps.ToArray();
|
||||
}
|
||||
|
||||
//public Month GetMonth(int year, int month)
|
||||
//{
|
||||
// return null;
|
||||
//}
|
||||
|
||||
//private HolidaysCollection _Holidays;
|
||||
///// <summary>
|
||||
///// Gets the collection of holidays associated with calendar.
|
||||
///// </summary>
|
||||
//public HolidaysCollection Holidays
|
||||
//{
|
||||
// get { return _Holidays; }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the calendar date time which has seconds part set to 0.
|
||||
/// </summary>
|
||||
/// <param name="dt"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime GetCalendarDateTime(DateTime dt)
|
||||
{
|
||||
if (_PreciseDateTimeValues)
|
||||
return dt;
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0);
|
||||
}
|
||||
|
||||
private static bool _PreciseDateTimeValues = false;
|
||||
/// <summary>
|
||||
/// Gets or sets whether Appointment StartTime and EndTime values retain seconds and milliseconds. When
|
||||
/// set to false seconds and milliseconds are discarded. When set to true the DateTime set to appointment
|
||||
/// StartTime and EndTime is used as is including seconds and milliseconds. Default value is false.
|
||||
/// </summary>
|
||||
public static bool PreciseDateTimeValues
|
||||
{
|
||||
get
|
||||
{
|
||||
return _PreciseDateTimeValues;
|
||||
}
|
||||
set
|
||||
{
|
||||
_PreciseDateTimeValues = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime CurrentDateTime
|
||||
{
|
||||
get
|
||||
{
|
||||
DateTime dt = DateTime.Now;
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
|
||||
internal System.Globalization.Calendar GetCalendar()
|
||||
{
|
||||
return CultureInfo.CurrentCulture.Calendar;
|
||||
}
|
||||
|
||||
private Dictionary<int, Year> _Years = new Dictionary<int, Year>();
|
||||
|
||||
internal void InternalAppointmentRemoved(Appointment item, bool isClearing)
|
||||
{
|
||||
item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
if (!item.IsRecurringInstance && !isClearing)
|
||||
InvalidateAppointmentCache(item);
|
||||
if (!isClearing)
|
||||
OnAppointmentRemoved(new AppointmentEventArgs(item));
|
||||
}
|
||||
|
||||
internal void InternalAppointmentsCleared()
|
||||
{
|
||||
InvalidateAppointmentCache();
|
||||
OnAppointmentsCleared(EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnAppointmentsCleared(EventArgs e)
|
||||
{
|
||||
EventHandler handler = AppointmentsCleared;
|
||||
if (handler != null) handler(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the AppointmentRemoved event.
|
||||
/// </summary>
|
||||
/// <param name="appointmentEventArgs">Event arguments</param>
|
||||
protected virtual void OnAppointmentRemoved(AppointmentEventArgs appointmentEventArgs)
|
||||
{
|
||||
AppointmentEventHandler handler = AppointmentRemoved;
|
||||
if (handler != null) handler(this, appointmentEventArgs);
|
||||
}
|
||||
|
||||
internal void InternalAppointmentAdded(Appointment item)
|
||||
{
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
if (!item.IsRecurringInstance)
|
||||
InvalidateAppointmentCache(item);
|
||||
OnAppointmentAdded(new AppointmentEventArgs(item));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the AppointmentAdded event.
|
||||
/// </summary>
|
||||
/// <param name="appointmentEventArgs">Event arguments</param>
|
||||
protected virtual void OnAppointmentAdded(AppointmentEventArgs appointmentEventArgs)
|
||||
{
|
||||
AppointmentEventHandler handler = AppointmentAdded;
|
||||
if (handler != null) handler(this, appointmentEventArgs);
|
||||
}
|
||||
|
||||
internal void OwnerRemoved(Owner item)
|
||||
{
|
||||
item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Owners"));
|
||||
}
|
||||
|
||||
internal void OwnerAdded(Owner item)
|
||||
{
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Owners"));
|
||||
}
|
||||
|
||||
internal void WorkDayRemoved(WorkDay item)
|
||||
{
|
||||
item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("WorkDays"));
|
||||
}
|
||||
|
||||
internal void WorkDayAdded(WorkDay item)
|
||||
{
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("WorkDays"));
|
||||
}
|
||||
|
||||
internal void CalendarWorkDateRemoved(CalendarWorkDay item)
|
||||
{
|
||||
item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("CalendarWorkDays"));
|
||||
}
|
||||
|
||||
internal void CalendarWorkDateAdded(CalendarWorkDay item)
|
||||
{
|
||||
item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("CalendarWorkDays"));
|
||||
}
|
||||
|
||||
private SubPropertyChangedEventHandler _ChildPropertyChangedEventHandler = null;
|
||||
private SubPropertyChangedEventHandler ChildPropertyChangedEventHandler
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ChildPropertyChangedEventHandler == null) _ChildPropertyChangedEventHandler = new SubPropertyChangedEventHandler(ChildPropertyChanged);
|
||||
return _ChildPropertyChangedEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
private void ChildPropertyChanged(object sender, SubPropertyChangedEventArgs e)
|
||||
{
|
||||
Appointment app = sender as Appointment;
|
||||
OnSubPropertyChanged(e);
|
||||
|
||||
if (app != null)
|
||||
{
|
||||
if (IsNonTimeProperty(e.PropertyChangedArgs.PropertyName)) return;
|
||||
if (app.InMoveTo && e.PropertyChangedArgs.PropertyName != "EndTime") return;
|
||||
if (IsRecurranceProperty(e.PropertyChangedArgs.PropertyName) ||
|
||||
e.Source is DailyRecurrenceSettings ||
|
||||
e.Source is WeeklyRecurrenceSettings ||
|
||||
e.Source is YearlyRecurrenceSettings ||
|
||||
e.Source is MonthlyRecurrenceSettings)
|
||||
InvalidateAppointmentCache();
|
||||
else
|
||||
InvalidateAppointmentCache(app);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsRecurranceProperty(string propertyName)
|
||||
{
|
||||
return propertyName == Appointment.RecurrencePropertyName;
|
||||
}
|
||||
|
||||
private void InvalidateAppointmentCache(Appointment app)
|
||||
{
|
||||
if (IsUpdateSuspended) return;
|
||||
if (app.Recurrence != null)
|
||||
{
|
||||
// Invalidate all
|
||||
InvalidateAppointmentCache();
|
||||
}
|
||||
else if (_Years.ContainsKey(app.LocalStartTime.Year))
|
||||
{
|
||||
DateTime d = DateTimeHelper.BeginningOfDay(app.LocalStartTime);
|
||||
DateTime end = app.LocalEndTime;
|
||||
int year = d.Year;
|
||||
while (d < end)
|
||||
{
|
||||
_Years[d.Year].InvalidateAppointments(d.Month, d.Day);
|
||||
d = d.AddDays(1);
|
||||
if (d.Year != year && !_Years.ContainsKey(d.Year))
|
||||
break;
|
||||
}
|
||||
}
|
||||
AccessToday();
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(AppointmentsPropertyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates appointments cache store and causes recurrences to be regenerated when requested.
|
||||
/// </summary>
|
||||
public void InvalidateAppointmentCache()
|
||||
{
|
||||
if (IsUpdateSuspended) return;
|
||||
|
||||
// Invalidate all
|
||||
foreach (Year year in _Years.Values)
|
||||
{
|
||||
year.InvalidateAppointments();
|
||||
}
|
||||
AccessToday();
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(AppointmentsPropertyName));
|
||||
}
|
||||
|
||||
private void AccessToday()
|
||||
{
|
||||
DateTime today = DateTime.Today;
|
||||
Day day = this.GetDay(today);
|
||||
int appCount = day.Appointments.Count;
|
||||
}
|
||||
private bool IsNonTimeProperty(string propertyName)
|
||||
{
|
||||
if (propertyName == "Description" || propertyName == "IsRecurringInstance" || propertyName == "Tag"
|
||||
|| propertyName == "OwnerKey" || propertyName == "IsSelected" || propertyName == "Locked" || propertyName=="LocalStartTime" || propertyName=="LocalEndTime")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int _UpdatesCount = 0;
|
||||
/// <summary>
|
||||
/// Suspends internal control updates to the cache structures etc. When making changes on multiple appointments
|
||||
/// time related properties or when adding multiple appointments before doing so call BeginUpdate and after
|
||||
/// updates are done call EndUpdate method to optimize performance.
|
||||
/// <remarks>Calls to BeginUpdate method can be nested and only last outer most EndUpdate call will resume internal control updates.</remarks>
|
||||
/// </summary>
|
||||
public void BeginUpdate()
|
||||
{
|
||||
_UpdatesCount++;
|
||||
}
|
||||
/// <summary>
|
||||
/// Resumes internal control updates that were suspended using BeginUpdate call and invalidates internal cache.
|
||||
/// </summary>
|
||||
public void EndUpdate()
|
||||
{
|
||||
if (_UpdatesCount == 0)
|
||||
throw new InvalidOperationException("EndUpdate must be called AFTER BeginUpdate");
|
||||
_UpdatesCount--;
|
||||
if (_UpdatesCount == 0)
|
||||
InvalidateAppointmentCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether internal control update is suspended due to the call to BeginUpdate method.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public bool IsUpdateSuspended
|
||||
{
|
||||
get
|
||||
{
|
||||
return _UpdatesCount > 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string AppointmentsPropertyName
|
||||
{
|
||||
get { return "Appointments"; }
|
||||
}
|
||||
internal static string WorkDaysPropertyName
|
||||
{
|
||||
get { return "WorkDays"; }
|
||||
}
|
||||
internal static string CalendarWorkDaysPropertyName
|
||||
{
|
||||
get { return "CalendarWorkDays"; }
|
||||
}
|
||||
|
||||
private TimeZoneInfo _DisplayTimeZone = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the default display time zone used for the appointments. Default value is null which indicates that system time-zone is used.
|
||||
/// Display Time zone can also be set for each Owner on Owner object. Value set here is used if specific display time-zone is not set on user.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public TimeZoneInfo DisplayTimeZone
|
||||
{
|
||||
get { return _DisplayTimeZone; }
|
||||
set
|
||||
{
|
||||
if (value != _DisplayTimeZone)
|
||||
{
|
||||
TimeZoneInfo oldValue = _DisplayTimeZone;
|
||||
_DisplayTimeZone = value;
|
||||
InvalidateAppointmentTimes();
|
||||
InvalidateAppointmentCache();
|
||||
OnDisplayTimeZoneChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InvalidateAppointmentTimes()
|
||||
{
|
||||
foreach (Appointment item in _Appointments)
|
||||
{
|
||||
item.InvokeLocalTimePropertyChange();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisplayTimeZoneChanged(TimeZoneInfo oldValue, TimeZoneInfo newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DisplayTimeZone"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal void InvokeAppointmentStartTimeReached(Appointment appointment)
|
||||
{
|
||||
OnAppointmentStartTimeReached(new AppointmentEventArgs(appointment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises AppointmentStartTimeReached event.
|
||||
/// </summary>
|
||||
/// <param name="appointmentEventArgs">Event arguments</param>
|
||||
protected virtual void OnAppointmentStartTimeReached(AppointmentEventArgs appointmentEventArgs)
|
||||
{
|
||||
AppointmentEventHandler handler = AppointmentStartTimeReached;
|
||||
if (handler != null)
|
||||
handler(this, appointmentEventArgs);
|
||||
}
|
||||
|
||||
internal void InvokeReminderNotification(Reminder reminder)
|
||||
{
|
||||
OnReminderNotification(new ReminderEventArgs(reminder));
|
||||
}
|
||||
/// <summary>
|
||||
/// Raises ReminderNotification event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnReminderNotification(ReminderEventArgs e)
|
||||
{
|
||||
ReminderEventHandler h = ReminderNotification;
|
||||
if (h != null)
|
||||
h(this, e);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
|
||||
if (eh != null)
|
||||
eh(this, e);
|
||||
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CustomReminders
|
||||
private ReminderCollection _CustomReminders = null;
|
||||
/// <summary>
|
||||
/// Gets the collection of custom reminders that are not associated with appointments.
|
||||
/// </summary>
|
||||
public ReminderCollection CustomReminders
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_CustomReminders == null) _CustomReminders = new ReminderCollection(this);
|
||||
return _CustomReminders;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Events Support
|
||||
public delegate void AppointmentEventHandler(object sender, AppointmentEventArgs e);
|
||||
/// <summary>
|
||||
/// Defines arguments for appointment related events.
|
||||
/// </summary>
|
||||
public class AppointmentEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the appointment referenced by this event.
|
||||
/// </summary>
|
||||
public Appointment Appointment;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentEventArgs class.
|
||||
/// </summary>
|
||||
/// <param name="appointment"></param>
|
||||
public AppointmentEventArgs(Appointment appointment)
|
||||
{
|
||||
Appointment = appointment;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents specific date based work day.
|
||||
/// </summary>
|
||||
public class CalendarWorkDay : BaseWorkDay
|
||||
{
|
||||
#region Internal Implementation
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDay class.
|
||||
/// </summary>
|
||||
public CalendarWorkDay()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDay class.
|
||||
/// </summary>
|
||||
/// <param name="date">Date this work-day represents</param>
|
||||
public CalendarWorkDay(DateTime date)
|
||||
{
|
||||
_Date = date;
|
||||
}
|
||||
|
||||
private DateTime _Date = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets the date this day represents.
|
||||
/// </summary>
|
||||
public DateTime Date
|
||||
{
|
||||
get { return _Date; }
|
||||
set
|
||||
{
|
||||
value = value.Date;
|
||||
if (value != _Date)
|
||||
{
|
||||
DateTime oldValue = _Date;
|
||||
_Date = value;
|
||||
OnDateChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when Date property has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old property value.</param>
|
||||
/// <param name="newValue">New property value.</param>
|
||||
protected virtual void OnDateChanged(DateTime oldValue, DateTime newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Date"));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents collection of calendar work days.
|
||||
/// </summary>
|
||||
public class CalendarWorkDayCollection : Collection<CalendarWorkDay>
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentCollection class.
|
||||
/// </summary>
|
||||
/// <param name="calendar"></param>
|
||||
public CalendarWorkDayCollection(CalendarModel calendar)
|
||||
{
|
||||
_Calendar = calendar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the CalendarWorkDayCollection class.
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
public CalendarWorkDayCollection(Owner owner)
|
||||
{
|
||||
_Owner = owner;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
private Dictionary<DateTime, CalendarWorkDay> _ItemsDictionary = new Dictionary<DateTime, CalendarWorkDay>();
|
||||
|
||||
private Owner _Owner = null;
|
||||
/// <summary>
|
||||
/// Gets the Owner of work-day collection.
|
||||
/// </summary>
|
||||
public Owner Owner
|
||||
{
|
||||
get { return _Owner; }
|
||||
internal set { _Owner = value; }
|
||||
}
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar collection is associated with.
|
||||
/// </summary>
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set
|
||||
{
|
||||
_Calendar = value;
|
||||
UpdateItemsCalendarModel();
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateItemsCalendarModel()
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
foreach (CalendarWorkDay item in this.Items)
|
||||
{
|
||||
item.Calendar = model;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
CalendarWorkDay day = this[index];
|
||||
OnBeforeRemove(index, day);
|
||||
base.RemoveItem(index);
|
||||
OnAfterRemove(index, day);
|
||||
}
|
||||
|
||||
private void OnAfterRemove(int index, CalendarWorkDay day)
|
||||
{
|
||||
_ItemsDictionary.Remove(day.Date);
|
||||
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
model.CalendarWorkDateRemoved(day);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index, CalendarWorkDay day)
|
||||
{
|
||||
day.Calendar = null;
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, CalendarWorkDay item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
OnAfterInsert(index, item);
|
||||
}
|
||||
|
||||
private void OnAfterInsert(int index, CalendarWorkDay item)
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
model.CalendarWorkDateAdded(item);
|
||||
_ItemsDictionary.Add(item.Date, item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, CalendarWorkDay item)
|
||||
{
|
||||
if (this[item.Date] != null)
|
||||
throw new InvalidOperationException("Date '" + item.Date.ToString() + "' already in collection.");
|
||||
item.Calendar = GetCalendarModel();
|
||||
}
|
||||
|
||||
private CalendarModel GetCalendarModel()
|
||||
{
|
||||
if (_Calendar != null) return _Calendar;
|
||||
if (_Owner != null) return _Owner.Calendar;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, CalendarWorkDay newItem)
|
||||
{
|
||||
CalendarWorkDay oldItem = this[index];
|
||||
OnBeforeSetItem(index, oldItem, newItem);
|
||||
base.SetItem(index, newItem);
|
||||
OnAfterSetItem(index, oldItem, newItem);
|
||||
}
|
||||
|
||||
private void OnAfterSetItem(int index, CalendarWorkDay oldItem, CalendarWorkDay newItem)
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
{
|
||||
model.CalendarWorkDateRemoved(oldItem);
|
||||
model.CalendarWorkDateAdded(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, CalendarWorkDay oldItem, CalendarWorkDay newItem)
|
||||
{
|
||||
if (this[newItem.Date] != null)
|
||||
throw new InvalidOperationException("Date '" + newItem.Date.ToString() + "' already in collection.");
|
||||
|
||||
oldItem.Calendar = null;
|
||||
newItem.Calendar = GetCalendarModel();
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
foreach (CalendarWorkDay item in this)
|
||||
{
|
||||
item.Calendar = null;
|
||||
if (model != null)
|
||||
model.CalendarWorkDateRemoved(item);
|
||||
}
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item based on the Key assigned to the item
|
||||
/// </summary>
|
||||
/// <param name="date">Date to retrieve data for.</param>
|
||||
/// <returns>Reference to CalendarWorkDay or null if no day in collection.</returns>
|
||||
public CalendarWorkDay this[DateTime date]
|
||||
{
|
||||
get
|
||||
{
|
||||
CalendarWorkDay item = null;
|
||||
if (_ItemsDictionary.TryGetValue(date.Date, out item))
|
||||
return item;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,142 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the daily recurrence settings.
|
||||
/// </summary>
|
||||
public class DailyRecurrenceSettings : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
private AppointmentRecurrence _Recurrence = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DailyRecurrenceSettings class.
|
||||
/// </summary>
|
||||
/// <param name="recurrence"></param>
|
||||
public DailyRecurrenceSettings(AppointmentRecurrence recurrence)
|
||||
{
|
||||
_Recurrence = recurrence;
|
||||
}
|
||||
|
||||
private eDailyRecurrenceRepeat _RepeatOnDaysOfWeek = eDailyRecurrenceRepeat.All;
|
||||
/// <summary>
|
||||
/// Gets or sets the days of week on which appointment is repeated.
|
||||
/// </summary>
|
||||
[DefaultValue(eDailyRecurrenceRepeat.All)]
|
||||
public eDailyRecurrenceRepeat RepeatOnDaysOfWeek
|
||||
{
|
||||
get { return _RepeatOnDaysOfWeek; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnDaysOfWeek)
|
||||
{
|
||||
eDailyRecurrenceRepeat oldValue = _RepeatOnDaysOfWeek;
|
||||
_RepeatOnDaysOfWeek = value;
|
||||
OnRepeatOnDaysOfWeekChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnDaysOfWeekChanged(eDailyRecurrenceRepeat oldValue, eDailyRecurrenceRepeat newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnDaysOfWeek"));
|
||||
}
|
||||
|
||||
private int _RepeatInterval = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between recurring appointments. Default value is 1. Setting this value to for example 3 means that
|
||||
/// recurrence is repeated every 3 days.
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatInterval
|
||||
{
|
||||
get { return _RepeatInterval; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatInterval)
|
||||
{
|
||||
int oldValue = _RepeatInterval;
|
||||
_RepeatInterval = value;
|
||||
OnRepeatIntervalChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatIntervalChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatInterval"));
|
||||
}
|
||||
|
||||
private bool _ExplicitDailyRecurrence = false;
|
||||
/// <summary>
|
||||
/// Indicates whether appointment is explicitly repeated every day regardless of its end time. By default end time of appointment + 1 day is considered as next
|
||||
/// starting point for appointment. When this property is set to true appointment start time + 1 day is used as next starting point of recurrence.
|
||||
/// </summary>
|
||||
[DefaultValue(false), Description("Indicates whether appointment is explicitly repeated every day regardless of its end time. By default end time of appointment + 1 day is considered as next starting point for appointment. When this property is set to true appointment start time + 1 day is used as next starting point of recurrence.")]
|
||||
public bool ExplicitDailyRecurrence
|
||||
{
|
||||
get { return _ExplicitDailyRecurrence; }
|
||||
set
|
||||
{
|
||||
if (value != _ExplicitDailyRecurrence)
|
||||
{
|
||||
bool oldValue = _ExplicitDailyRecurrence;
|
||||
_ExplicitDailyRecurrence = value;
|
||||
OnExplicitDailyRecurrenceChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Called when ExplicitDailyRecurrence property has changed.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">Old property value</param>
|
||||
/// <param name="newValue">New property value</param>
|
||||
protected virtual void OnExplicitDailyRecurrenceChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("ExplicitDailyRecurrence"));
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,206 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
public static class DateTimeHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns number of weekdays (Monday-Friday) between two dates.
|
||||
/// </summary>
|
||||
/// <param name="startDateTime">Start date</param>
|
||||
/// <param name="endDateTime">End date</param>
|
||||
/// <returns>Total number of weekdays between two dates</returns>
|
||||
public static int TotalWeekDays(DateTime startDateTime, DateTime endDateTime)
|
||||
{
|
||||
int totalDays = 0;
|
||||
startDateTime = new DateTime(startDateTime.Year, startDateTime.Month, startDateTime.Day);
|
||||
endDateTime = new DateTime(endDateTime.Year, endDateTime.Month, endDateTime.Day, 23, 59, 0);
|
||||
|
||||
// Start date to monday
|
||||
if (startDateTime.DayOfWeek == DayOfWeek.Saturday)
|
||||
startDateTime = startDateTime.AddDays(2);
|
||||
else if (startDateTime.DayOfWeek == DayOfWeek.Sunday)
|
||||
startDateTime = startDateTime.AddDays(1);
|
||||
else
|
||||
{
|
||||
DateTime newStartDateTime = startDateTime.AddDays(8 - (int)startDateTime.DayOfWeek);
|
||||
if (newStartDateTime > endDateTime)
|
||||
{
|
||||
if (endDateTime.DayOfWeek == DayOfWeek.Saturday)
|
||||
endDateTime = endDateTime.AddDays(-1);
|
||||
else if (endDateTime.DayOfWeek == DayOfWeek.Sunday)
|
||||
endDateTime = endDateTime.AddDays(-2);
|
||||
return (int)Math.Ceiling(Math.Max(0, endDateTime.Subtract(startDateTime).TotalDays));
|
||||
}
|
||||
totalDays = 6 - (int)startDateTime.DayOfWeek;
|
||||
startDateTime = newStartDateTime;
|
||||
}
|
||||
|
||||
// End date to Sunday
|
||||
if (endDateTime.DayOfWeek == DayOfWeek.Saturday)
|
||||
endDateTime = endDateTime.AddDays(1);
|
||||
else if (endDateTime.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
int d = (int)endDateTime.DayOfWeek;
|
||||
DateTime newEndDateTime = endDateTime.AddDays(-(int)endDateTime.DayOfWeek);
|
||||
totalDays += d;
|
||||
endDateTime=newEndDateTime;
|
||||
}
|
||||
|
||||
int t = (int)Math.Max(0, Math.Ceiling(endDateTime.Subtract(startDateTime).TotalDays));
|
||||
if (t > 0)
|
||||
totalDays += (t / 7) * 5;
|
||||
|
||||
return totalDays;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return total number of days specified by day parameter between two dates.
|
||||
/// </summary>
|
||||
/// <param name="startDate">Start date.</param>
|
||||
/// <param name="endDate">End date.</param>
|
||||
/// <param name="day">Day of week</param>
|
||||
/// <returns>Number of days between two dates</returns>
|
||||
public static int TotalDays(DateTime startDate, DateTime endDate, DayOfWeek day)
|
||||
{
|
||||
if (endDate < startDate) return 0;
|
||||
|
||||
|
||||
if (startDate.DayOfWeek > day)
|
||||
startDate = startDate.AddDays(7 - ((int)startDate.DayOfWeek - (int)day));
|
||||
else
|
||||
startDate = startDate.AddDays(day - startDate.DayOfWeek);
|
||||
if (endDate < startDate) return 0;
|
||||
|
||||
if (endDate.DayOfWeek < day)
|
||||
endDate = endDate.AddDays(-(7 - ((int)day - (int)endDate.DayOfWeek)));
|
||||
else
|
||||
endDate = endDate.AddDays(day - endDate.DayOfWeek);
|
||||
|
||||
if (endDate.Subtract(startDate).TotalDays <= 1) return 1;
|
||||
|
||||
int t = (int)Math.Ceiling(endDate.Subtract(startDate).TotalDays);
|
||||
int totalDays = (int)Math.Floor(Math.Max(1, (double)t / 7)) + 1; // +1 since endDate is always on the day
|
||||
//if (endDate.RelativeDayOfWeek == day) totalDays++;
|
||||
return totalDays;
|
||||
}
|
||||
|
||||
public static int TotalNumberOfDays(DateTime startDate, DateTime endDate, eDayOfWeekRecurrence daysOfWeek)
|
||||
{
|
||||
int totalDays=0;
|
||||
if (daysOfWeek == eDayOfWeekRecurrence.All)
|
||||
return (int)Math.Ceiling(endDate.Subtract(startDate).TotalDays);
|
||||
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Friday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Friday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Monday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Monday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Saturday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Saturday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Sunday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Sunday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Thursday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Thursday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Tuesday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Tuesday);
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Wednesday) != 0)
|
||||
totalDays += TotalDays(startDate, endDate, DayOfWeek.Wednesday);
|
||||
|
||||
return totalDays;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the date/time that represents end of the day value.
|
||||
/// </summary>
|
||||
/// <param name="_DayDate"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime EndOfDay(DateTime day)
|
||||
{
|
||||
return new DateTime(day.Year, day.Month, day.Day, 23, 59, 59);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the date/time that represents beginning of the day value.
|
||||
/// </summary>
|
||||
/// <param name="_DayDate"></param>
|
||||
/// <returns></returns>
|
||||
public static DateTime BeginningOfDay(DateTime day)
|
||||
{
|
||||
return new DateTime(day.Year, day.Month, day.Day, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if date falls at begging of the day 12:00 AM
|
||||
/// </summary>
|
||||
/// <param name="_DayDate"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsBeginningOfDay(DateTime day)
|
||||
{
|
||||
return day.Equals(BeginningOfDay(day));
|
||||
}
|
||||
|
||||
internal static bool HasDay(DayOfWeek dayOfWeek, eDayOfWeekRecurrence days)
|
||||
{
|
||||
if (dayOfWeek == DayOfWeek.Monday && (days & eDayOfWeekRecurrence.Monday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Tuesday && (days & eDayOfWeekRecurrence.Tuesday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Wednesday && (days & eDayOfWeekRecurrence.Wednesday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Thursday && (days & eDayOfWeekRecurrence.Thursday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Friday && (days & eDayOfWeekRecurrence.Friday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Saturday && (days & eDayOfWeekRecurrence.Saturday) != 0) return true;
|
||||
if (dayOfWeek == DayOfWeek.Sunday && (days & eDayOfWeekRecurrence.Sunday) != 0) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsWeekendDay(DateTime currentDay)
|
||||
{
|
||||
return currentDay.DayOfWeek == DayOfWeek.Sunday || currentDay.DayOfWeek == DayOfWeek.Saturday;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets greater date between two dates.
|
||||
/// </summary>
|
||||
/// <param name="date1">Date 1</param>
|
||||
/// <param name="date2">Date 2</param>
|
||||
/// <returns>Greater date.</returns>
|
||||
public static DateTime MaxDate(DateTime date1, DateTime date2)
|
||||
{
|
||||
return date1 > date2 ? date1 : date2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if both dates are on same day and year.
|
||||
/// </summary>
|
||||
/// <param name="date1">First date</param>
|
||||
/// <param name="date2">Second date</param>
|
||||
/// <returns>true if dates are on same day and year</returns>
|
||||
public static bool IsSameDay(DateTime date1, DateTime date2)
|
||||
{
|
||||
return (date1.Year == date2.Year && date1.Month == date2.Month &&
|
||||
date1.Day == date2.Day) || date1.Date.AddDays(1) == date2;
|
||||
|
||||
//return date1.Year == date2.Year && date1.Month == date2.Month && date1.Day == date2.Day;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if time periods overlap.
|
||||
/// </summary>
|
||||
/// <param name="startTime1">Start of first period.</param>
|
||||
/// <param name="endTime1">End of first period.</param>
|
||||
/// <param name="startTime2">Start of second period.</param>
|
||||
/// <param name="endTime2">End of second period.</param>
|
||||
/// <returns>true if periods overlap</returns>
|
||||
public static bool TimePeriodsOverlap(DateTime startTime1, DateTime endTime1, DateTime startTime2, DateTime endTime2)
|
||||
{
|
||||
if (startTime1 <= startTime2 && endTime1 > startTime2 || startTime1 >= startTime2 && startTime1 < endTime2)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,77 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the calendar day.
|
||||
/// </summary>
|
||||
public class Day
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Day class.
|
||||
/// </summary>
|
||||
/// <param name="dayDate"></param>
|
||||
/// <param name="calendar"></param>
|
||||
public Day(DateTime dayDate, CalendarModel calendar)
|
||||
{
|
||||
_DayDate = dayDate;
|
||||
_Calendar = calendar;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
private AppointmentSubsetCollection _Appointments;
|
||||
/// <summary>
|
||||
/// Gets appointments that start on this day.
|
||||
/// </summary>
|
||||
public AppointmentSubsetCollection Appointments
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Appointments == null)
|
||||
_Appointments = new AppointmentSubsetCollection(_Calendar, _DayDate, DateTimeHelper.EndOfDay(_DayDate));
|
||||
return _Appointments;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate the day appointments
|
||||
/// </summary>
|
||||
internal void InvalidateAppointments()
|
||||
{
|
||||
if (_Appointments != null)
|
||||
_Appointments.InvalidateCollection();
|
||||
}
|
||||
|
||||
private DateTime _DayDate = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets the date this day represents.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public DateTime DayDate
|
||||
{
|
||||
get { return _DayDate; }
|
||||
internal set { _DayDate = value; }
|
||||
}
|
||||
|
||||
private CalendarModel _Calendar;
|
||||
/// <summary>
|
||||
/// Gets the Calendar this day is part of.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set { _Calendar = value; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,191 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies notification type when appointment start time has been reached.
|
||||
/// </summary>
|
||||
[Flags()]
|
||||
public enum eStartTimeAction
|
||||
{
|
||||
/// <summary>
|
||||
/// No action is taken.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// StartTimeReached event is fired.
|
||||
/// </summary>
|
||||
StartTimeReachedEvent = 1,
|
||||
/// <summary>
|
||||
/// StartTimeCommand is executed.
|
||||
/// </summary>
|
||||
Command = 2,
|
||||
/// <summary>
|
||||
/// Both event and command are performed.
|
||||
/// </summary>
|
||||
StartTimeReachedEventAndCommand = StartTimeReachedEvent | Command
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies notification type when reminder time has been reached.
|
||||
/// </summary>
|
||||
[Flags()]
|
||||
public enum eReminderAction
|
||||
{
|
||||
/// <summary>
|
||||
/// No action is taken.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Reminder event is fired.
|
||||
/// </summary>
|
||||
Event = 1,
|
||||
/// <summary>
|
||||
/// Reminder Command is executed.
|
||||
/// </summary>
|
||||
Command = 2,
|
||||
/// <summary>
|
||||
/// Both event and command are performed.
|
||||
/// </summary>
|
||||
EventAndCommand = Event | Command
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the recurrence range type.
|
||||
/// </summary>
|
||||
public enum eRecurrenceRangeLimitType
|
||||
{
|
||||
/// <summary>
|
||||
/// Recurrence range has no end date specified.
|
||||
/// </summary>
|
||||
NoEndDate,
|
||||
/// <summary>
|
||||
/// Recurrence ends on date specified by RangeEndDate property.
|
||||
/// </summary>
|
||||
RangeEndDate,
|
||||
/// <summary>
|
||||
/// Recurrence ends after specified number of repeats by RangeNumberOfOccurrences property.
|
||||
/// </summary>
|
||||
RangeNumberOfOccurrences
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the pattern type for appointment recurrence.
|
||||
/// </summary>
|
||||
public enum eRecurrencePatternType
|
||||
{
|
||||
/// <summary>
|
||||
/// Appointment recurs daily.
|
||||
/// </summary>
|
||||
Daily,
|
||||
/// <summary>
|
||||
/// Appointment recurs weekly.
|
||||
/// </summary>
|
||||
Weekly,
|
||||
/// <summary>
|
||||
/// Appointment recurs monthly.
|
||||
/// </summary>
|
||||
Monthly,
|
||||
/// <summary>
|
||||
/// Appointment recurs yearly.
|
||||
/// </summary>
|
||||
Yearly
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the relative day in month for recurrence.
|
||||
/// </summary>
|
||||
public enum eRelativeDayInMonth
|
||||
{
|
||||
/// <summary>
|
||||
/// No value specified.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// The first occurrence of the specified day in its month.
|
||||
/// </summary>
|
||||
First,
|
||||
/// <summary>
|
||||
/// The second occurrence of the specified day in its month.
|
||||
/// </summary>
|
||||
Second,
|
||||
/// <summary>
|
||||
/// The third occurrence of the specified day in its month.
|
||||
/// </summary>
|
||||
Third,
|
||||
/// <summary>
|
||||
/// The fourth occurrence of the specified day in its month.
|
||||
/// </summary>
|
||||
Fourth,
|
||||
/// <summary>
|
||||
/// The last occurrence of the specified day in its month.
|
||||
/// </summary>
|
||||
Last
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies on which day the appointment is repeated.
|
||||
/// </summary>
|
||||
[Flags()]
|
||||
public enum eDayOfWeekRecurrence
|
||||
{
|
||||
None = 0,
|
||||
Monday = 1,
|
||||
Tuesday = 2,
|
||||
Wednesday = 4,
|
||||
Thursday = 8,
|
||||
Friday = 16,
|
||||
Saturday = 32,
|
||||
Sunday = 64,
|
||||
WeekendDays = Saturday | Sunday,
|
||||
WeekDays = Monday | Tuesday | Wednesday | Thursday | Friday,
|
||||
All = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies on which days daily recurrence is repeated.
|
||||
/// </summary>
|
||||
public enum eDailyRecurrenceRepeat
|
||||
{
|
||||
/// <summary>
|
||||
/// Appointment is repeated on all days.
|
||||
/// </summary>
|
||||
All = eDayOfWeekRecurrence.Monday | eDayOfWeekRecurrence.Tuesday | eDayOfWeekRecurrence.Wednesday | eDayOfWeekRecurrence.Thursday | eDayOfWeekRecurrence.Friday | eDayOfWeekRecurrence.Saturday | eDayOfWeekRecurrence.Sunday,
|
||||
|
||||
/// <summary>
|
||||
/// Appointment is repeated on week-days only, Monday-Friday.
|
||||
/// </summary>
|
||||
WeekDays = eDayOfWeekRecurrence.Monday | eDayOfWeekRecurrence.Tuesday | eDayOfWeekRecurrence.Wednesday | eDayOfWeekRecurrence.Thursday | eDayOfWeekRecurrence.Friday,
|
||||
|
||||
/// <summary>
|
||||
/// Appointment is repeated on weekend-days only, Saturday-Sunday.
|
||||
/// </summary>
|
||||
WeekendDays = eDayOfWeekRecurrence.WeekendDays
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies on which month monthly appointment recurrence is repeated.
|
||||
/// </summary>
|
||||
[Flags()]
|
||||
public enum eMonthRecurrence
|
||||
{
|
||||
January = 1,
|
||||
February = 2,
|
||||
March = 4,
|
||||
April = 8,
|
||||
May = 16,
|
||||
June = 32,
|
||||
July = 64,
|
||||
August = 128,
|
||||
September = 256,
|
||||
October = 512,
|
||||
November = 1024,
|
||||
December = 2048,
|
||||
All = January | February | March | April | May | June | July | August | September | October | November | December
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,16 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents collection of holidays.
|
||||
/// </summary>
|
||||
public class HolidaysCollection
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,48 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an interface for property notification change.
|
||||
/// </summary>
|
||||
public interface INotifySubPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when property on object or its sub-objects has changed.
|
||||
/// </summary>
|
||||
event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
}
|
||||
|
||||
public delegate void SubPropertyChangedEventHandler(object sender, SubPropertyChangedEventArgs e);
|
||||
/// <summary>
|
||||
/// Defines event arguments for SubPropertyChanged event.
|
||||
/// </summary>
|
||||
public class SubPropertyChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to PropertyChangedArgs of changed property.
|
||||
/// </summary>
|
||||
public PropertyChangedEventArgs PropertyChangedArgs = null;
|
||||
/// <summary>
|
||||
/// Reference to the source object of the event.
|
||||
/// </summary>
|
||||
public object Source = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the SubPropertyChangedEventArgs class.
|
||||
/// </summary>
|
||||
/// <param name="propertyChangedArgs"></param>
|
||||
/// <param name="source"></param>
|
||||
public SubPropertyChangedEventArgs(object source, PropertyChangedEventArgs propertyChangedArgs)
|
||||
{
|
||||
PropertyChangedArgs = propertyChangedArgs;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,156 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DevComponents.Schedule.Model.Serialization
|
||||
{
|
||||
public class CalendarEntry
|
||||
{
|
||||
#region Private variables
|
||||
|
||||
private string _Id = "";
|
||||
private string _Value = "";
|
||||
|
||||
private List<AttributeData> _Attributes;
|
||||
|
||||
private int _LineStart;
|
||||
private int _LineEnd;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="lineStart">Text line start</param>
|
||||
/// <param name="lineEnd">Text line end</param>
|
||||
public CalendarEntry(int lineStart, int lineEnd)
|
||||
{
|
||||
_LineStart = lineStart;
|
||||
_LineEnd = lineEnd;
|
||||
|
||||
_Attributes = new List<AttributeData>();
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
#region Attributes
|
||||
|
||||
/// <summary>
|
||||
/// Attributes
|
||||
/// </summary>
|
||||
public List<AttributeData> Attributes
|
||||
{
|
||||
get { return (_Attributes); }
|
||||
set { _Attributes = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Id
|
||||
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get { return (_Id); }
|
||||
set { _Id = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LineEnd
|
||||
|
||||
/// <summary>
|
||||
/// LineEnd
|
||||
/// </summary>
|
||||
public int LineEnd
|
||||
{
|
||||
get { return (_LineEnd); }
|
||||
set { _LineEnd = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LineStart
|
||||
|
||||
/// <summary>
|
||||
/// LineStart
|
||||
/// </summary>
|
||||
public int LineStart
|
||||
{
|
||||
get { return (_LineStart); }
|
||||
set { _LineStart = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Value
|
||||
|
||||
/// <summary>
|
||||
/// Value
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get { return (_Value); }
|
||||
set { _Value = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region AttributeData
|
||||
|
||||
public class AttributeData
|
||||
{
|
||||
#region Private variables
|
||||
|
||||
private string _Id;
|
||||
private string _Value;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="id">Id</param>
|
||||
/// <param name="value">Value</param>
|
||||
public AttributeData(string id, string value)
|
||||
{
|
||||
_Id = id;
|
||||
_Value = value;
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
#region Id
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute Id
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get { return (_Id); }
|
||||
set { _Id = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Value
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute value
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get { return (_Value); }
|
||||
set { _Value = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@@ -0,0 +1,989 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DevComponents.Schedule.Model.Serialization
|
||||
{
|
||||
/// <summary>
|
||||
/// Export DNB internal Model/Appointment data layout into
|
||||
/// ICS (Internet Calendaring and Scheduling - RFC5545) format file.
|
||||
/// </summary>
|
||||
public class IcsExporter
|
||||
{
|
||||
#region Private constants
|
||||
|
||||
private const int FoldCount = 74;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
private DateTime _DtStamp = DateTime.Now;
|
||||
|
||||
private string _ProdId = "PRODID:-//DotNetBar\\, Inc//iCal 1.0//EN";
|
||||
|
||||
private CalendarModel _Model;
|
||||
|
||||
private string[] _CalNames;
|
||||
private string[] _OwnerKeys;
|
||||
|
||||
private string _ExportFile;
|
||||
private StreamWriter _StreamWriter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// IcsExporter
|
||||
/// </summary>
|
||||
public IcsExporter()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IcsExporter
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
public IcsExporter(CalendarModel model)
|
||||
{
|
||||
Model = model;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
#region ProdId
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Calendar Product Id.
|
||||
/// </summary>
|
||||
public string ProdId
|
||||
{
|
||||
get { return (_ProdId); }
|
||||
set { _ProdId = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Model
|
||||
|
||||
/// <summary>
|
||||
/// CalendarModel
|
||||
/// </summary>
|
||||
public CalendarModel Model
|
||||
{
|
||||
get { return (_Model); }
|
||||
set { _Model = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Export
|
||||
|
||||
/// <summary>
|
||||
/// Exports Appointment data in the iCalendar format
|
||||
/// </summary>
|
||||
/// <param name="ownerKeys">Array of OwnerKeys</param>
|
||||
/// <param name="calNames">Array of CalendarNames</param>
|
||||
/// <param name="streamWriter">Export StreamWriter</param>
|
||||
public void Export(string[] ownerKeys, string[] calNames, StreamWriter streamWriter)
|
||||
{
|
||||
if (_Model == null)
|
||||
throw new Exception("Model can not be null.");
|
||||
|
||||
_CalNames = calNames;
|
||||
_OwnerKeys = ownerKeys;
|
||||
|
||||
if (streamWriter == null)
|
||||
throw new Exception("Invalid StreamWriter.");
|
||||
|
||||
_StreamWriter = streamWriter;
|
||||
|
||||
if (String.IsNullOrEmpty(_ExportFile) == true)
|
||||
_ExportFile = streamWriter.BaseStream.ToString();
|
||||
|
||||
ExportOwnerData();
|
||||
|
||||
streamWriter.Flush();
|
||||
}
|
||||
|
||||
#region Export variations
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments to the given export file.
|
||||
/// </summary>
|
||||
/// <param name="exportFile">Output file path</param>
|
||||
public void Export(string exportFile)
|
||||
{
|
||||
Export((string[])null, null, exportFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments to the given export stream.
|
||||
/// </summary>
|
||||
/// <param name="streamWriter">Output StreamWriter</param>
|
||||
public void Export(StreamWriter streamWriter)
|
||||
{
|
||||
Export((string[])null, null, streamWriter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments for the specified OwnerKey to
|
||||
/// the given export file.
|
||||
/// </summary>
|
||||
/// <param name="ownerKey">Appointment OwnerKey</param>
|
||||
/// <param name="exportFile">Output file path</param>
|
||||
public void Export(string ownerKey, string exportFile)
|
||||
{
|
||||
Export(ownerKey, ownerKey, exportFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments for the specified OwnerKey to
|
||||
/// the given export stream.
|
||||
/// </summary>
|
||||
/// <param name="ownerKey">Appointment OwnerKey</param>
|
||||
/// <param name="streamWriter">Output StreamWriter</param>
|
||||
public void Export(string ownerKey, StreamWriter streamWriter)
|
||||
{
|
||||
Export(ownerKey, ownerKey, streamWriter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments for the specified OwnerKey to
|
||||
/// the given export file, using the specified calendar name.
|
||||
/// </summary>
|
||||
/// <param name="ownerKey">Appointment OwnerKey</param>
|
||||
/// <param name="calName">Associated Calendar Name</param>
|
||||
/// <param name="exportFile">Output file path</param>
|
||||
public void Export(string ownerKey, string calName, string exportFile)
|
||||
{
|
||||
string[] ownerKeys = new string[] { ownerKey };
|
||||
string[] calNames = new string[] { calName };
|
||||
|
||||
Export(ownerKeys, calNames, exportFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments for the specified OwnerKey to
|
||||
/// the given export stream, using the specified calendar name.
|
||||
/// </summary>
|
||||
/// <param name="ownerKey">Appointment OwnerKey</param>
|
||||
/// <param name="calName">Associated Calendar Name</param>
|
||||
/// <param name="streamWriter">Output StreamWriter</param>
|
||||
public void Export(string ownerKey, string calName, StreamWriter streamWriter)
|
||||
{
|
||||
string[] ownerKeys = new string[] { ownerKey };
|
||||
string[] calNames = new string[] { calName };
|
||||
|
||||
Export(ownerKeys, calNames, streamWriter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports all appointments for the specified OwnerKey array to
|
||||
/// the given export file, using the specified associated calendar name array.
|
||||
/// </summary>
|
||||
/// <param name="ownerKeys">Array of OwnerKeys</param>
|
||||
/// <param name="calNames">Array of 1:1 associated Calendar Names</param>
|
||||
/// <param name="exportFile">Output file path</param>
|
||||
public virtual void Export(string[] ownerKeys, string[] calNames, string exportFile)
|
||||
{
|
||||
using (StreamWriter streamWriter = new StreamWriter(exportFile))
|
||||
Export(ownerKeys, calNames, streamWriter);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportOwnerData
|
||||
|
||||
/// <summary>
|
||||
/// Exports model data for each requested OwnerKey
|
||||
/// </summary>
|
||||
private void ExportOwnerData()
|
||||
{
|
||||
if (_OwnerKeys == null)
|
||||
_OwnerKeys = GetDefaultOwnerKeys();
|
||||
|
||||
if (_CalNames == null)
|
||||
_CalNames = _OwnerKeys;
|
||||
|
||||
string lastCalName = null;
|
||||
|
||||
for (int i = 0; i < _OwnerKeys.Length; i++)
|
||||
{
|
||||
string ownerKey = _OwnerKeys[i] ?? "";
|
||||
|
||||
string calName = ((i < _CalNames.Length)
|
||||
? _CalNames[i]
|
||||
: _CalNames[_CalNames.Length - 1]) ?? ownerKey;
|
||||
|
||||
if (calName.Equals(lastCalName) == false)
|
||||
{
|
||||
if (lastCalName != null)
|
||||
ExportCalEnd();
|
||||
|
||||
ExportCalBegin(calName);
|
||||
|
||||
lastCalName = calName;
|
||||
}
|
||||
|
||||
foreach (Appointment app in Model.Appointments)
|
||||
{
|
||||
if (IsAppointmentVisible(app, ownerKey) == true)
|
||||
ExportVEvent(app);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastCalName != null)
|
||||
ExportCalEnd();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportCalBegin
|
||||
|
||||
/// <summary>
|
||||
/// Exports the beginning calendar sequence
|
||||
/// </summary>
|
||||
/// <param name="calName"></param>
|
||||
private void ExportCalBegin(string calName)
|
||||
{
|
||||
ExportLine("BEGIN:VCALENDAR");
|
||||
ExportLine("CALSCALE:GREGORIAN");
|
||||
|
||||
if (_ProdId != null)
|
||||
ExportLine(_ProdId);
|
||||
|
||||
ExportLine("X-WR-CALNAME;VALUE=TEXT:" + calName);
|
||||
ExportLine("VERSION:2.0");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportCalEnd
|
||||
|
||||
/// <summary>
|
||||
/// Exports the ending calendar sequence
|
||||
/// </summary>
|
||||
private void ExportCalEnd()
|
||||
{
|
||||
ExportLine("END:VCALENDAR\n");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportVEvent
|
||||
|
||||
#region ExportVEvent
|
||||
|
||||
/// <summary>
|
||||
/// Exports a calendar event (appointment)
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
private void ExportVEvent(Appointment app)
|
||||
{
|
||||
ExportLine("\nBEGIN:VEVENT");
|
||||
|
||||
if (String.IsNullOrEmpty(app.Subject) == false)
|
||||
ExportLine("SUMMARY:" + AddMetaData(app.Subject));
|
||||
|
||||
if (String.IsNullOrEmpty(app.Description) == false)
|
||||
ExportLine("DESCRIPTION:" + AddMetaData(app.Description));
|
||||
|
||||
ExportLine("UID:" + Guid.NewGuid().ToString());
|
||||
ExportLine("DTSTAMP:" + GetUniversalTime(_DtStamp));
|
||||
|
||||
if (String.IsNullOrEmpty(app.CategoryColor) == false)
|
||||
ExportLine("X-DNB-CATEGORYCOLOR:" + app.CategoryColor);
|
||||
|
||||
if (String.IsNullOrEmpty(app.DisplayTemplate) == false)
|
||||
ExportLine("X-DNB-DISPLAYTEMPLATE:" + AddMetaData(app.DisplayTemplate));
|
||||
|
||||
if (app.ImageAlign != eImageContentAlignment.TopLeft)
|
||||
ExportLine("X-DNB-IMAGEALIGN:" + Enum.GetName(typeof(eImageContentAlignment), app.ImageAlign));
|
||||
|
||||
if (String.IsNullOrEmpty(app.ImageKey) == false)
|
||||
ExportLine("X-DNB-IMAGEKEY:" + app.ImageKey);
|
||||
|
||||
if (app.Locked == true)
|
||||
ExportLine("X-DNB-LOCKED:true");
|
||||
|
||||
if (app.StartTimeAction != eStartTimeAction.None)
|
||||
ExportLine("X-DNB-STARTTIMEACTION:" + Enum.GetName(typeof(eStartTimeAction), app.StartTimeAction));
|
||||
|
||||
if (String.IsNullOrEmpty(app.TimeMarkedAs) == false)
|
||||
ExportLine("X-DNB-TIMEMARKEDAS:" + app.TimeMarkedAs);
|
||||
|
||||
if (String.IsNullOrEmpty(app.Tooltip) == false)
|
||||
ExportLine("X-DNB-TOOLTIP:" + AddMetaData(app.Tooltip));
|
||||
|
||||
if ((app.StartTime.Hour == 0 && app.StartTime.Minute == 0) &&
|
||||
(app.EndTime.Hour == 0 && app.EndTime.Minute == 0))
|
||||
{
|
||||
ExportLine("DTSTART;VALUE=DATE:" + app.StartTime.ToString("yyyyMMdd"));
|
||||
|
||||
if ((app.EndTime - app.StartTime).TotalMinutes != 60 * 24)
|
||||
ExportLine("DTEND;VALUE=DATE:" + app.EndTime.ToString("yyyyMMdd"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ExportLine("DTSTART:" + GetUniversalTime(app.StartTime));
|
||||
ExportLine("DTEND:" + GetUniversalTime(app.EndTime));
|
||||
}
|
||||
|
||||
if (app.Private == true)
|
||||
ExportLine("CLASS:PRIVATE");
|
||||
|
||||
ExportRRule(app);
|
||||
ExportAlarms(app);
|
||||
|
||||
ExportLine("END:VEVENT");
|
||||
}
|
||||
|
||||
#region ExportRRule
|
||||
|
||||
#region ExportRRule
|
||||
|
||||
/// <summary>
|
||||
/// Exports the appointment Recurrence Rule
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
private void ExportRRule(Appointment app)
|
||||
{
|
||||
if (app.Recurrence != null)
|
||||
{
|
||||
switch (app.Recurrence.RecurrenceType)
|
||||
{
|
||||
case eRecurrencePatternType.Daily:
|
||||
ExportDailyRRule(app.Recurrence);
|
||||
break;
|
||||
|
||||
case eRecurrencePatternType.Weekly:
|
||||
ExportWeeklyRRule(app.Recurrence);
|
||||
break;
|
||||
|
||||
case eRecurrencePatternType.Monthly:
|
||||
ExportMonthlyRRule(app.Recurrence);
|
||||
break;
|
||||
|
||||
default:
|
||||
ExportYearlyRRule(app);
|
||||
break;
|
||||
}
|
||||
|
||||
ExportExDate(app.Recurrence);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportDailyRRule
|
||||
|
||||
/// <summary>
|
||||
/// ExportDailyRRule
|
||||
/// </summary>
|
||||
/// <param name="recur"></param>
|
||||
private void ExportDailyRRule(AppointmentRecurrence recur)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("RRULE:FREQ=DAILY");
|
||||
|
||||
AddRRuleInterval(sb, recur.Daily.RepeatInterval);
|
||||
AddRRuleByDay(sb, recur);
|
||||
|
||||
AddRRuleRangeInfo(sb, recur);
|
||||
|
||||
ExportLine(sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportWeeklyRRule
|
||||
|
||||
/// <summary>
|
||||
/// ExportWeeklyRRule
|
||||
/// </summary>
|
||||
/// <param name="recur"></param>
|
||||
private void ExportWeeklyRRule(AppointmentRecurrence recur)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("RRULE:FREQ=WEEKLY");
|
||||
|
||||
AddRRuleInterval(sb, recur.Weekly.RepeatInterval);
|
||||
|
||||
if (recur.Weekly.RepeatOnDaysOfWeek != eDayOfWeekRecurrence.None)
|
||||
AddRRuleByDay(sb, recur.Weekly.RepeatOnDaysOfWeek);
|
||||
|
||||
AddRRuleRangeInfo(sb, recur);
|
||||
|
||||
ExportLine(sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportMonthlyRRule
|
||||
|
||||
/// <summary>
|
||||
/// ExportMonthlyRRule
|
||||
/// </summary>
|
||||
/// <param name="recur"></param>
|
||||
private void ExportMonthlyRRule(AppointmentRecurrence recur)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("RRULE:FREQ=MONTHLY");
|
||||
|
||||
AddRRuleInterval(sb, recur.Monthly.RepeatInterval);
|
||||
|
||||
if (recur.Monthly.RepeatOnRelativeDayInMonth == eRelativeDayInMonth.None)
|
||||
sb.AppendFormat("{0}{1:D}", ";BYMONTHDAY=", recur.Monthly.RepeatOnDayOfMonth);
|
||||
else
|
||||
AddRRuleByDay(sb, recur.Monthly.RepeatOnRelativeDayInMonth, recur.Monthly.RelativeDayOfWeek);
|
||||
|
||||
AddRRuleRangeInfo(sb, recur);
|
||||
|
||||
ExportLine(sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportYearlyRRule
|
||||
|
||||
/// <summary>
|
||||
/// ExportYearlyRRule
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
private void ExportYearlyRRule(Appointment app)
|
||||
{
|
||||
AppointmentRecurrence recur = app.Recurrence;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append("RRULE:FREQ=YEARLY");
|
||||
sb.AppendFormat("{0}{1:D}", ";BYMONTH=", recur.Yearly.RepeatOnMonth);
|
||||
|
||||
if (recur.Yearly.RepeatOnRelativeDayInMonth == eRelativeDayInMonth.None)
|
||||
{
|
||||
if (recur.Yearly.RepeatOnDayOfMonth != app.StartTime.Day)
|
||||
sb.AppendFormat("{0}{1:D}", ";BYMONTHDAY=", recur.Yearly.RepeatOnDayOfMonth);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRRuleByDay(sb, recur.Yearly.RepeatOnRelativeDayInMonth, recur.Yearly.RelativeDayOfWeek);
|
||||
}
|
||||
|
||||
AddRRuleRangeInfo(sb, recur);
|
||||
|
||||
ExportLine(sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddRRuleByDay
|
||||
|
||||
/// <summary>
|
||||
/// AddRRuleByDay
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="repeat"></param>
|
||||
private void AddRRuleByDay(StringBuilder sb, eDayOfWeekRecurrence repeat)
|
||||
{
|
||||
sb.Append(";BYDAY=");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Sunday) != 0)
|
||||
sb.Append("SU,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Monday) != 0)
|
||||
sb.Append("MO,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Tuesday) != 0)
|
||||
sb.Append("TU,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Wednesday) != 0)
|
||||
sb.Append("WE,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Thursday) != 0)
|
||||
sb.Append("TH,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Friday) != 0)
|
||||
sb.Append("FR,");
|
||||
|
||||
if ((repeat & eDayOfWeekRecurrence.Saturday) != 0)
|
||||
sb.Append("SA,");
|
||||
|
||||
sb.Length--;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddRRuleByDay
|
||||
|
||||
/// <summary>
|
||||
/// AddRRuleByDay
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="repeat"></param>
|
||||
private void AddRRuleByDay(StringBuilder sb, AppointmentRecurrence recur)
|
||||
{
|
||||
if (recur.Daily.RepeatInterval > 1)
|
||||
{
|
||||
sb.Append(";BYDAY=");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (recur.Daily.RepeatOnDaysOfWeek)
|
||||
{
|
||||
case eDailyRecurrenceRepeat.All:
|
||||
sb.Append(";BYDAY=SU,MO,TU,WE,TH,FR,SA");
|
||||
break;
|
||||
|
||||
case eDailyRecurrenceRepeat.WeekDays:
|
||||
sb.Append(";BYDAY=MO,TU,WE,TH,FR");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.Append(";BYDAY=SA,SU");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddRRuleByDay
|
||||
|
||||
/// <summary>
|
||||
/// AddRRuleByDay
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="rday"></param>
|
||||
/// <param name="dow"></param>
|
||||
private void AddRRuleByDay(StringBuilder sb, eRelativeDayInMonth rday, DayOfWeek dow)
|
||||
{
|
||||
sb.Append(";BYDAY=");
|
||||
|
||||
switch (dow)
|
||||
{
|
||||
case DayOfWeek.Sunday:
|
||||
sb.Append("SU");
|
||||
break;
|
||||
|
||||
case DayOfWeek.Monday:
|
||||
sb.Append("MO");
|
||||
break;
|
||||
|
||||
case DayOfWeek.Tuesday:
|
||||
sb.Append("TU");
|
||||
break;
|
||||
|
||||
case DayOfWeek.Wednesday:
|
||||
sb.Append("WE");
|
||||
break;
|
||||
|
||||
case DayOfWeek.Thursday:
|
||||
sb.Append("TH");
|
||||
break;
|
||||
|
||||
case DayOfWeek.Friday:
|
||||
sb.Append("FR");
|
||||
break;
|
||||
|
||||
default:
|
||||
sb.Append("SA");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rday)
|
||||
{
|
||||
case eRelativeDayInMonth.First:
|
||||
sb.Append(";BYSETPOS=1");
|
||||
break;
|
||||
|
||||
case eRelativeDayInMonth.Second:
|
||||
sb.Append(";BYSETPOS=2");
|
||||
break;
|
||||
|
||||
case eRelativeDayInMonth.Third:
|
||||
sb.Append(";BYSETPOS=3");
|
||||
break;
|
||||
|
||||
case eRelativeDayInMonth.Fourth:
|
||||
sb.Append(";BYSETPOS=4");
|
||||
break;
|
||||
|
||||
case eRelativeDayInMonth.Last:
|
||||
sb.Append(";BYSETPOS=-1");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddRRuleInterval
|
||||
|
||||
/// <summary>
|
||||
/// AddRRuleInterval
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="interval"></param>
|
||||
private void AddRRuleInterval(StringBuilder sb, int interval)
|
||||
{
|
||||
if (interval > 1)
|
||||
sb.Append(";INTERVAL=" + interval.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddRRuleRangeInfo
|
||||
|
||||
/// <summary>
|
||||
/// AddRRuleRangeInfo
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="recur"></param>
|
||||
private void AddRRuleRangeInfo(StringBuilder sb, AppointmentRecurrence recur)
|
||||
{
|
||||
switch (recur.RangeLimitType)
|
||||
{
|
||||
case eRecurrenceRangeLimitType.RangeNumberOfOccurrences:
|
||||
if (recur.RangeNumberOfOccurrences > 0)
|
||||
sb.Append(";COUNT=" + (recur.RangeNumberOfOccurrences + 1).ToString());
|
||||
break;
|
||||
|
||||
case eRecurrenceRangeLimitType.RangeEndDate:
|
||||
sb.Append(";UNTIL=" + GetUtcDate(recur.RangeEndDate));
|
||||
break;
|
||||
}
|
||||
|
||||
if (recur.RecurrenceStartDate != DateTime.MinValue && recur.RecurrenceStartDate != recur.Appointment.StartTime)
|
||||
ExportLine("X-DNB-RECSTARTDATE=" + GetUtcDate(recur.RecurrenceStartDate));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportExDate
|
||||
|
||||
/// <summary>
|
||||
/// ExportExDate
|
||||
/// </summary>
|
||||
/// <param name="recur"></param>
|
||||
private void ExportExDate(AppointmentRecurrence recur)
|
||||
{
|
||||
if (recur.SkippedRecurrences != null)
|
||||
{
|
||||
foreach (DateTime date in recur.SkippedRecurrences)
|
||||
ExportLine("EXDATE:" + GetUtcDate(date));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportAlarms
|
||||
|
||||
/// <summary>
|
||||
/// ExportAlarms
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
private void ExportAlarms(Appointment app)
|
||||
{
|
||||
if (app.Reminders != null)
|
||||
{
|
||||
foreach (Reminder rem in app.Reminders)
|
||||
{
|
||||
ExportLine("\nBEGIN:VALARM");
|
||||
|
||||
if (string.IsNullOrEmpty(rem.Description) == false)
|
||||
ExportLine("DESCRIPTION:" + rem.Description);
|
||||
|
||||
ExportLine("TRIGGER;VALUE=DURATION:" + GetDuration(rem.ReminderTime - app.StartTime));
|
||||
ExportLine("ACTION:DISPLAY");
|
||||
ExportLine("END:VALARM");
|
||||
}
|
||||
}
|
||||
|
||||
if (app.StartTimeAction != eStartTimeAction.None)
|
||||
{
|
||||
ExportLine("\nBEGIN:VALARM");
|
||||
ExportLine("TRIGGER;VALUE=DURATION:PT0M");
|
||||
ExportLine("ACTION:DISPLAY");
|
||||
ExportLine("END:VALARM\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDuration
|
||||
|
||||
/// <summary>
|
||||
/// GetDuration
|
||||
/// </summary>
|
||||
/// <param name="ts"></param>
|
||||
/// <returns></returns>
|
||||
private string GetDuration(TimeSpan ts)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (ts.TotalMinutes < 0)
|
||||
sb.Append("-");
|
||||
|
||||
sb.Append("P");
|
||||
|
||||
int totalMinutes = (int)Math.Abs(ts.TotalMinutes);
|
||||
|
||||
int days = totalMinutes / (24 * 60);
|
||||
|
||||
if (days > 0)
|
||||
{
|
||||
sb.Append(days + "D");
|
||||
totalMinutes %= (24 * 60);
|
||||
}
|
||||
|
||||
if (totalMinutes > 0)
|
||||
{
|
||||
sb.Append("T");
|
||||
|
||||
int hours = totalMinutes / 60;
|
||||
|
||||
if (hours > 0)
|
||||
{
|
||||
sb.Append(hours + "H");
|
||||
totalMinutes %= 60;
|
||||
}
|
||||
|
||||
if (totalMinutes > 0)
|
||||
sb.Append(totalMinutes + "M");
|
||||
}
|
||||
|
||||
return (sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetUtcDate
|
||||
|
||||
/// <summary>
|
||||
/// GetUtcDate
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
private string GetUtcDate(DateTime dateTime)
|
||||
{
|
||||
DateTime date = dateTime.ToUniversalTime();
|
||||
|
||||
return (date.ToString("yyyyMMdd\\tHHmms\\z"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddMetaData
|
||||
|
||||
/// <summary>
|
||||
/// Adds escape chars to text meta data
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
private string AddMetaData(string s)
|
||||
{
|
||||
Regex p = new Regex("[,;\\n]");
|
||||
MatchCollection mc = p.Matches(s);
|
||||
|
||||
if (mc.Count > 0)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int index = 0;
|
||||
|
||||
foreach (Match ma in mc)
|
||||
{
|
||||
if (index < ma.Index)
|
||||
sb.Append(s.Substring(index, ma.Index - index));
|
||||
|
||||
index = ma.Index + ma.Length;
|
||||
|
||||
char c = ma.Value[0];
|
||||
|
||||
if (c == '\n')
|
||||
c = 'n';
|
||||
|
||||
sb.Append('\\');
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
if (index < s.Length)
|
||||
sb.Append(s.Substring(index, s.Length - index));
|
||||
|
||||
return (sb.ToString());
|
||||
}
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExportLine
|
||||
|
||||
/// <summary>
|
||||
/// Exports a line
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
private void ExportLine(string text)
|
||||
{
|
||||
_StreamWriter.WriteLine(WrapLine(text, FoldCount));
|
||||
}
|
||||
|
||||
#region WrapLine
|
||||
|
||||
/// <summary>
|
||||
/// Performs line wrapping (aka folding)
|
||||
/// </summary>
|
||||
/// <param name="text">Text to wrap</param>
|
||||
/// <param name="wrapLength">Wrapping length</param>
|
||||
/// <returns></returns>
|
||||
private string WrapLine(string text, int wrapLength)
|
||||
{
|
||||
if (text.Length <= wrapLength)
|
||||
return (text);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int len;
|
||||
|
||||
for (int i = 0; i < text.Length; i += len)
|
||||
{
|
||||
len = text.Length - i;
|
||||
|
||||
if (len > wrapLength)
|
||||
len = BreakLine(text, i, wrapLength);
|
||||
|
||||
if (i > 0)
|
||||
sb.Append("\n ");
|
||||
|
||||
sb.Append(text, i, len);
|
||||
}
|
||||
|
||||
return (sb.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BreakLine
|
||||
|
||||
/// <summary>
|
||||
/// Determines where to break a line of text
|
||||
/// </summary>
|
||||
/// <param name="text">Text</param>
|
||||
/// <param name="pos">Current text pos</param>
|
||||
/// <param name="max">Max line length</param>
|
||||
/// <returns></returns>
|
||||
private int BreakLine(string text, int pos, int max)
|
||||
{
|
||||
int i = max - 1;
|
||||
|
||||
while (i > 0 && IsBreakChar(text[pos + i]) == false)
|
||||
i--;
|
||||
|
||||
if (i <= 0)
|
||||
return (max);
|
||||
|
||||
while (i >= 0 && Char.IsWhiteSpace(text[pos + i]) == true)
|
||||
i--;
|
||||
|
||||
return (i + 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsBreakChar
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a char is a break char
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns>true if break char</returns>
|
||||
private bool IsBreakChar(char c)
|
||||
{
|
||||
return (Char.IsWhiteSpace(c) || c == ',' || c == ';');
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetDefaultOwnerKeys
|
||||
|
||||
/// <summary>
|
||||
/// GetDefaultOwnerKeys
|
||||
/// </summary>
|
||||
/// <returns>A list of all defined model OwnerKeys</returns>
|
||||
private string[] GetDefaultOwnerKeys()
|
||||
{
|
||||
List<string> list = new List<string>();
|
||||
|
||||
foreach (Appointment app in Model.Appointments)
|
||||
{
|
||||
string key = app.OwnerKey ?? "";
|
||||
|
||||
if (list.Contains(app.OwnerKey) == false)
|
||||
list.Add(key);
|
||||
}
|
||||
|
||||
list.Sort();
|
||||
|
||||
string[] ownerKeys = new string[list.Count];
|
||||
list.CopyTo(ownerKeys);
|
||||
|
||||
return (ownerKeys);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetUniversalTime
|
||||
|
||||
/// <summary>
|
||||
/// GetUniversalTime
|
||||
/// </summary>
|
||||
/// <param name="date"></param>
|
||||
/// <returns></returns>
|
||||
private string GetUniversalTime(DateTime date)
|
||||
{
|
||||
return (date.ToUniversalTime().ToString("yyyyMMdd\\tHHmms\\z"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsAppointmentVisible
|
||||
|
||||
/// <summary>
|
||||
/// Determines if an appointment is visible
|
||||
/// with respect to the given ownerKey
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="ownerKey"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool IsAppointmentVisible(Appointment app, string ownerKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ownerKey))
|
||||
return (true);
|
||||
|
||||
return (app.OwnerKey == ownerKey);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the calendar month.
|
||||
/// </summary>
|
||||
public class Month
|
||||
{
|
||||
#region Private Variables
|
||||
private int _Year = 0, _Month = 0;
|
||||
private ReadOnlyCollection<Day> _ReadOnlyDays = null;
|
||||
private List<Day> _Days = null;
|
||||
private CalendarModel _CalendarModel = null;
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Month class.
|
||||
/// </summary>
|
||||
/// <param name="year"></param>
|
||||
/// <param name="month"></param>
|
||||
public Month(CalendarModel calendar, int year, int month)
|
||||
{
|
||||
_CalendarModel = calendar;
|
||||
_Year = year;
|
||||
_Month = month;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets collection of days in this month.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Day> Days
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ReadOnlyDays == null)
|
||||
CreateDayCollection();
|
||||
return _ReadOnlyDays;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDayCollection()
|
||||
{
|
||||
Calendar cal = _CalendarModel.GetCalendar();
|
||||
int daysCount = cal.GetDaysInMonth(_Year, _Month);
|
||||
_Days = new List<Day>(daysCount);
|
||||
for (int i = 0; i < daysCount; i++)
|
||||
{
|
||||
Day day = new Day(new DateTime(_Year, _Month, i + 1), _CalendarModel);
|
||||
|
||||
_Days.Add(day);
|
||||
}
|
||||
_ReadOnlyDays = new ReadOnlyCollection<Day>(_Days);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the month year.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public int Year
|
||||
{
|
||||
get { return _Year; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the month.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public int MonthOfYear
|
||||
{
|
||||
get { return _Month; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Calendar this day is part of.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public CalendarModel CalendarModel
|
||||
{
|
||||
get { return _CalendarModel; }
|
||||
internal set { _CalendarModel = value; }
|
||||
}
|
||||
|
||||
internal void InvalidateAppointments()
|
||||
{
|
||||
if (_Days == null) return;
|
||||
|
||||
foreach (Day day in _Days)
|
||||
{
|
||||
day.InvalidateAppointments();
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvalidateAppointments(int day)
|
||||
{
|
||||
if (_Days == null) return;
|
||||
|
||||
_Days[day - 1].InvalidateAppointments();
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,195 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines monthly recurrence settings.
|
||||
/// </summary>
|
||||
public class MonthlyRecurrenceSettings : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
private AppointmentRecurrence _Recurrence = null;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the MonthlyRecurrenceSettings class.
|
||||
/// </summary>
|
||||
/// <param name="recurrence"></param>
|
||||
public MonthlyRecurrenceSettings(AppointmentRecurrence recurrence)
|
||||
{
|
||||
_Recurrence = recurrence;
|
||||
}
|
||||
|
||||
|
||||
private int _RepeatOnDayOfMonth = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the day of month on which appointment is repeated.
|
||||
/// When RepeatOnRelativeDayInMonth property is set to value other than None value of this property is not used.
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatOnDayOfMonth
|
||||
{
|
||||
get { return _RepeatOnDayOfMonth; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnDayOfMonth)
|
||||
{
|
||||
int oldValue = _RepeatOnDayOfMonth;
|
||||
_RepeatOnDayOfMonth = value;
|
||||
OnRepeatOnDayOfMonthChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnDayOfMonthChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnDayOfMonth"));
|
||||
}
|
||||
|
||||
|
||||
private eRelativeDayInMonth _RepeatOnRelativeDayInMonth = eRelativeDayInMonth.None;
|
||||
/// <summary>
|
||||
/// Gets or sets whether appointment should repeat on first, second, third, fourth or last day in month as specified
|
||||
/// by RepeatOnDayOfMonth property. Property applies only for RecurrenceType Monthly or Yearly.
|
||||
/// </summary>
|
||||
[DefaultValue(eRelativeDayInMonth.None)]
|
||||
public eRelativeDayInMonth RepeatOnRelativeDayInMonth
|
||||
{
|
||||
get { return _RepeatOnRelativeDayInMonth; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnRelativeDayInMonth)
|
||||
{
|
||||
eRelativeDayInMonth oldValue = _RepeatOnRelativeDayInMonth;
|
||||
_RepeatOnRelativeDayInMonth = value;
|
||||
OnRepeatOnRelativeDayInMonthChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnRelativeDayInMonthChanged(eRelativeDayInMonth oldValue, eRelativeDayInMonth newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnRelativeDayInMonth"));
|
||||
}
|
||||
|
||||
private int _RepeatInterval = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between recurring appointments. Default value is 1.
|
||||
/// <remarks>
|
||||
/// For example, setting RepeatInterval to 2 means that appointment will recur every 2 months.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatInterval
|
||||
{
|
||||
get { return _RepeatInterval; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatInterval)
|
||||
{
|
||||
int oldValue = _RepeatInterval;
|
||||
_RepeatInterval = value;
|
||||
OnRepeatIntervalChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatIntervalChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatInterval"));
|
||||
}
|
||||
|
||||
private DayOfWeek _RelativeDayOfWeek = DayOfWeek.Monday;
|
||||
/// <summary>
|
||||
/// Gets or sets the day of week on which relative repeat as specified by RepeatOnRelativeDayInMonth is effective.
|
||||
/// For example setting RepeatOnRelativeDayInMonth to First and RelativeDayOfWeek to Monday will repeat the appointment on first
|
||||
/// Monday in a month.
|
||||
/// </summary>
|
||||
[DefaultValue(DayOfWeek.Monday)]
|
||||
public DayOfWeek RelativeDayOfWeek
|
||||
{
|
||||
get { return _RelativeDayOfWeek; }
|
||||
set
|
||||
{
|
||||
if (value != _RelativeDayOfWeek)
|
||||
{
|
||||
DayOfWeek oldValue = _RelativeDayOfWeek;
|
||||
_RelativeDayOfWeek = value;
|
||||
OnRelativeDayOfWeekChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRelativeDayOfWeekChanged(DayOfWeek oldValue, DayOfWeek newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RelativeDayOfWeek"));
|
||||
}
|
||||
|
||||
private eMonthRecurrence _RepeatOnMonths = eMonthRecurrence.All;
|
||||
/// <summary>
|
||||
/// Gets or sets the months on which appointment is repeated. This property is represented by bit-flag enum
|
||||
/// which means that you can combine the values from eMonthRecurrence enum using OR operator to specify multiple values.
|
||||
/// Default value is All.
|
||||
/// <remarks>
|
||||
/// <para>For example you could set this property to eMonthRecurrence.January | eMonthRecurrence.July to repeat appointments on January and July only.</para>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DefaultValue(eMonthRecurrence.All)]
|
||||
public eMonthRecurrence RepeatOnMonths
|
||||
{
|
||||
get { return _RepeatOnMonths; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnMonths)
|
||||
{
|
||||
eMonthRecurrence oldValue = _RepeatOnMonths;
|
||||
_RepeatOnMonths = value;
|
||||
OnRepeatOnMonthsChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void OnRepeatOnMonthsChanged(eMonthRecurrence oldValue, eMonthRecurrence newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnMonths"));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,58 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
internal class NotificationRequest
|
||||
{
|
||||
#region Internal Implementation
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NotificationRequest class.
|
||||
/// </summary>
|
||||
/// <param name="notificationTime"></param>
|
||||
/// <param name="notificationHandler"></param>
|
||||
public NotificationRequest(DateTime notificationTime, NotificationServerEventHandler notificationHandler)
|
||||
{
|
||||
_NotificationTime = notificationTime;
|
||||
_NotificationHandler = notificationHandler;
|
||||
_ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
private int _ThreadId;
|
||||
public int ThreadId
|
||||
{
|
||||
get { return _ThreadId; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
private DateTime _NotificationTime = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets requested notification time.
|
||||
/// </summary>
|
||||
public DateTime NotificationTime
|
||||
{
|
||||
get { return _NotificationTime; }
|
||||
set
|
||||
{
|
||||
_NotificationTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationServerEventHandler _NotificationHandler = null;
|
||||
/// <summary>
|
||||
/// Gets the callback handler for notification when notification time is reached.
|
||||
/// </summary>
|
||||
public NotificationServerEventHandler NotificationHandler
|
||||
{
|
||||
get { return _NotificationHandler; }
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,173 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.Win32;
|
||||
using System.Timers;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides notification support for appointments.
|
||||
/// </summary>
|
||||
internal static class NotificationServer
|
||||
{
|
||||
#region Private Variables
|
||||
private static Timer _Timer = null;
|
||||
internal static event NotificationServerEventHandler NotifySubscribers;
|
||||
private static DateTime _NextEventTime;
|
||||
private static List<NotificationRequest> _RequestList = new List<NotificationRequest>();
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
static NotificationServer()
|
||||
{
|
||||
_Timer = new Timer();
|
||||
_Timer.Elapsed += new ElapsedEventHandler(NotificationEventTimerTick);
|
||||
SystemEvents.TimeChanged += new EventHandler(SystemEvents_TimeChanged);
|
||||
_NextEventTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
static void SystemEvents_TimeChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_NextEventTime > DateTime.MinValue && UtcNow.Subtract(_NextEventTime).TotalSeconds <= 0)
|
||||
NotifySubscribersOnTimeEvent();
|
||||
}
|
||||
|
||||
static void NotificationEventTimerTick(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
NotifySubscribersOnTimeEvent();
|
||||
}
|
||||
|
||||
private static bool _Notifying = false;
|
||||
private static void NotifySubscribersOnTimeEvent()
|
||||
{
|
||||
try
|
||||
{
|
||||
_Notifying = true;
|
||||
_Timer.Stop();
|
||||
#if DEBUG
|
||||
Console.WriteLine("{0} NotificationServer notifying subscribers count: {1}", DateTime.Now, _RequestList.Count);
|
||||
#endif
|
||||
// Reset next notification time...
|
||||
_NextEventTime = DateTime.MinValue;
|
||||
|
||||
if (_RequestList.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DateTime now = CalendarModel.GetCalendarDateTime(UtcNow);
|
||||
NotificationRequest[] requestList = new NotificationRequest[_RequestList.Count];
|
||||
_RequestList.CopyTo(requestList);
|
||||
DateTime nextNotification = DateTime.MinValue;
|
||||
foreach (NotificationRequest item in requestList)
|
||||
{
|
||||
if (item.NotificationTime <= now)
|
||||
{
|
||||
if (item.NotificationHandler != null)
|
||||
{
|
||||
//if (System.Threading.Thread.CurrentThread.ManagedThreadId != item.ThreadId)
|
||||
//{
|
||||
|
||||
// //System.Threading.SendOrPostCallback callback = new System.Threading.SendOrPostCallback(SendNotification);
|
||||
// //System.Windows.Threading.DispatcherSynchronizationContext.Current.Post(callback, item);
|
||||
//}
|
||||
//else
|
||||
item.NotificationHandler(item, new NotificationServerEventArgs(now));
|
||||
}
|
||||
_RequestList.Remove(item);
|
||||
}
|
||||
else if (item.NotificationTime < nextNotification || nextNotification == DateTime.MinValue)
|
||||
nextNotification = item.NotificationTime;
|
||||
}
|
||||
|
||||
if (nextNotification > DateTime.MinValue)
|
||||
{
|
||||
SetNextEventTime(nextNotification);
|
||||
}
|
||||
//else
|
||||
// Console.WriteLine("Not setting next notification time...");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_Notifying = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendNotification(object e)
|
||||
{
|
||||
NotificationRequest item = (NotificationRequest)e;
|
||||
if (item.NotificationHandler != null)
|
||||
{
|
||||
if (System.Threading.Thread.CurrentThread.ManagedThreadId != item.ThreadId)
|
||||
{
|
||||
Console.WriteLine("Different THREAD");
|
||||
}
|
||||
else
|
||||
{
|
||||
DateTime now = CalendarModel.GetCalendarDateTime(UtcNow);
|
||||
item.NotificationHandler(item, new NotificationServerEventArgs(now));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void SetNextEventTime(DateTime dateTime)
|
||||
{
|
||||
DateTime now = CalendarModel.GetCalendarDateTime(UtcNow);
|
||||
if (dateTime.Subtract(now).TotalSeconds <= 0)
|
||||
{
|
||||
if (!_Notifying)
|
||||
NotifySubscribersOnTimeEvent();
|
||||
else
|
||||
_NextEventTime = DateTime.MinValue;
|
||||
}
|
||||
else if (dateTime.Subtract(now).TotalSeconds > 0 && (_NextEventTime == DateTime.MinValue || dateTime < _NextEventTime))
|
||||
{
|
||||
double nextEvent = dateTime.Subtract(UtcNow).TotalSeconds + 1;
|
||||
if (nextEvent > int.MaxValue / 1000)
|
||||
nextEvent = (int.MaxValue - 1) / 1000;
|
||||
_Timer.Interval = (int)Math.Max(5, nextEvent * 1000);
|
||||
#if DEBUG
|
||||
Console.WriteLine("{0} NotificationServer scheduling next event on: {1}, previous scheduled event on: {2}", DateTime.Now, dateTime, _NextEventTime);
|
||||
#endif
|
||||
_Timer.Start();
|
||||
_NextEventTime = dateTime;
|
||||
}
|
||||
//else
|
||||
// _NextEventTime = DateTime.MinValue;
|
||||
}
|
||||
|
||||
private static DateTime UtcNow
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeZoneInfo.ConvertTimeToUtc(CalendarModel.CurrentDateTime);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateNotification(NotificationRequest request)
|
||||
{
|
||||
SetNextEventTime(request.NotificationTime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
public static void Register(NotificationRequest request)
|
||||
{
|
||||
_RequestList.Add(request);
|
||||
SetNextEventTime(request.NotificationTime);
|
||||
}
|
||||
|
||||
public static void Unregister(NotificationRequest request)
|
||||
{
|
||||
_RequestList.Remove(request);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal delegate void NotificationServerEventHandler(NotificationRequest sender, NotificationServerEventArgs e);
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,49 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
internal class NotificationServerEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the NotificationServerEventArgs class.
|
||||
/// </summary>
|
||||
/// <param name="notificationTime"></param>
|
||||
public NotificationServerEventArgs(DateTime notificationTime)
|
||||
{
|
||||
_NotificationTime = notificationTime;
|
||||
}
|
||||
|
||||
private DateTime _NotificationTime;
|
||||
/// <summary>
|
||||
/// Gets the time notification is sent on.
|
||||
/// </summary>
|
||||
public DateTime NotificationTime
|
||||
{
|
||||
get { return _NotificationTime; }
|
||||
}
|
||||
|
||||
private DateTime _NextRequestedNotificationTime = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets the next requested notification time by the handler of the event.
|
||||
/// Handler of event must set this to the desired next notification time in order to be notified.
|
||||
/// The value recorded will be the lowest value set by all handlers.
|
||||
/// </summary>
|
||||
public DateTime NextRequestedNotificationTime
|
||||
{
|
||||
get { return _NextRequestedNotificationTime; }
|
||||
set
|
||||
{
|
||||
value = CalendarModel.GetCalendarDateTime(value);
|
||||
if (value > NotificationTime && (value < _NextRequestedNotificationTime || _NextRequestedNotificationTime == DateTime.MinValue))
|
||||
{
|
||||
_NextRequestedNotificationTime = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,296 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
using DevComponents.DotNetBar.Schedule;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
public class Owner : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Owner class.
|
||||
/// </summary>
|
||||
public Owner()
|
||||
{
|
||||
_WorkDays = new WorkDayCollection(this);
|
||||
_CalendarWorkDays = new CalendarWorkDayCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Owner class.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public Owner(string key)
|
||||
: this()
|
||||
{
|
||||
_Key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Owner class.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="displayName"></param>
|
||||
public Owner(string key, string displayName)
|
||||
: this()
|
||||
{
|
||||
_Key = key;
|
||||
_DisplayName = displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Owner class.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="displayName"></param>
|
||||
/// <param name="colorScheme"></param>
|
||||
public Owner(string key, string displayName, eCalendarColor colorScheme)
|
||||
: this()
|
||||
{
|
||||
_Key = key;
|
||||
_ColorScheme = colorScheme;
|
||||
_DisplayName = displayName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Owner class.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="colorScheme"></param>
|
||||
public Owner(string key, eCalendarColor colorScheme)
|
||||
: this()
|
||||
{
|
||||
_Key = key;
|
||||
_ColorScheme = colorScheme;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
private WorkDayCollection _WorkDays;
|
||||
/// <summary>
|
||||
/// Gets working days associated with this owner. If empty WorkDays from CalendarModel are used instead.
|
||||
/// </summary>
|
||||
public WorkDayCollection WorkDays
|
||||
{
|
||||
get { return _WorkDays; }
|
||||
}
|
||||
|
||||
private CalendarWorkDayCollection _CalendarWorkDays;
|
||||
/// <summary>
|
||||
/// Gets date based working days associated with this owner. Date specific working days take precedence over days specified in WorkDays collection. If empty WorkDays on owner or from CalendarModel are used instead.
|
||||
/// </summary>
|
||||
public CalendarWorkDayCollection CalendarWorkDays
|
||||
{
|
||||
get { return _CalendarWorkDays; }
|
||||
}
|
||||
|
||||
private string _Key = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the unique key that identifies the owner.
|
||||
/// </summary>
|
||||
[DefaultValue("")]
|
||||
public string Key
|
||||
{
|
||||
get { return _Key; }
|
||||
set
|
||||
{
|
||||
if (value != _Key)
|
||||
{
|
||||
string oldValue = _Key;
|
||||
_Key = value;
|
||||
OnKeyChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnKeyChanged(string oldValue, string newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Key"));
|
||||
}
|
||||
|
||||
private string _Description = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the owner description. For example if owner represents person, it would be person name or if owner represents resource
|
||||
/// like room it would be the room name.
|
||||
/// </summary>
|
||||
[DefaultValue("")]
|
||||
public string Description
|
||||
{
|
||||
get { return _Description; }
|
||||
set
|
||||
{
|
||||
if (value != _Description)
|
||||
{
|
||||
string oldValue = _Description;
|
||||
_Description = value;
|
||||
OnDescriptionChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDescriptionChanged(string oldValue, string newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Description"));
|
||||
|
||||
}
|
||||
|
||||
private object _Tag = null;
|
||||
/// <summary>
|
||||
/// Gets or sets custom data associated with the object.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public object Tag
|
||||
{
|
||||
get { return _Tag; }
|
||||
set
|
||||
{
|
||||
if (value != _Tag)
|
||||
{
|
||||
object oldValue = _Tag;
|
||||
_Tag = value;
|
||||
OnTagChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTagChanged(object oldValue, object newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Tag"));
|
||||
}
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar owner is associated with.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set
|
||||
{
|
||||
if (_Calendar != value)
|
||||
{
|
||||
_Calendar = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TimeZoneInfo _DisplayTimeZone = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the display time-zone for this owner. Default value is null.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public TimeZoneInfo DisplayTimeZone
|
||||
{
|
||||
get { return _DisplayTimeZone; }
|
||||
set
|
||||
{
|
||||
if (value != _DisplayTimeZone)
|
||||
{
|
||||
TimeZoneInfo oldValue = _DisplayTimeZone;
|
||||
_DisplayTimeZone = value;
|
||||
OnDisplayTimeZoneChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisplayTimeZoneChanged(TimeZoneInfo oldValue, TimeZoneInfo newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DisplayTimeZone"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private eCalendarColor _ColorScheme = eCalendarColor.Automatic;
|
||||
/// <summary>
|
||||
/// Gets or sets the owner color scheme used to represent owner data in user interface.
|
||||
/// </summary>
|
||||
[DefaultValue(eCalendarColor.Automatic)]
|
||||
public eCalendarColor ColorScheme
|
||||
{
|
||||
get { return _ColorScheme; }
|
||||
set
|
||||
{
|
||||
if (value != _ColorScheme)
|
||||
{
|
||||
eCalendarColor oldValue = _ColorScheme;
|
||||
_ColorScheme = value;
|
||||
OnColorSchemeChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorSchemeChanged(eCalendarColor oldValue, eCalendarColor newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("ColorScheme"));
|
||||
|
||||
}
|
||||
private string _DisplayName = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the display name for the owner. Display name is used in User Interface to identify the owner.
|
||||
/// If not specified the Key is used instead in UI.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public string DisplayName
|
||||
{
|
||||
get { return _DisplayName; }
|
||||
set
|
||||
{
|
||||
if (value != _DisplayName)
|
||||
{
|
||||
string oldValue = _DisplayName;
|
||||
_DisplayName = value;
|
||||
OnDisplayNameChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisplayNameChanged(string oldValue, string newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DisplayName"));
|
||||
|
||||
}
|
||||
|
||||
internal static string DisplayNamePropertyName = "DisplayName";
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,123 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
public class OwnerCollection : Collection<Owner>
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentCollection class.
|
||||
/// </summary>
|
||||
/// <param name="calendar"></param>
|
||||
public OwnerCollection(CalendarModel calendar)
|
||||
{
|
||||
_Calendar = calendar;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Internal Implementation
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar collection is associated with.
|
||||
/// </summary>
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set { _Calendar = value; }
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
Owner owner = this[index];
|
||||
OnBeforeRemove(index, owner);
|
||||
base.RemoveItem(index);
|
||||
OnAfterRemove(index, owner);
|
||||
}
|
||||
|
||||
private void OnAfterRemove(int index, Owner owner)
|
||||
{
|
||||
_Calendar.OwnerRemoved(owner);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index, Owner owner)
|
||||
{
|
||||
owner.Calendar = null;
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, Owner item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
OnAfterInsert(index, item);
|
||||
}
|
||||
|
||||
private void OnAfterInsert(int index, Owner item)
|
||||
{
|
||||
_Calendar.OwnerAdded(item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, Owner item)
|
||||
{
|
||||
item.Calendar = _Calendar;
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, Owner newItem)
|
||||
{
|
||||
Owner oldItem = this[index];
|
||||
OnBeforeSetItem(index, oldItem, newItem);
|
||||
base.SetItem(index, newItem);
|
||||
OnAfterSetItem(index, oldItem, newItem);
|
||||
}
|
||||
|
||||
private void OnAfterSetItem(int index, Owner oldItem, Owner newItem)
|
||||
{
|
||||
_Calendar.OwnerRemoved(oldItem);
|
||||
_Calendar.OwnerAdded(newItem);
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, Owner oldItem, Owner newItem)
|
||||
{
|
||||
oldItem.Calendar = null;
|
||||
newItem.Calendar = _Calendar;
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (Owner item in this)
|
||||
{
|
||||
item.Calendar = null;
|
||||
_Calendar.OwnerRemoved(item);
|
||||
}
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item based on the Key assigned to the item
|
||||
/// </summary>
|
||||
/// <param name="ownerKey"></param>
|
||||
/// <returns></returns>
|
||||
public Owner this[string ownerKey]
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (Owner item in this.Items)
|
||||
{
|
||||
if (item.Key == ownerKey) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
//set
|
||||
//{
|
||||
// Owner owner = this[ownerKey];
|
||||
// this[IndexOf(owner)] = value;
|
||||
//}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,600 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
internal class RecurrenceGenerator : IRecurrenceGenerator
|
||||
{
|
||||
|
||||
#region IRecurrenceGenerator Members
|
||||
|
||||
/// <summary>
|
||||
/// Generates Daily recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Daily recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
public void GenerateDailyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
Appointment app = recurrence.Appointment;
|
||||
int appointmentDaysDuration = recurrence.Daily.ExplicitDailyRecurrence ? 0 : (int)Math.Max(0, Math.Ceiling(app.EndTime.Date.Subtract(app.StartTime.Date).TotalDays));
|
||||
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate,
|
||||
recurrence.Daily.ExplicitDailyRecurrence ? (app.StartTime.AddDays(1).Date) : (DateTimeHelper.IsBeginningOfDay(app.EndTime) ? app.EndTime : app.EndTime.AddDays(recurrence.Daily.RepeatInterval).Date));
|
||||
|
||||
if (recurrenceStartDate > endDate) return;
|
||||
|
||||
int repeats = 0;
|
||||
|
||||
// Check the range first
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
||||
{
|
||||
if (startDate > recurrence.RangeEndDate) return;
|
||||
}
|
||||
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences)
|
||||
{
|
||||
DateTime rangeStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate,
|
||||
recurrence.Daily.ExplicitDailyRecurrence ? (app.LocalStartTime.AddDays(1).Date) : (DateTimeHelper.IsBeginningOfDay(app.LocalEndTime) ? app.LocalEndTime : app.LocalEndTime.AddDays(recurrence.Daily.RepeatInterval).Date));
|
||||
int totalDays = (int)Math.Ceiling(startDate.Subtract(rangeStartDate).TotalDays);
|
||||
|
||||
switch (recurrence.Daily.RepeatOnDaysOfWeek)
|
||||
{
|
||||
case eDailyRecurrenceRepeat.All:
|
||||
repeats = Math.Max(0, totalDays / (recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
||||
|
||||
if (repeats >= recurrence.RangeNumberOfOccurrences)
|
||||
return;
|
||||
break;
|
||||
|
||||
case eDailyRecurrenceRepeat.WeekDays:
|
||||
repeats = Math.Max(0, DateTimeHelper.TotalWeekDays(recurrenceStartDate, startDate) / (recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
||||
|
||||
// Assume weekdays repeat
|
||||
if (repeats > recurrence.RangeNumberOfOccurrences)
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
repeats = Math.Max(0, DateTimeHelper.TotalWeekDays(recurrenceStartDate, startDate));
|
||||
repeats = Math.Max(0, totalDays - repeats);
|
||||
|
||||
// Assume weekend days repeat
|
||||
if (repeats > recurrence.RangeNumberOfOccurrences)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
//repeats = 0;
|
||||
}
|
||||
|
||||
DateTime currentDay = recurrenceStartDate; // DateTimeHelper.MaxDate(startDate, recurrenceStartDate);
|
||||
|
||||
while (currentDay <= endDate)
|
||||
{
|
||||
if (currentDay >= startDate || currentDay < startDate && IsRecurringOnDay(currentDay, startDate, endDate, app))
|
||||
{
|
||||
if (!IsRecurringOnDay(currentDay, startDate, endDate, app)) break;
|
||||
if (recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.All ||
|
||||
(recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.WeekDays && !DateTimeHelper.IsWeekendDay(currentDay)) ||
|
||||
(recurrence.Daily.RepeatOnDaysOfWeek == eDailyRecurrenceRepeat.WeekendDays && DateTimeHelper.IsWeekendDay(currentDay)))
|
||||
{
|
||||
if (!IsIgnoredRecurrence(currentDay, recurrence))
|
||||
{
|
||||
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
||||
repeats++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (recurrence.Daily.RepeatOnDaysOfWeek)
|
||||
{
|
||||
case eDailyRecurrenceRepeat.All:
|
||||
currentDay = currentDay.AddDays(recurrence.Daily.RepeatInterval + appointmentDaysDuration);
|
||||
break;
|
||||
|
||||
case eDailyRecurrenceRepeat.WeekDays:
|
||||
//currentDay = currentDay.AddDays(currentDay.DayOfWeek == DayOfWeek.Saturday ? 2 : Math.Max(1, recurrence.Daily.RepeatInterval + appointmentDaysDuration));
|
||||
currentDay = currentDay.AddDays(Math.Max(1, recurrence.Daily.RepeatInterval + appointmentDaysDuration)); // Changed for consistency
|
||||
break;
|
||||
|
||||
case eDailyRecurrenceRepeat.WeekendDays:
|
||||
while (true)
|
||||
{
|
||||
currentDay = currentDay.AddDays(1);
|
||||
|
||||
if (DateTimeHelper.IsWeekendDay(currentDay) == true)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay >= recurrence.RangeEndDate ||
|
||||
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && repeats >= recurrence.RangeNumberOfOccurrences)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsIgnoredRecurrence(DateTime currentDay, AppointmentRecurrence recurrence)
|
||||
{
|
||||
if (recurrence.SkippedRecurrences.Count == 0) return false;
|
||||
foreach (DateTime date in recurrence.SkippedRecurrences)
|
||||
{
|
||||
if (date.Date == currentDay.Date)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsRecurringOnDay(DateTime currentDay, DateTime rangeStart, DateTime rangeEnd, Appointment rootAppointment)
|
||||
{
|
||||
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
||||
DateTime startTime = rootAppointment.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
||||
DateTime endTime = rootAppointment.GetValidDateTime(startTime.Add(duration));
|
||||
|
||||
DateTime localStartTime = rootAppointment.GetLocalDateTime(rootAppointment.GetUTCDateTime(startTime));
|
||||
DateTime localEndTime = rootAppointment.GetLocalDateTime(rootAppointment.GetUTCDateTime(endTime));
|
||||
|
||||
// If time-zone is not used return false
|
||||
//if (startTime == localStartTime) return currentDay >= rangeStart;
|
||||
|
||||
return DateTimeHelper.TimePeriodsOverlap(localStartTime, localEndTime, rangeStart, rangeEnd);
|
||||
}
|
||||
|
||||
private DateTime GetAppointmentEndTime(DateTime currentDay, Appointment rootAppointment)
|
||||
{
|
||||
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
||||
DateTime startTime = rootAppointment.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
||||
DateTime endTime = rootAppointment.GetValidDateTime(startTime.Add(duration));
|
||||
|
||||
return endTime;
|
||||
}
|
||||
|
||||
private Appointment CreateRecurringAppointmentInstance(DateTime currentDay, Appointment rootAppointment)
|
||||
{
|
||||
Appointment app = rootAppointment.Copy();
|
||||
TimeSpan duration = rootAppointment.EndTime.Subtract(rootAppointment.StartTime);
|
||||
DateTime startTime = app.GetValidDateTime(new DateTime(currentDay.Year, currentDay.Month, currentDay.Day, rootAppointment.StartTime.Hour, rootAppointment.StartTime.Minute, 0));
|
||||
DateTime endTime = app.GetValidDateTime(startTime.Add(duration));
|
||||
|
||||
app.StartTime = startTime;
|
||||
app.EndTime = endTime;
|
||||
app.IsRecurringInstance = true;
|
||||
app.RootAppointment = rootAppointment;
|
||||
app.Tooltip = rootAppointment.Tooltip;
|
||||
if (rootAppointment.Recurrence == null || !rootAppointment.Recurrence.IndependentVisibility)
|
||||
app.Visible = rootAppointment.Visible;
|
||||
|
||||
foreach (Reminder reminder in app.Reminders)
|
||||
{
|
||||
//reminder.ReminderTime = app.StartTime.Add(reminder.ReminderTime.Subtract(rootAppointment.StartTime));
|
||||
reminder.IsActive = true;
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates Weekly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Weekly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
public void GenerateWeeklyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
Appointment app = recurrence.Appointment;
|
||||
|
||||
DateTime baseStartDate = app.EndTime.Date;
|
||||
if (app.EndTime.Date > app.StartTime.Date && app.EndTime.TimeOfDay > app.StartTime.TimeOfDay && baseStartDate != DateTime.MinValue)
|
||||
baseStartDate = baseStartDate.AddDays(1);
|
||||
DateTime recurrenceStartDate = baseStartDate;
|
||||
int repeatInterval = recurrence.Weekly.RepeatInterval;
|
||||
|
||||
if (RepeatsOnSingleDayOnly(recurrence.Weekly.RepeatOnDaysOfWeek) && !(app.StartTime.Date == startDate.Date && endDate.Date == startDate.Date))
|
||||
{
|
||||
recurrenceStartDate = baseStartDate.AddMilliseconds(-1).AddDays((repeatInterval) * 7 /*+ (recurrence.Weekly.RepeatInterval > 1 ? (8 - (int)baseStartDate.DayOfWeek) : 1)*/).Date;
|
||||
if (recurrenceStartDate.DayOfWeek != GetDayOfWeek(recurrence.Weekly.RepeatOnDaysOfWeek))
|
||||
{
|
||||
DayOfWeek dayOfWeek = GetDayOfWeek(recurrence.Weekly.RepeatOnDaysOfWeek);
|
||||
while (recurrenceStartDate.DayOfWeek != dayOfWeek)
|
||||
recurrenceStartDate = recurrenceStartDate.AddDays(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (app.EndTime.Date > app.StartTime.Date && app.EndTime.TimeOfDay < app.StartTime.TimeOfDay)
|
||||
recurrenceStartDate = app.StartTime.Date.AddDays(1);
|
||||
else
|
||||
recurrenceStartDate = app.EndTime.Date.AddDays(1);
|
||||
}
|
||||
|
||||
recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, recurrenceStartDate);
|
||||
|
||||
if (recurrenceStartDate > endDate) return;
|
||||
|
||||
int totalRecurrences = 0;
|
||||
// Check the range first
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
||||
{
|
||||
if (startDate > recurrence.RangeEndDate) return;
|
||||
}
|
||||
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && repeatInterval == 1)
|
||||
{
|
||||
int totalDays = (int)Math.Ceiling(endDate.Subtract(recurrenceStartDate).TotalDays);
|
||||
int occurrencesPerWeek = GetNumberOfDays(recurrence.Weekly.RepeatOnDaysOfWeek);
|
||||
if (occurrencesPerWeek == 0)
|
||||
throw new ArgumentException("Weekly recurrence must have at least single day selected using RepeatOnDaysOfWeek property.");
|
||||
totalRecurrences = DateTimeHelper.TotalNumberOfDays(recurrenceStartDate, endDate, recurrence.Weekly.RepeatOnDaysOfWeek);
|
||||
if (totalRecurrences > recurrence.RangeNumberOfOccurrences) return;
|
||||
}
|
||||
|
||||
bool countTotalRecurrences = false;
|
||||
if (repeatInterval > 1 && recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences)
|
||||
countTotalRecurrences = true;
|
||||
|
||||
bool spansDays = app.StartTime.Day != app.EndTime.Day && endDate.Subtract(startDate).TotalDays <= 1;
|
||||
|
||||
DateTime currentDay = recurrenceStartDate;// DateTimeHelper.MaxDate(recurrenceStartDate, startDate);
|
||||
while (currentDay <= endDate)
|
||||
{
|
||||
if ((currentDay >= startDate || spansDays && GetAppointmentEndTime(currentDay, app) >= startDate) && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek) &&
|
||||
!IsIgnoredRecurrence(currentDay, recurrence))
|
||||
{
|
||||
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
||||
totalRecurrences++;
|
||||
}
|
||||
else if (countTotalRecurrences && currentDay < startDate && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
||||
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
||||
{
|
||||
totalRecurrences++;
|
||||
if (totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentDay.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
while (currentDay.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
currentDay = currentDay.AddDays(1);
|
||||
if (currentDay > endDate || recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay > recurrence.RangeEndDate)
|
||||
break;
|
||||
if ((currentDay >= startDate || spansDays && GetAppointmentEndTime(currentDay, app) >= startDate) && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
||||
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
||||
{
|
||||
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDay, app));
|
||||
}
|
||||
else if (countTotalRecurrences && currentDay < startDate && DateTimeHelper.HasDay(currentDay.DayOfWeek, recurrence.Weekly.RepeatOnDaysOfWeek)
|
||||
&& !IsIgnoredRecurrence(currentDay, recurrence))
|
||||
{
|
||||
totalRecurrences++;
|
||||
if (totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
currentDay = currentDay.AddDays(1 + (repeatInterval - 1) * 7);
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDay > recurrence.RangeEndDate ||
|
||||
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences > recurrence.RangeNumberOfOccurrences)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private DayOfWeek GetDayOfWeek(eDayOfWeekRecurrence d)
|
||||
{
|
||||
if (d == eDayOfWeekRecurrence.Monday)
|
||||
return DayOfWeek.Monday;
|
||||
else if (d == eDayOfWeekRecurrence.Tuesday)
|
||||
return DayOfWeek.Tuesday;
|
||||
else if (d == eDayOfWeekRecurrence.Wednesday)
|
||||
return DayOfWeek.Wednesday;
|
||||
else if (d == eDayOfWeekRecurrence.Thursday)
|
||||
return DayOfWeek.Thursday;
|
||||
else if (d == eDayOfWeekRecurrence.Friday)
|
||||
return DayOfWeek.Friday;
|
||||
else if (d == eDayOfWeekRecurrence.Saturday)
|
||||
return DayOfWeek.Saturday;
|
||||
else
|
||||
return DayOfWeek.Sunday;
|
||||
}
|
||||
|
||||
private bool RepeatsOnSingleDayOnly(eDayOfWeekRecurrence dow)
|
||||
{
|
||||
return dow == eDayOfWeekRecurrence.Monday || dow == eDayOfWeekRecurrence.Tuesday ||
|
||||
dow == eDayOfWeekRecurrence.Wednesday || dow == eDayOfWeekRecurrence.Thursday ||
|
||||
dow == eDayOfWeekRecurrence.Friday || dow == eDayOfWeekRecurrence.Saturday || dow == eDayOfWeekRecurrence.Sunday;
|
||||
}
|
||||
|
||||
private int GetNumberOfDays(eDayOfWeekRecurrence daysOfWeek)
|
||||
{
|
||||
int days = 0;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Friday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Monday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Saturday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Sunday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Thursday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Tuesday) != 0)
|
||||
days++;
|
||||
if ((daysOfWeek & eDayOfWeekRecurrence.Wednesday) != 0)
|
||||
days++;
|
||||
|
||||
return days;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates Monthly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
public void GenerateMonthlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
Appointment app = recurrence.Appointment;
|
||||
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, app.EndTime/*.AddDays(1).Date*/);
|
||||
if (recurrenceStartDate > endDate) return;
|
||||
|
||||
int totalRecurrences = 0;
|
||||
// Check the range first
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
||||
{
|
||||
if (startDate > recurrence.RangeEndDate) return;
|
||||
}
|
||||
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && recurrence.RangeNumberOfOccurrences > 0)
|
||||
{
|
||||
int count = recurrence.RangeNumberOfOccurrences + 1;
|
||||
DateTime testDate = DateTimeHelper.BeginningOfDay(recurrenceStartDate);
|
||||
DateTime startDateOnly = DateTimeHelper.BeginningOfDay(startDate);
|
||||
while (count > 0 && testDate < startDateOnly)
|
||||
{
|
||||
int repeatInterval = recurrence.Monthly.RepeatInterval;
|
||||
while (repeatInterval > 0)
|
||||
{
|
||||
testDate = GetNextMonthlyRecurrence(recurrence, testDate);
|
||||
repeatInterval--;
|
||||
}
|
||||
if (testDate < startDate)
|
||||
totalRecurrences++;
|
||||
count--;
|
||||
}
|
||||
if (count == 0) return;
|
||||
}
|
||||
|
||||
DateTime currentDate = recurrenceStartDate;// DateTimeHelper.MaxDate(recurrenceStartDate, startDate).AddMonths(-1);
|
||||
|
||||
do
|
||||
{
|
||||
int repeatCount = recurrence.Monthly.RepeatInterval;
|
||||
do
|
||||
{
|
||||
repeatCount--;
|
||||
currentDate = GetNextMonthlyRecurrence(recurrence, currentDate);
|
||||
} while (repeatCount > 0);
|
||||
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDate > recurrence.RangeEndDate ||
|
||||
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
||||
break;
|
||||
|
||||
if (currentDate >= startDate && currentDate <= endDate && !IsIgnoredRecurrence(currentDate, recurrence))
|
||||
{
|
||||
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDate, app));
|
||||
totalRecurrences++;
|
||||
}
|
||||
} while (currentDate <= endDate);
|
||||
}
|
||||
|
||||
private DateTime GetNextMonthlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
||||
{
|
||||
MonthlyRecurrenceSettings monthly = recurrence.Monthly;
|
||||
return GetNextMonthlyRecurrence(startDate, monthly.RepeatOnRelativeDayInMonth,
|
||||
monthly.RelativeDayOfWeek, monthly.RepeatOnDayOfMonth, monthly.RepeatOnMonths);
|
||||
}
|
||||
|
||||
private static void AdjustForRepeatOnMonths(ref DateTime dt, eMonthRecurrence repeatOnMonths)
|
||||
{
|
||||
while (((int)Math.Pow(2, (dt.Month - 1)) & (int)repeatOnMonths) == 0)
|
||||
dt = dt.AddMonths(1);
|
||||
}
|
||||
private static DateTime GetNextMonthlyRecurrence(DateTime startDate, eRelativeDayInMonth repeatOnRelativeDayInMonth, DayOfWeek relativeDayOfWeek, int repeatOnDayOfMonth, eMonthRecurrence repeatOnMonths)
|
||||
{
|
||||
Calendar cal = GetCalendar();
|
||||
|
||||
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.None)
|
||||
{
|
||||
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
||||
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
||||
return dt.AddDays(Math.Min(cal.GetDaysInMonth(dt.Year, dt.Month), repeatOnDayOfMonth) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.First)
|
||||
{
|
||||
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
||||
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
||||
while (dt.DayOfWeek != relativeDayOfWeek)
|
||||
dt = dt.AddDays(1);
|
||||
return dt;
|
||||
}
|
||||
else if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Last)
|
||||
{
|
||||
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day + 1);
|
||||
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
||||
dt = dt.AddDays(cal.GetDaysInMonth(dt.Year, dt.Month) - 1);
|
||||
while (dt.DayOfWeek != relativeDayOfWeek)
|
||||
dt = dt.AddDays(-1);
|
||||
return dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Second, third and forth
|
||||
int relCount = 2;
|
||||
if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Third)
|
||||
relCount = 3;
|
||||
else if (repeatOnRelativeDayInMonth == eRelativeDayInMonth.Fourth)
|
||||
relCount = 4;
|
||||
DateTime dt = startDate.AddDays(cal.GetDaysInMonth(startDate.Year, startDate.Month) - startDate.Day);
|
||||
if (repeatOnMonths != eMonthRecurrence.All) AdjustForRepeatOnMonths(ref dt, repeatOnMonths); // Adjust for repeat on Months
|
||||
while (relCount > 0)
|
||||
{
|
||||
dt = dt.AddDays(1);
|
||||
if (dt.DayOfWeek == relativeDayOfWeek)
|
||||
relCount--;
|
||||
}
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Could not find the next relative date for monthly recurrence starting on " + startDate.ToString() + " RelativeDayInMonth=" + repeatOnRelativeDayInMonth.ToString() + " RelativeDayOfWeek=" + relativeDayOfWeek.ToString());
|
||||
}
|
||||
|
||||
private static Calendar GetCalendar()
|
||||
{
|
||||
return CultureInfo.CurrentCulture.Calendar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates Yearly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
public void GenerateYearlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
Appointment app = recurrence.Appointment;
|
||||
DateTime recurrenceStartDate = DateTimeHelper.MaxDate(recurrence.RecurrenceStartDate, app.EndTime.AddDays(1).Date);
|
||||
if (recurrenceStartDate > endDate) return;
|
||||
|
||||
if (!(startDate.Month <= recurrence.Yearly.RepeatOnMonth && endDate.Month >= recurrence.Yearly.RepeatOnMonth) && endDate.Subtract(startDate).TotalDays < 28)
|
||||
return;
|
||||
int totalRecurrences = 0;
|
||||
// Check the range first
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate)
|
||||
{
|
||||
if (startDate > recurrence.RangeEndDate) return;
|
||||
}
|
||||
else if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && recurrence.RangeNumberOfOccurrences > 0 && recurrenceStartDate < startDate)
|
||||
{
|
||||
int count = recurrence.RangeNumberOfOccurrences + 1;
|
||||
DateTime testDate = DateTimeHelper.BeginningOfDay(recurrenceStartDate.AddDays(-(recurrenceStartDate.Day - 1))).AddMonths(-1);
|
||||
if (testDate < app.EndTime)
|
||||
testDate = app.EndTime.Date.AddDays(1);
|
||||
DateTime startDateOnly = DateTimeHelper.BeginningOfDay(startDate);
|
||||
while (count > 0 && testDate < startDateOnly)
|
||||
{
|
||||
testDate = GetNextYearlyRecurrence(recurrence, testDate);
|
||||
if (testDate >= startDateOnly) break;
|
||||
count--;
|
||||
totalRecurrences++;
|
||||
}
|
||||
if (count == 0) return;
|
||||
}
|
||||
|
||||
DateTime currentDate = DateTimeHelper.MaxDate(startDate, recurrenceStartDate).AddDays(-1);
|
||||
if (recurrence.Yearly.RepeatInterval > 1)
|
||||
currentDate = recurrenceStartDate;
|
||||
do
|
||||
{
|
||||
currentDate = GetNextYearlyRecurrence(recurrence, currentDate);
|
||||
|
||||
if (recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeEndDate && currentDate > recurrence.RangeEndDate ||
|
||||
recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && totalRecurrences >= recurrence.RangeNumberOfOccurrences)
|
||||
break;
|
||||
|
||||
if (currentDate >= startDate && currentDate <= endDate && !IsIgnoredRecurrence(currentDate, recurrence))
|
||||
{
|
||||
subsetCollection.Add(CreateRecurringAppointmentInstance(currentDate, app));
|
||||
totalRecurrences++;
|
||||
}
|
||||
} while (currentDate <= endDate);
|
||||
}
|
||||
internal static DateTime GetNextYearlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
||||
{
|
||||
YearlyRecurrenceSettings yearly = recurrence.Yearly;
|
||||
if (yearly.RepeatInterval > 1)
|
||||
{
|
||||
DateTime currentDate = startDate;
|
||||
for (int i = 0; i < yearly.RepeatInterval; i++)
|
||||
{
|
||||
currentDate = GetNextSingleYearlyRecurrence(recurrence, currentDate);
|
||||
}
|
||||
return currentDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetNextSingleYearlyRecurrence(recurrence, startDate);
|
||||
}
|
||||
}
|
||||
internal static DateTime GetNextSingleYearlyRecurrence(AppointmentRecurrence recurrence, DateTime startDate)
|
||||
{
|
||||
YearlyRecurrenceSettings yearly = recurrence.Yearly;
|
||||
Calendar cal = GetCalendar();
|
||||
|
||||
if (startDate.Month < yearly.RepeatOnMonth)
|
||||
{
|
||||
startDate = startDate.AddMonths((yearly.RepeatOnMonth - startDate.Month) - 1);
|
||||
return GetNextMonthlyRecurrence(startDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
||||
}
|
||||
else if (startDate.Month == yearly.RepeatOnMonth)
|
||||
{
|
||||
DateTime refDate = startDate.AddDays(-(startDate.Day));
|
||||
DateTime ret = GetNextMonthlyRecurrence(refDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
||||
if (ret > startDate) return ret;
|
||||
}
|
||||
|
||||
// Forward to next year and right month
|
||||
startDate = startDate.AddDays(cal.GetDaysInYear(startDate.Year) - startDate.DayOfYear).AddMonths(yearly.RepeatOnMonth - 1);
|
||||
return GetNextMonthlyRecurrence(startDate, yearly.RepeatOnRelativeDayInMonth, yearly.RelativeDayOfWeek, yearly.RepeatOnDayOfMonth, eMonthRecurrence.All);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal interface IRecurrenceGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates Daily recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Daily recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
void GenerateDailyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
||||
|
||||
/// <summary>
|
||||
/// Generates Weekly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Weekly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
void GenerateWeeklyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
||||
|
||||
/// <summary>
|
||||
/// Generates Monthly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
void GenerateMonthlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
||||
|
||||
/// <
|
||||
/// summary>
|
||||
/// Generates Yearly recurring appointments. If appointment is assigned to calendar method must populate the Calendar.Appointments collection as well.
|
||||
/// </summary>
|
||||
/// <param name="subsetCollection">Collection to add generated recurrences to</param>
|
||||
/// <param name="recurrence">Recurrence description, must be of Monthly recurrence type.</param>
|
||||
/// <param name="startDate">Start date for generation.</param>
|
||||
/// <param name="endDate">End date for generation.</param>
|
||||
void GenerateYearlyRecurrence(AppointmentSubsetCollection subsetCollection, AppointmentRecurrence recurrence, DateTime startDate, DateTime endDate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,482 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an appointment reminder.
|
||||
/// </summary>
|
||||
public class Reminder
|
||||
{
|
||||
#region Private variables
|
||||
private bool _NotificationRequestRegistered = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Reminder class.
|
||||
/// </summary>
|
||||
public Reminder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Reminder class.
|
||||
/// </summary>
|
||||
/// <param name="reminderTime"></param>
|
||||
public Reminder(DateTime reminderTime)
|
||||
{
|
||||
ReminderTime = reminderTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Reminder class.
|
||||
/// </summary>
|
||||
/// <param name="description"></param>
|
||||
/// <param name="reminderTime"></param>
|
||||
public Reminder(string description, DateTime reminderTime)
|
||||
{
|
||||
Description = description;
|
||||
ReminderTime = reminderTime;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
/// <summary>
|
||||
/// Occurs when ReminderTime has been reached. Note that event handler will be called on the thread of System.Timer which is different
|
||||
/// than UI thread. You should use BeginInvoke calls to marshal the calls to your UI thread.
|
||||
/// </summary>
|
||||
[Description("Occurs when ReminderTime has been reached.")]
|
||||
public event ReminderEventHandler ReminderNotification;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
private bool _IsActiveForPastAppointments = true;
|
||||
/// <summary>
|
||||
/// Gets or sets whether reminder will be active for appointments that are in the past. Default value is true.
|
||||
/// This property is useful if you are creating recurring appointments with reminders that start in past but don't want reminders
|
||||
/// for past instances of appointment to be active.
|
||||
/// </summary>
|
||||
public bool IsActiveForPastAppointments
|
||||
{
|
||||
get { return _IsActiveForPastAppointments; }
|
||||
set
|
||||
{
|
||||
if (value != _IsActiveForPastAppointments)
|
||||
{
|
||||
bool oldValue = _IsActiveForPastAppointments;
|
||||
_IsActiveForPastAppointments = value;
|
||||
OnIsActiveForPastAppointmentsChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsActiveForPastAppointmentsChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
//OnPropertyChanged(new PropertyChangedEventArgs("IsActiveForPastAppointments"));
|
||||
ReminderCollection parentCollection = this.ParentCollection;
|
||||
|
||||
if (parentCollection != null)
|
||||
{
|
||||
Appointment app = this.ParentCollection.Appointment;
|
||||
if (app != null)
|
||||
{
|
||||
UpdateNotifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _Description = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the reminder description.
|
||||
/// </summary>
|
||||
public string Description
|
||||
{
|
||||
get { return _Description; }
|
||||
set
|
||||
{
|
||||
if (value != _Description)
|
||||
{
|
||||
string oldValue = _Description;
|
||||
_Description = value;
|
||||
OnDescriptionChanged(oldValue, _Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDescriptionChanged(string oldValue, string newValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private object _Tag = null;
|
||||
/// <summary>
|
||||
/// Gets or sets additional data associated with the object.
|
||||
/// </summary>
|
||||
[DefaultValue(null)]
|
||||
public object Tag
|
||||
{
|
||||
get { return _Tag; }
|
||||
set
|
||||
{
|
||||
if (value != _Tag)
|
||||
{
|
||||
object oldValue = _Tag;
|
||||
_Tag = value;
|
||||
OnTagChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTagChanged(object oldValue, object newValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time reminder will be executed at.
|
||||
/// <remarks>
|
||||
/// Unless you mark reminder as inactive by setting the IsActive=false the reminder will occur next time
|
||||
/// notifications are updated or when appointment data is loaded.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
private DateTime _ReminderTime = DateTime.MinValue;
|
||||
public DateTime ReminderTime
|
||||
{
|
||||
get { return GetTimeZoneDateTime(_ReminderTime); }
|
||||
set
|
||||
{
|
||||
value = GetUTCDateTime(CalendarModel.GetCalendarDateTime(value));
|
||||
if (_ReminderTime != value)
|
||||
{
|
||||
DateTime oldValue = _ReminderTime;
|
||||
_ReminderTime = value;
|
||||
OnReminderTimeChanged(oldValue, _ReminderTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime GetUTCDateTime(DateTime date)
|
||||
{
|
||||
if (date == DateTime.MinValue || date == DateTime.MaxValue || date.Kind == DateTimeKind.Utc)
|
||||
return date;
|
||||
return TimeZoneInfo.ConvertTimeToUtc(date);
|
||||
}
|
||||
|
||||
private DateTime GetTimeZoneDateTime(DateTime date)
|
||||
{
|
||||
if (date.Kind != DateTimeKind.Utc) return date;
|
||||
TimeZoneInfo zone = TimeZoneInfo.Local;
|
||||
return TimeZoneInfo.ConvertTimeFromUtc(date, zone);
|
||||
}
|
||||
|
||||
private void OnReminderTimeChanged(DateTime oldValue, DateTime newValue)
|
||||
{
|
||||
if (NeedsNotification)
|
||||
UpdateNotifications();
|
||||
}
|
||||
|
||||
private bool NeedsNotification
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ReminderAction != eReminderAction.None && _IsActive;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action performed when reminder time is reached. Default value is Event and Command.
|
||||
/// </summary>
|
||||
private eReminderAction _ReminderAction = eReminderAction.EventAndCommand;
|
||||
[DefaultValue(eReminderAction.EventAndCommand)]
|
||||
public eReminderAction ReminderAction
|
||||
{
|
||||
get { return _ReminderAction; }
|
||||
set
|
||||
{
|
||||
if (_ReminderAction != value)
|
||||
{
|
||||
eReminderAction oldValue = _ReminderAction;
|
||||
_ReminderAction = value;
|
||||
OnReminderActionChanged(oldValue, _ReminderAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReminderActionChanged(eReminderAction oldValue, eReminderAction newValue)
|
||||
{
|
||||
if (oldValue == eReminderAction.None || newValue == eReminderAction.None)
|
||||
UpdateNotifications();
|
||||
}
|
||||
|
||||
internal void UpdateNotifications()
|
||||
{
|
||||
if (_Appointment != null && !_IsActiveForPastAppointments && _Appointment.StartTime.Date < DateTime.Today)
|
||||
{
|
||||
UnregisterNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_Appointment != null && _Appointment.Calendar != null || _Parent != null && _Parent.ParentModel != null) &&
|
||||
_IsActive && _ReminderTime > DateTime.MinValue && _ReminderAction != eReminderAction.None)
|
||||
{
|
||||
NotificationRequest request = this.NotificationRequest;
|
||||
RegisterNotification(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnregisterNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterNotification(NotificationRequest request)
|
||||
{
|
||||
if (_NotificationRequestRegistered)
|
||||
NotificationServer.UpdateNotification(request);
|
||||
else
|
||||
NotificationServer.Register(request);
|
||||
_NotificationRequestRegistered = true;
|
||||
}
|
||||
|
||||
private Appointment _Appointment;
|
||||
/// <summary>
|
||||
/// Gets the Appointment reminder is attached to.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Appointment Appointment
|
||||
{
|
||||
get { return _Appointment; }
|
||||
internal set
|
||||
{
|
||||
if (_Appointment != value)
|
||||
{
|
||||
_Appointment = value;
|
||||
if (NeedsNotification) UpdateNotifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _IsActive = true;
|
||||
/// <summary>
|
||||
/// Gets or sets whether reminder is active. Active reminders fire events or execute commands when
|
||||
/// reminder time has been reached. Set this value to false to dismiss the reminder.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
public bool IsActive
|
||||
{
|
||||
get { return _IsActive; }
|
||||
set
|
||||
{
|
||||
if (_IsActive != value)
|
||||
{
|
||||
bool oldValue = _IsActive;
|
||||
_IsActive = value;
|
||||
OnIsActiveChanged(oldValue, _IsActive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIsActiveChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
UpdateNotifications();
|
||||
}
|
||||
|
||||
private NotificationRequest _NotificationRequest = null;
|
||||
private NotificationRequest NotificationRequest
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_NotificationRequest == null)
|
||||
{
|
||||
_NotificationRequest = new NotificationRequest(((_IsSnoozeReminder && _SnoozeDateTime != DateTime.MinValue) ? _SnoozeDateTime : _ReminderTime), new NotificationServerEventHandler(OnReminderNotification));
|
||||
}
|
||||
else
|
||||
_NotificationRequest.NotificationTime = ((_IsSnoozeReminder && _SnoozeDateTime != DateTime.MinValue) ? _SnoozeDateTime : _ReminderTime);
|
||||
return _NotificationRequest;
|
||||
}
|
||||
set
|
||||
{
|
||||
_NotificationRequest = value;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _SnoozeDateTime = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets or sets the next snooze time for the reminder. Use the Snooze method if you want to snooze the reminder correctly.
|
||||
/// </summary>
|
||||
public DateTime SnoozeDateTime
|
||||
{
|
||||
get { return _SnoozeDateTime; }
|
||||
set
|
||||
{
|
||||
value = CalendarModel.GetCalendarDateTime(value);
|
||||
value = value.Kind == DateTimeKind.Utc ? value : GetUTCDateTime(value);
|
||||
if (value != _SnoozeDateTime)
|
||||
{
|
||||
DateTime oldValue = _SnoozeDateTime;
|
||||
_SnoozeDateTime = value;
|
||||
if (_SnoozeDateTime == DateTime.MinValue)
|
||||
{
|
||||
this.IsSnoozeReminder = false;
|
||||
UnregisterNotification();
|
||||
}
|
||||
OnSnoozeDateTimeChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSnoozeDateTimeChanged(DateTime oldValue, DateTime newValue)
|
||||
{
|
||||
//OnPropertyChanged(new PropertyChangedEventArgs("SnoozeDateTime"));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snoozes reminder so it occurs at specified notification time. This method should be used instead of the SnoozeDateTime property and it will
|
||||
/// set the SnoozeDateTime property to the next notification time.
|
||||
/// </summary>
|
||||
/// <param name="nextNotificationTime">Next reminder notification time.</param>
|
||||
public void Snooze(DateTime nextNotificationTime)
|
||||
{
|
||||
nextNotificationTime = CalendarModel.GetCalendarDateTime(nextNotificationTime);
|
||||
DateTime utcTime = nextNotificationTime.Kind == DateTimeKind.Utc ? nextNotificationTime : GetUTCDateTime(nextNotificationTime);
|
||||
UnregisterNotification();
|
||||
this.SnoozeDateTime = nextNotificationTime;
|
||||
this.IsSnoozeReminder = true;
|
||||
NotificationRequest request = this.NotificationRequest;
|
||||
request.NotificationTime = utcTime;
|
||||
RegisterNotification(request);
|
||||
}
|
||||
|
||||
private void UnregisterNotification()
|
||||
{
|
||||
if (_NotificationRequestRegistered)
|
||||
{
|
||||
NotificationServer.Unregister(this.NotificationRequest);
|
||||
_NotificationRequestRegistered = false;
|
||||
this.NotificationRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReminderNotification(NotificationRequest request, NotificationServerEventArgs e)
|
||||
{
|
||||
if (_ReminderAction != eReminderAction.None)
|
||||
{
|
||||
this.NotificationRequest = null;
|
||||
ProcessNotification();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the ReminderNotification as if the reminder time has been reached.
|
||||
/// <remarks>This method is automatically called by reminder once ReminderTime has been reached.</remarks>
|
||||
/// </summary>
|
||||
public void ProcessNotification()
|
||||
{
|
||||
if (_Appointment != null && _Appointment.Calendar != null)
|
||||
_Appointment.Calendar.InvokeReminderNotification(this);
|
||||
else if (_Parent != null && _Parent.ParentModel != null)
|
||||
_Parent.ParentModel.InvokeReminderNotification(this);
|
||||
|
||||
if (_ReminderAction == eReminderAction.None) return;
|
||||
|
||||
if ((_ReminderAction & eReminderAction.Event) != 0)
|
||||
{
|
||||
OnReminderNotification(new ReminderEventArgs(this));
|
||||
}
|
||||
|
||||
if ((_ReminderAction & eReminderAction.Command) != 0)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the ReminderNotification event.
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
protected virtual void OnReminderNotification(ReminderEventArgs eventArgs)
|
||||
{
|
||||
ReminderEventHandler h = ReminderNotification;
|
||||
if (h != null)
|
||||
h(this, eventArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an copy of the reminder.
|
||||
/// </summary>
|
||||
/// <returns>Reminder copy.</returns>
|
||||
public Reminder Copy()
|
||||
{
|
||||
Reminder copy = new Reminder();
|
||||
copy.Description = _Description;
|
||||
copy.Tag = _Tag;
|
||||
copy.IsActive = _IsActive;
|
||||
copy.ReminderAction = _ReminderAction;
|
||||
copy.IsActiveForPastAppointments = this.IsActiveForPastAppointments;
|
||||
if (ReminderNotification != null)
|
||||
copy.ReminderNotification = (ReminderEventHandler)ReminderNotification.Clone();
|
||||
copy.ReminderTime = ReminderTime;
|
||||
copy.IsSnoozeReminder = _IsSnoozeReminder;
|
||||
return copy;
|
||||
}
|
||||
|
||||
private bool _IsSnoozeReminder = false;
|
||||
/// <summary>
|
||||
/// Gets or sets whether this reminder is snooze reminder usually created by Reminder dialog when user hits the Snooze button.
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool IsSnoozeReminder
|
||||
{
|
||||
get { return _IsSnoozeReminder; }
|
||||
set
|
||||
{
|
||||
_IsSnoozeReminder = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ReminderCollection _Parent = null;
|
||||
internal ReminderCollection ParentCollection
|
||||
{
|
||||
get { return _Parent; }
|
||||
set
|
||||
{
|
||||
_Parent = value;
|
||||
if (NeedsNotification)
|
||||
UpdateNotifications();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Events Support
|
||||
public delegate void ReminderEventHandler(object sender, ReminderEventArgs e);
|
||||
/// <summary>
|
||||
/// Defines arguments for reminder related events.
|
||||
/// </summary>
|
||||
public class ReminderEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the reminder referenced by this event.
|
||||
/// </summary>
|
||||
public Reminder Reminder = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ReminderEventArgs class.
|
||||
/// </summary>
|
||||
/// <param name="appointment"></param>
|
||||
public ReminderEventArgs(Reminder reminder)
|
||||
{
|
||||
Reminder = reminder;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,107 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents collection of reminders.
|
||||
/// </summary>
|
||||
public class ReminderCollection : Collection<Reminder>
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ReminderCollection class.
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public ReminderCollection(Appointment parent)
|
||||
{
|
||||
_Appointment = parent;
|
||||
}
|
||||
CalendarModel _Model = null;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ReminderCollection class.
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public ReminderCollection(CalendarModel parentModel)
|
||||
{
|
||||
_Model = parentModel;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal Implementation
|
||||
protected override void InsertItem(int index, Reminder item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, Reminder item)
|
||||
{
|
||||
item.Appointment = _Appointment;
|
||||
item.ParentCollection = this;
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, Reminder item)
|
||||
{
|
||||
OnBeforeSetItem(index, item);
|
||||
base.SetItem(index, item);
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, Reminder item)
|
||||
{
|
||||
this[index].Appointment = null;
|
||||
this[index].ParentCollection = null;
|
||||
item.Appointment = _Appointment;
|
||||
item.ParentCollection = this;
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
OnBeforeRemove(index);
|
||||
base.RemoveItem(index);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index)
|
||||
{
|
||||
this[index].Appointment = null;
|
||||
this[index].ParentCollection = null;
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (Reminder item in this)
|
||||
{
|
||||
item.Appointment = null;
|
||||
}
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
private Appointment _Appointment;
|
||||
/// <summary>
|
||||
/// Gets parent appointment.
|
||||
/// </summary>
|
||||
public Appointment Appointment
|
||||
{
|
||||
get { return _Appointment; }
|
||||
internal set
|
||||
{
|
||||
_Appointment = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets parent model if collection is custom reminders collection.
|
||||
/// </summary>
|
||||
public CalendarModel ParentModel
|
||||
{
|
||||
get { return _Model; }
|
||||
internal set { _Model = value; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,128 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DevComponents.Schedule
|
||||
{
|
||||
internal class NativeMethods
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct TimeZoneInformation
|
||||
{
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Bias;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string StandardName;
|
||||
public NativeMethods.SystemTime StandardDate;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int StandardBias;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string DaylightName;
|
||||
public NativeMethods.SystemTime DaylightDate;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int DaylightBias;
|
||||
public TimeZoneInformation(NativeMethods.DynamicTimeZoneInformation dtzi)
|
||||
{
|
||||
this.Bias = dtzi.Bias;
|
||||
this.StandardName = dtzi.StandardName;
|
||||
this.StandardDate = dtzi.StandardDate;
|
||||
this.StandardBias = dtzi.StandardBias;
|
||||
this.DaylightName = dtzi.DaylightName;
|
||||
this.DaylightDate = dtzi.DaylightDate;
|
||||
this.DaylightBias = dtzi.DaylightBias;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct DynamicTimeZoneInformation
|
||||
{
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Bias;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string StandardName;
|
||||
public NativeMethods.SystemTime StandardDate;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int StandardBias;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
|
||||
public string DaylightName;
|
||||
public NativeMethods.SystemTime DaylightDate;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int DaylightBias;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x80)]
|
||||
public string TimeZoneKeyName;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SystemTime
|
||||
{
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Year;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Month;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short DayOfWeek;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Day;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Hour;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Minute;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Second;
|
||||
[MarshalAs(UnmanagedType.U2)]
|
||||
public short Milliseconds;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RegistryTimeZoneInformation
|
||||
{
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int Bias;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int StandardBias;
|
||||
[MarshalAs(UnmanagedType.I4)]
|
||||
public int DaylightBias;
|
||||
public NativeMethods.SystemTime StandardDate;
|
||||
public NativeMethods.SystemTime DaylightDate;
|
||||
public RegistryTimeZoneInformation(NativeMethods.TimeZoneInformation tzi)
|
||||
{
|
||||
this.Bias = tzi.Bias;
|
||||
this.StandardDate = tzi.StandardDate;
|
||||
this.StandardBias = tzi.StandardBias;
|
||||
this.DaylightDate = tzi.DaylightDate;
|
||||
this.DaylightBias = tzi.DaylightBias;
|
||||
}
|
||||
|
||||
public RegistryTimeZoneInformation(byte[] bytes)
|
||||
{
|
||||
if ((bytes == null) || (bytes.Length != 0x2c))
|
||||
{
|
||||
throw new ArgumentException("Argument_InvalidREG_TZI_FORMAT", "bytes");
|
||||
}
|
||||
this.Bias = BitConverter.ToInt32(bytes, 0);
|
||||
this.StandardBias = BitConverter.ToInt32(bytes, 4);
|
||||
this.DaylightBias = BitConverter.ToInt32(bytes, 8);
|
||||
this.StandardDate.Year = BitConverter.ToInt16(bytes, 12);
|
||||
this.StandardDate.Month = BitConverter.ToInt16(bytes, 14);
|
||||
this.StandardDate.DayOfWeek = BitConverter.ToInt16(bytes, 0x10);
|
||||
this.StandardDate.Day = BitConverter.ToInt16(bytes, 0x12);
|
||||
this.StandardDate.Hour = BitConverter.ToInt16(bytes, 20);
|
||||
this.StandardDate.Minute = BitConverter.ToInt16(bytes, 0x16);
|
||||
this.StandardDate.Second = BitConverter.ToInt16(bytes, 0x18);
|
||||
this.StandardDate.Milliseconds = BitConverter.ToInt16(bytes, 0x1a);
|
||||
this.DaylightDate.Year = BitConverter.ToInt16(bytes, 0x1c);
|
||||
this.DaylightDate.Month = BitConverter.ToInt16(bytes, 30);
|
||||
this.DaylightDate.DayOfWeek = BitConverter.ToInt16(bytes, 0x20);
|
||||
this.DaylightDate.Day = BitConverter.ToInt16(bytes, 0x22);
|
||||
this.DaylightDate.Hour = BitConverter.ToInt16(bytes, 0x24);
|
||||
this.DaylightDate.Minute = BitConverter.ToInt16(bytes, 0x26);
|
||||
this.DaylightDate.Second = BitConverter.ToInt16(bytes, 40);
|
||||
this.DaylightDate.Milliseconds = BitConverter.ToInt16(bytes, 0x2a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace DevComponents.Schedule
|
||||
{
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal static class UnsafeNativeMethods
|
||||
{
|
||||
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
|
||||
internal static extern int GetDynamicTimeZoneInformation(out NativeMethods.DynamicTimeZoneInformation lpDynamicTimeZoneInformation);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
internal static extern int GetTimeZoneInformation(out NativeMethods.TimeZoneInformation lpTimeZoneInformation);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
internal static extern bool GetFileMUIPath(int flags, [MarshalAs(UnmanagedType.LPWStr)] string filePath, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder language, ref int languageLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder fileMuiPath, ref int fileMuiPathLength, ref long enumerator);
|
||||
|
||||
[SecurityCritical, DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
internal static extern SafeLibraryHandle LoadLibraryEx(string libFilename, IntPtr reserved, int flags);
|
||||
|
||||
[SecurityCritical, DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
|
||||
internal static extern int LoadString(SafeLibraryHandle handle, int id, StringBuilder buffer, int bufferLength);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,122 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines weekly recurrence settings.
|
||||
/// </summary>
|
||||
public class WeeklyRecurrenceSettings : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
private AppointmentRecurrence _Recurrence = null;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WeeklyRecurrenceSettings class.
|
||||
/// </summary>
|
||||
/// <param name="recurrence"></param>
|
||||
public WeeklyRecurrenceSettings(AppointmentRecurrence recurrence)
|
||||
{
|
||||
_Recurrence = recurrence;
|
||||
}
|
||||
|
||||
|
||||
private eDayOfWeekRecurrence _RepeatOnDaysOfWeek = eDayOfWeekRecurrence.All;
|
||||
/// <summary>
|
||||
/// Gets or sets the days of week on which appointment is repeated. This property is represented by bit-flag enum
|
||||
/// which means that you can combine the values from eDayOfWeekRecurrence enum using OR operator to specify multiple values.
|
||||
/// Default value is All.
|
||||
/// <remarks>
|
||||
/// <para>This property value cannot be set to eDayOfWeekRecurrence.None.</para>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DefaultValue(eDayOfWeekRecurrence.All)]
|
||||
public eDayOfWeekRecurrence RepeatOnDaysOfWeek
|
||||
{
|
||||
get { return _RepeatOnDaysOfWeek; }
|
||||
set
|
||||
{
|
||||
if (value == eDayOfWeekRecurrence.None)
|
||||
throw new ArgumentException("RepeatOnDaysOfWeek cannot be set to eDayOfWeekRecurrence.None");
|
||||
|
||||
if (value != _RepeatOnDaysOfWeek)
|
||||
{
|
||||
eDayOfWeekRecurrence oldValue = _RepeatOnDaysOfWeek;
|
||||
_RepeatOnDaysOfWeek = value;
|
||||
OnRepeatOnDaysOfWeekChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnDaysOfWeekChanged(eDayOfWeekRecurrence oldValue, eDayOfWeekRecurrence newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnDaysOfWeek"));
|
||||
}
|
||||
|
||||
private int _RepeatInterval = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between recurring appointments. Default value is 1.
|
||||
/// <remarks>
|
||||
/// For example, setting RepeatInterval to 2 means that appointment will recur every 2 weeks.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatInterval
|
||||
{
|
||||
get { return _RepeatInterval; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatInterval)
|
||||
{
|
||||
int oldValue = _RepeatInterval;
|
||||
_RepeatInterval = value;
|
||||
OnRepeatIntervalChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatIntervalChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatInterval"));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,70 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents working day in calendar.
|
||||
/// </summary>
|
||||
public class WorkDay : BaseWorkDay
|
||||
{
|
||||
#region Internal Implementation
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDay class.
|
||||
/// </summary>
|
||||
public WorkDay()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDay class.
|
||||
/// </summary>
|
||||
/// <param name="dayOfWeek"></param>
|
||||
public WorkDay(DayOfWeek dayOfWeek)
|
||||
{
|
||||
_DayOfWeek = dayOfWeek;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDay class.
|
||||
/// </summary>
|
||||
/// <param name="dayOfWeek"></param>
|
||||
/// <param name="workStartTime"></param>
|
||||
/// <param name="workEndTime"></param>
|
||||
public WorkDay(DayOfWeek dayOfWeek, WorkTime workStartTime, WorkTime workEndTime)
|
||||
{
|
||||
_DayOfWeek = dayOfWeek;
|
||||
_WorkStartTime = workStartTime;
|
||||
_WorkEndTime = workEndTime;
|
||||
}
|
||||
|
||||
private DayOfWeek _DayOfWeek = DayOfWeek.Monday;
|
||||
/// <summary>
|
||||
/// Gets or sets the day of week this instance represents.
|
||||
/// </summary>
|
||||
public DayOfWeek DayOfWeek
|
||||
{
|
||||
get { return _DayOfWeek; }
|
||||
set
|
||||
{
|
||||
if (value != _DayOfWeek)
|
||||
{
|
||||
DayOfWeek oldValue = _DayOfWeek;
|
||||
_DayOfWeek = value;
|
||||
OnDayOfWeekChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDayOfWeekChanged(DayOfWeek oldValue, DayOfWeek newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DayOfWeek"));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,171 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents collection of working days.
|
||||
/// </summary>
|
||||
public class WorkDayCollection : Collection<WorkDay>
|
||||
{
|
||||
#region Constructor
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AppointmentCollection class.
|
||||
/// </summary>
|
||||
/// <param name="calendar"></param>
|
||||
public WorkDayCollection(CalendarModel calendar)
|
||||
{
|
||||
_Calendar = calendar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkDayCollection class.
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
public WorkDayCollection(Owner owner)
|
||||
{
|
||||
_Owner = owner;
|
||||
}
|
||||
#endregion
|
||||
#region Internal Implementation
|
||||
private Owner _Owner = null;
|
||||
/// <summary>
|
||||
/// Gets the Owner of work-day collection.
|
||||
/// </summary>
|
||||
public Owner Owner
|
||||
{
|
||||
get { return _Owner; }
|
||||
internal set { _Owner = value; }
|
||||
}
|
||||
|
||||
private CalendarModel _Calendar = null;
|
||||
/// <summary>
|
||||
/// Gets the calendar collection is associated with.
|
||||
/// </summary>
|
||||
public CalendarModel Calendar
|
||||
{
|
||||
get { return _Calendar; }
|
||||
internal set
|
||||
{
|
||||
_Calendar = value;
|
||||
UpdateItemsCalendarModel();
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateItemsCalendarModel()
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
foreach (WorkDay item in this.Items)
|
||||
{
|
||||
item.Calendar = model;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
WorkDay day = this[index];
|
||||
OnBeforeRemove(index, day);
|
||||
base.RemoveItem(index);
|
||||
OnAfterRemove(index, day);
|
||||
}
|
||||
|
||||
private void OnAfterRemove(int index, WorkDay day)
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
model.WorkDayRemoved(day);
|
||||
}
|
||||
|
||||
private void OnBeforeRemove(int index, WorkDay day)
|
||||
{
|
||||
day.Calendar = null;
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, WorkDay item)
|
||||
{
|
||||
OnBeforeInsert(index, item);
|
||||
base.InsertItem(index, item);
|
||||
OnAfterInsert(index, item);
|
||||
}
|
||||
|
||||
private void OnAfterInsert(int index, WorkDay item)
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
model.WorkDayAdded(item);
|
||||
}
|
||||
|
||||
private void OnBeforeInsert(int index, WorkDay item)
|
||||
{
|
||||
if (this[item.DayOfWeek] != null)
|
||||
throw new InvalidOperationException("Day '" + item.DayOfWeek.ToString() + "' already in collection.");
|
||||
item.Calendar = GetCalendarModel();
|
||||
}
|
||||
|
||||
private CalendarModel GetCalendarModel()
|
||||
{
|
||||
if (_Calendar != null) return _Calendar;
|
||||
if (_Owner != null) return _Owner.Calendar;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, WorkDay newItem)
|
||||
{
|
||||
WorkDay oldItem = this[index];
|
||||
OnBeforeSetItem(index, oldItem, newItem);
|
||||
base.SetItem(index, newItem);
|
||||
OnAfterSetItem(index, oldItem, newItem);
|
||||
}
|
||||
|
||||
private void OnAfterSetItem(int index, WorkDay oldItem, WorkDay newItem)
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
if (model != null)
|
||||
{
|
||||
model.WorkDayRemoved(oldItem);
|
||||
model.WorkDayAdded(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeforeSetItem(int index, WorkDay oldItem, WorkDay newItem)
|
||||
{
|
||||
oldItem.Calendar = null;
|
||||
newItem.Calendar = GetCalendarModel();
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
CalendarModel model = GetCalendarModel();
|
||||
foreach (WorkDay item in this)
|
||||
{
|
||||
item.Calendar = null;
|
||||
if (model != null)
|
||||
model.WorkDayRemoved(item);
|
||||
}
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item based on the Key assigned to the item
|
||||
/// </summary>
|
||||
/// <param name="day">Day of week to retrive data for.</param>
|
||||
/// <returns>Reference to WorkDay or null if no day in collection.</returns>
|
||||
public WorkDay this[DayOfWeek day]
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (WorkDay item in this.Items)
|
||||
{
|
||||
if (item.DayOfWeek == day) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,132 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a work time.
|
||||
/// </summary>
|
||||
public struct WorkTime
|
||||
{
|
||||
#region Private variables
|
||||
|
||||
private int _Hour; // Hour
|
||||
private int _Minute; // Minute
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the WorkTime structure.
|
||||
/// </summary>
|
||||
/// <param name="hour"></param>
|
||||
/// <param name="minute"></param>
|
||||
public WorkTime(int hour, int minute)
|
||||
{
|
||||
if (hour < 0 || hour > 23)
|
||||
throw new ArgumentException("Hour value must be from 0 to 23");
|
||||
|
||||
if (minute < 0 || minute > 59)
|
||||
throw new ArgumentException("Minute value must be from 0 to 59");
|
||||
|
||||
_Hour = hour;
|
||||
_Minute = minute;
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hour from 0 to 23 this time instance represents.
|
||||
/// </summary>
|
||||
public int Hour
|
||||
{
|
||||
get { return _Hour; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 23)
|
||||
throw new ArgumentException("Hour value must be from 0 to 23");
|
||||
|
||||
_Hour = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minute from 0 to 59 this time instance represents.
|
||||
/// </summary>
|
||||
public int Minute
|
||||
{
|
||||
get { return _Minute; }
|
||||
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > 59)
|
||||
throw new ArgumentException("Minute value must be from 0 to 59");
|
||||
|
||||
_Minute = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the WorkTime is Empty
|
||||
/// </summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_Hour == 0 && _Minute == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operator overloades
|
||||
|
||||
public static bool operator >=(WorkTime op1, WorkTime op2)
|
||||
{
|
||||
if ((op1.Hour > op2.Hour) ||
|
||||
(op1.Hour == op2.Hour && op1.Minute >= op2.Minute))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
public static bool operator <=(WorkTime op1, WorkTime op2)
|
||||
{
|
||||
if ((op1.Hour < op2.Hour) ||
|
||||
(op1.Hour == op2.Hour && op1.Minute <= op2.Minute))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
public static bool operator >(WorkTime op1, WorkTime op2)
|
||||
{
|
||||
if ((op1.Hour > op2.Hour) ||
|
||||
(op1.Hour == op2.Hour && op1.Minute > op2.Minute))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
public static bool operator <(WorkTime op1, WorkTime op2)
|
||||
{
|
||||
if ((op1.Hour < op2.Hour) ||
|
||||
(op1.Hour == op2.Hour && op1.Minute < op2.Minute))
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,72 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the calendar year.
|
||||
/// </summary>
|
||||
public class Year
|
||||
{
|
||||
#region Internal Implementation
|
||||
private int _Year = 0;
|
||||
private CalendarModel _CalendarModel = null;
|
||||
private ReadOnlyCollection<Month> _ReadOnlyMonths = null;
|
||||
private List<Month> _Months = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the Year class.
|
||||
/// </summary>
|
||||
/// <param name="year"></param>
|
||||
/// <param name="calendarModel"></param>
|
||||
public Year(int year, CalendarModel calendarModel)
|
||||
{
|
||||
_Year = year;
|
||||
_CalendarModel = calendarModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns read-only collection of months in year.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<Month> Months
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_ReadOnlyMonths == null)
|
||||
CreateCollection();
|
||||
return _ReadOnlyMonths;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCollection()
|
||||
{
|
||||
_Months = new List<Month>(12);
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
_Months.Add(new Month(_CalendarModel, _Year, i + 1));
|
||||
}
|
||||
_ReadOnlyMonths = new ReadOnlyCollection<Month>(_Months);
|
||||
}
|
||||
|
||||
internal void InvalidateAppointments()
|
||||
{
|
||||
if (_Months == null) return;
|
||||
foreach (Month month in _Months)
|
||||
{
|
||||
month.InvalidateAppointments();
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvalidateAppointments(int month, int day)
|
||||
{
|
||||
if (_Months == null) return;
|
||||
_Months[month - 1].InvalidateAppointments(day);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -0,0 +1,193 @@
|
||||
#if FRAMEWORK20
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace DevComponents.Schedule.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines yearly recurrence settings.
|
||||
/// </summary>
|
||||
public class YearlyRecurrenceSettings : INotifyPropertyChanged, INotifySubPropertyChanged
|
||||
{
|
||||
#region Internal Implementation
|
||||
private AppointmentRecurrence _Recurrence = null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the YearlyRecurrenceSettings class.
|
||||
/// </summary>
|
||||
/// <param name="recurrence"></param>
|
||||
public YearlyRecurrenceSettings(AppointmentRecurrence recurrence)
|
||||
{
|
||||
_Recurrence = recurrence;
|
||||
}
|
||||
|
||||
private int _RepeatInterval = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between recurring appointments. Default value is 1.
|
||||
/// <remarks>
|
||||
/// For example, setting RepeatInterval to 2 means that appointment will recur every 2 years.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatInterval
|
||||
{
|
||||
get { return _RepeatInterval; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatInterval)
|
||||
{
|
||||
int oldValue = _RepeatInterval;
|
||||
_RepeatInterval = value;
|
||||
OnRepeatIntervalChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatIntervalChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatInterval"));
|
||||
}
|
||||
|
||||
private int _RepeatOnDayOfMonth = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the day of month on which appointment is repeated.
|
||||
/// When RepeatOnRelativeDayInMonth property is set to value other than None value of this property is not used.
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatOnDayOfMonth
|
||||
{
|
||||
get { return _RepeatOnDayOfMonth; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnDayOfMonth)
|
||||
{
|
||||
int oldValue = _RepeatOnDayOfMonth;
|
||||
_RepeatOnDayOfMonth = value;
|
||||
OnRepeatOnDayOfMonthChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnDayOfMonthChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnDayOfMonth"));
|
||||
}
|
||||
|
||||
|
||||
private eRelativeDayInMonth _RepeatOnRelativeDayInMonth = eRelativeDayInMonth.None;
|
||||
/// <summary>
|
||||
/// Gets or sets whether appointment should repeat on first, second, third, fourth or last day in month as specified
|
||||
/// by RepeatOnDayOfMonth property. Default value is None.
|
||||
/// </summary>
|
||||
[DefaultValue(eRelativeDayInMonth.None)]
|
||||
public eRelativeDayInMonth RepeatOnRelativeDayInMonth
|
||||
{
|
||||
get { return _RepeatOnRelativeDayInMonth; }
|
||||
set
|
||||
{
|
||||
if (value != _RepeatOnRelativeDayInMonth)
|
||||
{
|
||||
eRelativeDayInMonth oldValue = _RepeatOnRelativeDayInMonth;
|
||||
_RepeatOnRelativeDayInMonth = value;
|
||||
OnRepeatOnRelativeDayInMonthChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnRelativeDayInMonthChanged(eRelativeDayInMonth oldValue, eRelativeDayInMonth newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnRelativeDayInMonth"));
|
||||
}
|
||||
|
||||
private DayOfWeek _RelativeDayOfWeek = DayOfWeek.Monday;
|
||||
/// <summary>
|
||||
/// Gets or sets the day of week on which relative repeat as specified by RepeatOnRelativeDayInMonth is effective.
|
||||
/// For example setting RepeatOnRelativeDayInMonth to First and RelativeDayOfWeek to Monday will repeat the appointment on first
|
||||
/// Monday in a month.
|
||||
/// </summary>
|
||||
[DefaultValue(DayOfWeek.Monday)]
|
||||
public DayOfWeek RelativeDayOfWeek
|
||||
{
|
||||
get { return _RelativeDayOfWeek; }
|
||||
set
|
||||
{
|
||||
if (value != _RelativeDayOfWeek)
|
||||
{
|
||||
DayOfWeek oldValue = _RelativeDayOfWeek;
|
||||
_RelativeDayOfWeek = value;
|
||||
OnRelativeDayOfWeekChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRelativeDayOfWeekChanged(DayOfWeek oldValue, DayOfWeek newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RelativeDayOfWeek"));
|
||||
}
|
||||
|
||||
private int _RepeatOnMonth = 1;
|
||||
/// <summary>
|
||||
/// Gets or sets the month the appointment is repeated on every year.
|
||||
/// </summary>
|
||||
[DefaultValue(1)]
|
||||
public int RepeatOnMonth
|
||||
{
|
||||
get { return _RepeatOnMonth; }
|
||||
set
|
||||
{
|
||||
if (value < 1 || value > 12)
|
||||
throw new ArgumentException("Valid RepeatOnMonth values are between 1-12");
|
||||
if (value != _RepeatOnMonth)
|
||||
{
|
||||
int oldValue = _RepeatOnMonth;
|
||||
_RepeatOnMonth = value;
|
||||
OnRepeatOnMonthChanged(oldValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRepeatOnMonthChanged(int oldValue, int newValue)
|
||||
{
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RepeatOnMonth"));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifyPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property value has changed.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the PropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChangedEventHandler eh = PropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
OnSubPropertyChanged(new SubPropertyChangedEventArgs(this, e));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region INotifySubPropertyChanged Members
|
||||
/// <summary>
|
||||
/// Occurs when property or property of child objects has changed. This event is similar to PropertyChanged event with key
|
||||
/// difference that it occurs for the property changed of child objects as well.
|
||||
/// </summary>
|
||||
public event SubPropertyChangedEventHandler SubPropertyChanged;
|
||||
/// <summary>
|
||||
/// Raises the SubPropertyChanged event.
|
||||
/// </summary>
|
||||
/// <param name="e">Event arguments</param>
|
||||
protected virtual void OnSubPropertyChanged(SubPropertyChangedEventArgs e)
|
||||
{
|
||||
SubPropertyChangedEventHandler eh = SubPropertyChanged;
|
||||
if (eh != null) eh(this, e);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user