#if FRAMEWORK20 using System; using System.Collections.Generic; using DevComponents.Schedule.Model; using System.ComponentModel; namespace DevComponents.DotNetBar.Schedule { internal class ModelTimeLineViewConnector : ModelViewConnector { #region Const private const int DaysInWeek = 7; #endregion #region Static data static private AppointmentSubsetCollection _lineAppts; static private List _listAppts; static private DateTime _lineStartTime; // TimeLine start date static private DateTime _lineEndTime; // TimeLine end date static private int _lineState; // Refresh state static private List _periodAppts; static private DateTime _periodStartTime; // Period start date static private DateTime _periodEndTime; // Period end date #endregion #region Private variables private CalendarModel _Model; // The associated CalendarModel private TimeLineView _View; // The associated _TimeLineView private DateTime _ViewStartTime; // View start time private DateTime _ViewEndTime; // View end time private int _ViewState; // View refresh state private DayInfo[] _DayInfo; // DayInfo array (WorkStartTimes) private bool _IsConnected; // Connection status #endregion /// /// Constructor /// /// Assoc CalendarModel /// Assoc TimeLineView public ModelTimeLineViewConnector(CalendarModel model, TimeLineView timeLineView) { _Model = model; _View = timeLineView; } #region Public properties /// /// Gets the connection status /// public override bool IsConnected { get { return (_IsConnected); } } /// /// Gets the internal AppointmentSubsetCollection /// public AppointmentSubsetCollection Appts { get { if (_lineAppts == null) _lineAppts = new AppointmentSubsetCollection(_Model, _lineStartTime, _lineEndTime); return (_lineAppts); } } /// /// Gets the list of line appointments /// public List ListAppts { get { return (_listAppts); } } /// /// Gets the DayInfo array /// public DayInfo[] DayInfo { get { return (_DayInfo); } } #endregion #region Connect processing /// /// Performs Model connection processing /// public override void Connect() { VerifyModel(); if (_IsConnected) Disconnect(); LoadData(); // Get notification on Model property changes HookEvents(true); _IsConnected = true; } #endregion #region Hook events /// /// Hooks or unhooks our system events /// /// private void HookEvents(bool hook) { if (hook == true) { _Model.PropertyChanged += ModelPropertyChanged; _Model.SubPropertyChanged += ModelSubPropertyChanged; _View.CalendarView.CustomItems.CollectionChanged += CustomItemsCollectionChanged; } else { _Model.PropertyChanged -= ModelPropertyChanged; _Model.SubPropertyChanged -= ModelSubPropertyChanged; _View.CalendarView.CustomItems.CollectionChanged -= CustomItemsCollectionChanged; } } #endregion #region Event processing #region Model property change processing /// /// Handles Model property change notifications /// /// /// private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == CalendarModel.AppointmentsPropertyName) { _ViewStartTime = new DateTime(); RefreshData(_ViewState == _lineState); _View.NeedRecalcLayout = true; _View.NeedRecalcSize = true; } else if (e.PropertyName == CalendarModel.WorkDaysPropertyName) { UpdateWorkDayDetails(); _View.Refresh(); } } #endregion #region ModelSubPropertyChanged /// /// Handles ModelSubProperty change notifications /// /// object /// SubPropertyChangedEventArgs private void ModelSubPropertyChanged(object sender, SubPropertyChangedEventArgs e) { if (e.Source is WorkDay) { UpdateWorkDayDetails(); } else if (e.Source is Owner) { Owner owner = (Owner)e.Source; if (_View.OwnerKey != null && _View.OwnerKey.Equals(owner.Key)) { if (e.PropertyChangedArgs.PropertyName == Owner.DisplayNamePropertyName) _View.DisplayName = owner.DisplayName; else if (e.PropertyChangedArgs.PropertyName.Equals("ColorScheme")) _View.CalendarColor = owner.ColorScheme; } } else if (e.Source is Appointment) { Appointment app = e.Source as Appointment; AppointmentTimeLineView appView; string name = e.PropertyChangedArgs.PropertyName; if (name.Equals("Tooltip")) { appView = GetViewFromTimeLine(app); if (appView != null) appView.Tooltip = app.Tooltip; } else if (name.Equals("IsSelected")) { appView = GetViewFromTimeLine(app); if (appView != null) appView.IsSelected = app.IsSelected; } else if (name.Equals("CategoryColor") || name.Equals("TimeMarkedAs")) { appView = GetViewFromTimeLine(app); if (appView != null) appView.Refresh(); } else if (name.Equals("OwnerKey")) { if (_View.CalendarView.IsMultiCalendar == true) { if (_View.OwnerKey == app.OwnerKey) { _ViewStartTime = new DateTime(); RefreshData(false); _View.NeedRecalcLayout = true; _View.RecalcSize(); } else { appView = GetViewFromTimeLine(app); if (appView != null) { _ViewStartTime = new DateTime(); RefreshData(false); _View.NeedRecalcLayout = true; _View.RecalcSize(); } } } } else if (name.Equals("Visible")) { RefreshData(true); } } } #endregion #region CustomItems_CollectionChanged /// /// Handles CustomItemCollection change events /// /// /// void CustomItemsCollectionChanged(object sender, EventArgs e) { _ViewStartTime = new DateTime(); RefreshData(_ViewState == _lineState); _View.NeedRecalcLayout = true; _View.NeedRecalcSize = true; } #endregion #endregion #region Disconnect processing /// /// Severs the Model/TimeLineView connection /// public override void Disconnect() { VerifyModel(); if (_IsConnected) { _IsConnected = false; _lineState = 0; // Clear our TimeLine items and // stop notification on Model property changes ClearTimeLineItems(); HookEvents(false); } } /// /// Clears TimeLine view items /// private void ClearTimeLineItems() { if (_View.CalendarItems.Count > 0) { // Loop through each CalendarItem, resetting // it's associated connection for (int i = _View.CalendarItems.Count - 1; i >= 0; i--) { AppointmentTimeLineView view = _View.CalendarItems[i] as AppointmentTimeLineView; if (view != null) { view.IsSelectedChanged -= _View.ItemIsSelectedChanged; view.Appointment = null; view.IsSelected = false; } _View.CalendarItems.RemoveAt(i); } } _View.SubItems.Clear(); } #endregion #region LoadData processing /// /// Loads Model/TimeLineView connection data /// private void LoadData() { LoadViewData(true, false); UpdateWorkDayDetails(); } #endregion #region LoadViewData /// /// Loads the view data /// /// Forceful reload /// Validation needed private void LoadViewData(bool reload, bool validate) { DateTime startTime, endTime; GetDateRange(out startTime, out endTime); reload = LoadPeriodData(reload, startTime, endTime); if (reload == true || _ViewStartTime != startTime || _ViewEndTime != endTime) { _ViewStartTime = startTime; _ViewEndTime = endTime; if (validate == true) { RemoveOutdatedViews(_periodAppts); RemoveOutdatedCustomItems(); } UpdateTimeLineView(_periodAppts); UpdateCustomItems(); } } #endregion #region LoadPeriodData /// /// Loads the Period data (visible view range) /// /// Forceful reload /// /// /// reload flag private bool LoadPeriodData(bool reload, DateTime startTime, DateTime endTime) { reload = LoadLineData(reload); if (reload == true || _periodStartTime != startTime || _periodEndTime != endTime) { _periodStartTime = startTime; _periodEndTime = endTime; _periodAppts = new List(); if (_listAppts != null) { for (int i = 0; i < _listAppts.Count; i++) { Appointment app = _listAppts[i]; if (app.EndTime > _periodStartTime && app.StartTime < _periodEndTime) _periodAppts.Add(app); } } reload = true; } return (reload); } #endregion #region LoadLineData /// /// Loads the TimeLine appointment data /// /// private bool LoadLineData(bool reload) { if (reload == true || _lineStartTime != _View.StartDate || _lineEndTime != _View.EndDate) { _lineStartTime = _View.StartDate; _lineEndTime = _View.EndDate; _lineAppts = null; // Legacy... _listAppts = GetAppointmentList(_Model, _View.StartDate, _View.EndDate.AddDays(1)); _lineState = _lineState ^ 1; } if (_ViewState != _lineState) { _ViewState = _lineState; _View.ModelReloaded = true; reload = true; } return (reload); } #endregion #region RefreshData processing /// /// Refreshes the data in a previously established /// and loaded connection /// public void RefreshData(bool reload) { if (_View.Displayed == true) LoadViewData(reload, true); } #endregion #region GetDateRange /// /// Gets the range of appointment dates /// /// /// private void GetDateRange(out DateTime startDate, out DateTime endDate) { int scol = _View.FirstVisibleColumn; int ncols = _View.ClientRect.Width / _View.ColumnWidth; startDate = _View.StartDate; endDate = startDate; try { startDate = startDate.AddMinutes(scol * _View.CalendarView.BaseInterval); endDate = startDate.AddMinutes(ncols * _View.CalendarView.BaseInterval); startDate = startDate.AddMinutes(-_View.CalendarView.BaseInterval); endDate = endDate.AddMinutes(_View.CalendarView.BaseInterval); } // ReSharper disable EmptyGeneralCatchClause catch // ReSharper restore EmptyGeneralCatchClause { } } #endregion #region UpdateTimeLineView /// /// Updates the TimeLine view /// /// private void UpdateTimeLineView(List appointments) { // Loop through each appointment // updating the assoc view accordingly foreach (Appointment appointment in appointments) { if (IsAppointmentVisible(appointment)) { // Get the assoc view AppointmentTimeLineView view = GetViewFromTimeLine(appointment) ?? GetNewView(appointment); // Set the view start and end times to // match the assoc appointment view.StartTime = appointment.StartTime; view.EndTime = appointment.EndTime; // Update the item data if (view.TimeLineView == null) { view.TimeLineView = _View; _View.CalendarItems.Add(view); _View.SubItems.Add(view); view.IsSelectedChanged += _View.ItemIsSelectedChanged; } } } } #endregion #region UpdateCustomItems /// /// Updates the TimeLine CustomItems /// private void UpdateCustomItems() { CustomCalendarItemCollection items = _View.CalendarView.CustomItems; if (items != null) { for (int i = 0; i < items.Count; i++) { CustomCalendarItem item = items[i]; if (IsCustomItemVisible(item) == true && (item.StartTime < _ViewEndTime && item.EndTime > _ViewStartTime)) { item.CalendarView = _View.CalendarView; CustomCalendarItem ci = GetItemFromTimeLine(item) ?? GetNewCustomItem(item); if (ci.StartTime != item.StartTime || ci.EndTime != item.EndTime) { ci.StartTime = item.StartTime; ci.EndTime = item.EndTime; } } } } } #endregion #region UpdateWorkDayDetails /// /// Updates the WorkDay details array /// private void UpdateWorkDayDetails() { // Update workDay timings if (_DayInfo == null) _DayInfo = new DayInfo[DaysInWeek]; for (int i = 0; i < DaysInWeek; i++) { if (_DayInfo[i] == null) _DayInfo[i] = new DayInfo(); Owner owner = _Model.Owners[_View.OwnerKey]; WorkDay workDay = (owner != null && owner.WorkDays.Count > 0) ? owner.WorkDays[(DayOfWeek)i] : _Model.WorkDays[(DayOfWeek)i]; if (workDay != null) { _DayInfo[i].WorkStartTime = workDay.WorkStartTime; _DayInfo[i].WorkEndTime = workDay.WorkEndTime; } else { _DayInfo[i].WorkStartTime = new WorkTime(); _DayInfo[i].WorkEndTime = new WorkTime(); } } } #endregion #region RemoveOutdatedViews /// /// Removes Outdated Views /// /// private void RemoveOutdatedViews(List appts) { for (int i=_View.CalendarItems.Count - 1; i>=0; i--) { AppointmentTimeLineView view = _View.CalendarItems[i] as AppointmentTimeLineView; if (view != null) { if (ValidViewAppointment(appts, view) == false) { view.TimeLineView = null; _View.NeedRecalcLayout = true; _View.SubItems._Remove(view); _View.CalendarItems.RemoveAt(i); if (view == _View.SelectedItem) _View.SelectedItem = null; view.IsSelectedChanged -= _View.ItemIsSelectedChanged; } } } } /// /// Determines if the provided view is valid, given /// the current list of Appointments /// /// /// /// private bool ValidViewAppointment( List appts, AppointmentTimeLineView view) { if (IsAppointmentVisible(view.Appointment) == false) return (false); if (view.IsSelected == true) { if (_Model.Appointments.Contains(view.Appointment) == true) return (true); } return (appts.Contains(view.Appointment)); } #endregion #region RemoveOutdatedCustomItems /// /// Removes out dated CustomItems /// private void RemoveOutdatedCustomItems() { for (int i = _View.CalendarItems.Count - 1; i >= 0; i--) { CustomCalendarItem item = _View.CalendarItems[i] as CustomCalendarItem; if (item != null) { if (IsValidItem(item) == false || (item.IsSelected == false && (item.EndTime < _ViewStartTime || item.StartTime > _ViewEndTime))) { _View.NeedRecalcSize = true; _View.SubItems._Remove(item); _View.CalendarItems.RemoveAt(i); if (item == _View.SelectedItem) _View.SelectedItem = null; item.IsSelectedChanged -= _View.ItemIsSelectedChanged; } } } } /// /// Determines if the given CustomItem is valid /// for the current view /// /// /// private bool IsValidItem(CustomCalendarItem view) { return (IsCustomItemVisible(view) == true && _View.CalendarView.CustomItems.Contains(view.BaseCalendarItem) == true); } #endregion #region GetViewFromTimeLine /// /// Gets the AppointmentView from the timeline /// /// /// AppointmentView or null private AppointmentTimeLineView GetViewFromTimeLine(Appointment appointment) { foreach (CalendarItem item in _View.CalendarItems) { AppointmentTimeLineView view = item as AppointmentTimeLineView; if (view != null && view.Appointment == appointment) return (view); } return (null); } #endregion #region GetItemFromTimeLine /// /// Gets the CustomCalendarItem from the timeline. /// /// /// CustomCalendarItem or null private CustomCalendarItem GetItemFromTimeLine(CustomCalendarItem item) { foreach (CalendarItem citem in _View.CalendarItems) { CustomCalendarItem view = citem as CustomCalendarItem; if (view != null && (view == item || view.BaseCalendarItem == item)) return (view); } return (null); } #endregion #region GetNewView /// /// Gets a new appointment view /// /// Appointment /// New view private AppointmentTimeLineView GetNewView(Appointment appointment) { AppointmentTimeLineView view = new AppointmentTimeLineView(_View, appointment); view.Tooltip = appointment.Tooltip; return (view); } #endregion #region GetNewCustomItem /// /// Gets a new CustomCalendarItem /// /// /// CustomCalendarItem private CustomCalendarItem GetNewCustomItem(CustomCalendarItem item) { CustomCalendarItem ci = (CustomCalendarItem)item.Copy(); ci.BaseCalendarItem = item; _View.CalendarItems.Add(ci); _View.NeedRecalcLayout = true; _View.SubItems.Add(ci); ci.IsSelectedChanged += _View.ItemIsSelectedChanged; return (ci); } #endregion #region View support routines /// /// Returns the view /// /// public override eCalendarView GetView() { return (eCalendarView.TimeLine); } /// /// Verifies the Model and MonthView are valid /// private void VerifyModel() { if (_Model == null) throw new NullReferenceException("CalendarModel must be set on connector."); if (_View == null) throw new NullReferenceException("AppointmentTimeLineView must be set on connector."); } #endregion } } #endif