/// <summary>
        /// Initializes a new instance of the <see cref="TimeLineViewModel"/> class.
        /// </summary>
        public TimeLineViewModel()
        {
            var now = DateTime.UtcNow;

            _maxTime = now;
            _minTime = now.AddHours(-24);

            _scrollPosition         = 0;
            _scrollScale            = 1.0;
            _warpGradientParameters = CalculateSkyGradient();

            _service = ServiceLocator.GetInstance <EarthQuakesService>();
            //NOTE: service is null when Blend calls this constructor when opening the TimeLineView.xaml
            if (_service != null)
            {
                _service.RequestMode          = EarthQuakeRequestMode.LastWeek;
                _regionsFilter                = new FilterViewModel(_service.EartQuakeRegionNames, _service.SelectedRegions);
                _regionsFilter.FilterChanged += RegionsFilterPropertyChanged;

                //Register using a weak event on the service PropertyChanged event.
                WeakPropertyChangedListener.CreateIfNecessary(_service, this);
                UpdateEarthQuakeData(_service.EarthQuakes);

                if (!_service.IsStarted)
                {
                    _service.StartService();
                }
            }
        }
        private static void OnSpecialSlotsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            MultiDayViewSettings     settings       = (MultiDayViewSettings)sender;
            INotifyCollectionChanged oldSlotsSource = args.OldValue as INotifyCollectionChanged;

            if (oldSlotsSource != null)
            {
                var listener = settings.specialSlotsCollectionChangedListener;
                if (listener != null)
                {
                    listener.Detach();
                    listener = null;
                }

                int count = settings.specialSlotsPropertyChangedListeners != null ? settings.specialSlotsPropertyChangedListeners.Count : 0;
                while (count > 0)
                {
                    var propertyListener = settings.specialSlotsPropertyChangedListeners[0];
                    settings.specialSlotsPropertyChangedListeners.RemoveAt(0);
                    propertyListener.Detach();
                    propertyListener = null;
                    count--;
                }
            }

            INotifyCollectionChanged newSlotsSource = args.NewValue as INotifyCollectionChanged;

            if (newSlotsSource != null)
            {
                settings.specialSlotsCollectionChangedListener = WeakCollectionChangedListener.CreateIfNecessary(newSlotsSource, settings);

                foreach (Slot slot in (IEnumerable <Slot>)newSlotsSource)
                {
                    var listener = WeakPropertyChangedListener.CreateIfNecessary(slot, settings);
                    if (listener != null)
                    {
                        settings.specialSlotsPropertyChangedListeners.Add(listener);
                    }
                }
            }

            if (oldSlotsSource != null && settings.IsOwnerLoaded)
            {
                settings.owner.timeRulerLayer.RecycleSlots((IEnumerable <Slot>)oldSlotsSource);
            }

            settings.Invalide(MultiDayViewUpdateFlag.AffectsSpecialSlots);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="MapViewModel"/> class.
        /// </summary>
        public MapViewModel()
        {
            _earthQuakeRadiusScaleMin = 10;
            _earthQuakeRadiusScaleMax = 90;
            _earthQuakeColorScaleMin  = 2.5;
            _earthQuakeColorScaleMax  = 10;

            _service = ServiceLocator.GetInstance <EarthQuakesService>();
            // NOTE: service is null when Blend calls this constructor when opening the TimeLineView.xaml
            if (_service != null)
            {
                _service.RequestMode          = EarthQuakeRequestMode.LastWeek;
                _regionsFilter                = new FilterViewModel(_service.EartQuakeRegionNames, _service.SelectedRegions);
                _regionsFilter.FilterChanged += RegionsFilterPropertyChanged;

                WeakPropertyChangedListener.CreateIfNecessary(_service, this);
                GetEarthQuakeData(_service.EarthQuakes);

                if (!_service.IsStarted)
                {
                    _service.StartService();
                }
            }
        }
        /// <summary>
        /// Implementation of the <see cref="ICollectionChangedListener" /> interface.
        /// </summary>
        /// <param name="sender">The collection sending the event.</param>
        /// <param name="e">The event args.</param>
        public void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
            case NotifyCollectionChangedAction.Add:
                foreach (Slot slot in e.NewItems)
                {
                    WeakPropertyChangedListener newListener = WeakPropertyChangedListener.CreateIfNecessary(slot, this);
                    if (newListener != null)
                    {
                        this.specialSlotsPropertyChangedListeners.Add(newListener);
                    }
                }
                break;

            case NotifyCollectionChangedAction.Remove:
                if (this.specialSlotsPropertyChangedListeners != null && this.specialSlotsPropertyChangedListeners.Count > 0)
                {
                    foreach (Slot slot in e.OldItems)
                    {
                        WeakPropertyChangedListener oldPropertyListener = this.specialSlotsPropertyChangedListeners[e.OldStartingIndex];
                        if (oldPropertyListener != null)
                        {
                            this.specialSlotsPropertyChangedListeners.Remove(oldPropertyListener);
                            oldPropertyListener.Detach();
                            oldPropertyListener = null;
                        }
                    }
                }

                this.owner.timeRulerLayer?.RecycleSlots(e.OldItems.Cast <Slot>());
                break;

            case NotifyCollectionChangedAction.Move:
            case NotifyCollectionChangedAction.Replace:
                WeakPropertyChangedListener propertyListener = this.specialSlotsPropertyChangedListeners[e.OldStartingIndex];
                if (propertyListener != null)
                {
                    this.specialSlotsPropertyChangedListeners.Remove(propertyListener);
                    propertyListener.Detach();
                    propertyListener = null;
                }

                WeakPropertyChangedListener listener = WeakPropertyChangedListener.CreateIfNecessary(e.NewItems[0], this);
                if (listener != null)
                {
                    this.specialSlotsPropertyChangedListeners.Add(listener);
                }
                break;

            case NotifyCollectionChangedAction.Reset:
                break;

            default:
                break;
            }

            if (sender == this.SpecialSlotsSource)
            {
                this.Invalide(MultiDayViewUpdateFlag.AffectsSpecialSlots);
            }
        }