//------------------------------------------------------------- // // Copyright © Microsoft Corporation. All Rights Reserved. // //------------------------------------------------------------- // @owner=alexgor, deliant, victark using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Globalization; using System.Diagnostics.CodeAnalysis; using System.Collections; #if Microsoft_CONTROL namespace System.Windows.Forms.DataVisualization.Charting #else namespace System.Web.UI.DataVisualization.Charting #endif { /// /// Base class for all chart element collections /// #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public abstract class ChartElementCollection : Collection, IChartElement, IDisposable where T : ChartElement { #region Member variables private IChartElement _parent = null; private CommonElements _common = null; internal int _suspendUpdates = 0; #endregion #region Properties /// /// Gets or sets the parent. /// internal IChartElement Parent { get { return _parent; } set { _parent = value; Invalidate(); } } /// /// Gets the CommonElements of the chart. /// internal CommonElements Common { get { if (_common == null && _parent != null) { _common = _parent.Common; } return _common; } } /// /// Gets the chart. /// internal Chart Chart { get { if (Common != null) return Common.Chart; else return null; } } /// /// Gets the items as List<T>. Use this property to perform advanced List specific operations (Sorting, etc) /// internal List ItemList { get { return Items as List; } } internal bool IsSuspended { get { return _suspendUpdates > 0; } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The parent chart element. internal ChartElementCollection(IChartElement parent) { _parent = parent; } #endregion #region Methods /// /// Forces the invalidation of the parent chart element /// public virtual void Invalidate() { if (_parent != null && !IsSuspended) _parent.Invalidate(); } /// /// Suspends invalidation /// public virtual void SuspendUpdates() { _suspendUpdates++; } /// /// Resumes invalidation. /// public virtual void ResumeUpdates() { if (_suspendUpdates>0) _suspendUpdates--; if (_suspendUpdates==0) this.Invalidate(); } /// /// Removes all elements from the . /// [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] protected override void ClearItems() { SuspendUpdates(); while (this.Count > 0) { this.RemoveItem(0); } ResumeUpdates(); } /// /// Deinitializes the specified item. /// /// The item. internal virtual void Deinitialize( T item) { } /// /// Initializes the specified item. /// /// The item. internal virtual void Initialize(T item) { } /// /// Removes the element at the specified index of the . /// /// The zero-based index of the element to remove. [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] protected override void RemoveItem(int index) { this.Deinitialize(this[index]); this[index].Parent = null; base.RemoveItem(index); Invalidate(); } /// /// Inserts an element into the at the specified index. /// /// The zero-based index at which should be inserted. /// The object to insert. The value can be null for reference types. [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] protected override void InsertItem(int index, T item) { this.Initialize(item); item.Parent = this; base.InsertItem(index, item); Invalidate(); } /// /// Replaces the element at the specified index. /// /// The zero-based index of the element to replace. /// The new value for the element at the specified index. The value can be null for reference types. [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] protected override void SetItem(int index, T item) { this.Initialize(item); item.Parent = this; base.SetItem(index, item); Invalidate(); } #endregion #region IChartElement Members IChartElement IChartElement.Parent { get { return this.Parent; } set { this.Parent = value; } } void IChartElement.Invalidate() { this.Invalidate(); } CommonElements IChartElement.Common { get{ return this.Common; } } #endregion #region IDisposable Members /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) { // Dispose managed resources foreach (T element in this) { element.Dispose(); } } } /// /// Performs freeing, releasing, or resetting managed resources. /// [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")] public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } #endregion } /// /// Base class for all collections of named chart elements. Performs the name management and enforces the uniquness of the names /// /// #if ASPPERM_35 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)] [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] #endif public abstract class ChartNamedElementCollection : ChartElementCollection, INameController where T : ChartNamedElement { #region Fields private List _cachedState = null; private int _disableDeleteCount = 0; #endregion #region Properties /// /// Gets the name prefix that is used to create unique chart element names. /// /// The default name prefix of the chart elements stored in the collection. protected virtual string NamePrefix { get { return typeof(T).Name; } } /// /// Gets or sets the chart element with the specified name. /// /// public T this[string name] { get { int index = this.IndexOf(name); if (index != -1) { return this[index]; } throw new ArgumentException(SR.ExceptionNameNotFound(name, this.GetType().Name)); } set { int nameIndex = this.IndexOf(name); int itemIndex = this.IndexOf(value); bool nameFound = nameIndex > -1; bool itemFound = itemIndex > -1; if (!nameFound && !itemFound) this.Add(value); else if (nameFound && !itemFound) this[nameIndex] = value; else if (!nameFound && itemFound) throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(name, this.GetType().Name)); else if (nameFound && itemFound && nameIndex != itemIndex) throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(name, this.GetType().Name)); } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The parent chart element. internal ChartNamedElementCollection(IChartElement parent) : base(parent) { } #endregion #region Events internal event EventHandler NameReferenceChanged; internal event EventHandler NameReferenceChanging; #endregion #region Methods /// /// Determines whether the chart element with the specified name already exists in the collection. /// /// The new chart element name. /// /// true if new chart element name is unique; otherwise, false. /// public virtual bool IsUniqueName(string name) { return FindByName(name)==null; } /// /// Finds the unique name for a new element being added to the collection /// /// Next unique chart element name public virtual string NextUniqueName() { // Find unique name string result = string.Empty; string prefix = this.NamePrefix; for (int i = 1; i < System.Int32.MaxValue; i++) { result = prefix + i.ToString(CultureInfo.InvariantCulture); // Check whether the name is unique if (IsUniqueName(result)) { break; } } return result; } /// /// Indexes the of chart element with the specified name. /// /// The name. /// public int IndexOf(string name) { int i = 0; foreach (T namedObj in this) { if (namedObj.Name == name) return i; i++; } return -1; } /// /// Verifies the name reference to a chart named element stored in this collection and throws the argument exception if its not valid. /// /// Chart element name. internal void VerifyNameReference(string name) { if (Chart!=null && !Chart.serializing && !IsNameReferenceValid(name)) throw new ArgumentException(SR.ExceptionNameNotFound(name, this.GetType().Name)); } /// /// Verifies the name reference to a chart named element stored in this collection. /// /// Chart element name. internal bool IsNameReferenceValid(string name) { return String.IsNullOrEmpty(name) || name == Constants.NotSetValue || IndexOf(name) >= 0; } /// /// Finds the chart element by the name. /// /// The name. /// public virtual T FindByName(string name) { foreach (T namedObj in this) { if (namedObj.Name == name) return namedObj; } return null; } /// /// Inserts the specified item in the collection at the specified index. /// /// The zero-based index where the item is to be inserted. /// The object to insert. protected override void InsertItem(int index, T item) { if (String.IsNullOrEmpty(item.Name)) item.Name = this.NextUniqueName(); else if (!IsUniqueName(item.Name)) throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(item.Name, this.GetType().Name)); //If the item references other named references we might need to fix the references FixNameReferences(item); base.InsertItem(index, item); if (this.Count == 1 && item != null) { // First element is added to the list -> fire the NameReferenceChanged event to update all the dependent elements ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(null, item)); } } /// /// Replaces the element at the specified index. /// /// The zero-based index of the element to replace. /// The new value for the element at the specified index. protected override void SetItem(int index, T item) { if (String.IsNullOrEmpty(item.Name)) item.Name = this.NextUniqueName(); else if (!IsUniqueName(item.Name) && IndexOf(item.Name) != index) throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(item.Name, this.GetType().Name)); //If the item references other named references we might need to fix the references FixNameReferences(item); // Remember the removedElement ChartNamedElement removedElement = index /// Removes the element at the specified index of the collection. /// /// The zero-based index of the element to remove. protected override void RemoveItem(int index) { // Remember the removedElement ChartNamedElement removedElement = index < Count ? this[index] : null; if (_disableDeleteCount == 0) { ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(removedElement, null)); } base.RemoveItem(index); if (_disableDeleteCount == 0) { // All elements referencing the removed element will be redirected to the first element in collection // Fire the NameReferenceChanged event to update all the dependent elements ChartNamedElement defaultElement = this.Count > 0 ? this[0] : null; ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(removedElement, defaultElement)); } } /// /// Fixes the name references of the item. /// internal virtual void FixNameReferences(T item) { //Nothing to fix at the base class... } #endregion #region INameController Members /// /// Determines whether is the name us unique. /// /// The name. /// /// true if is the name us unique; otherwise, false. /// bool INameController.IsUniqueName(string name) { return this.IsUniqueName(name); } /// /// Gets or sets a value indicating whether this instance is in edit mode by collecrtion editor. /// /// /// true if this instance the colection is editing; otherwise, false. /// bool INameController.IsColectionEditing { get { return _disableDeleteCount == 0; } set { _disableDeleteCount += value ? 1 : -1; } } /// /// Raises the event. /// /// The instance containing the event data. void INameController.OnNameReferenceChanging(NameReferenceChangedEventArgs e) { if (!IsSuspended) { if (this.NameReferenceChanging != null) this.NameReferenceChanging(this, e); } } /// /// Raises the event. /// /// The instance containing the event data. void INameController.OnNameReferenceChanged(NameReferenceChangedEventArgs e) { if (!IsSuspended) { if (this.NameReferenceChanged != null) this.NameReferenceChanged(this, e); } } /// /// Does the snapshot of collection items. /// /// if set to true collection items will be saved. /// The changing callback. /// The changed callback. void INameController.DoSnapshot(bool save, EventHandler changingCallback, EventHandler changedCallback) { if (save) { _cachedState = new List(this); if (changingCallback != null) this.NameReferenceChanging += changingCallback; if (changedCallback != null) this.NameReferenceChanged += changedCallback; } else { if (changingCallback != null) this.NameReferenceChanging -= changingCallback; if (changedCallback != null) this.NameReferenceChanged -= changedCallback; _cachedState.Clear(); _cachedState = null; } } /// /// Gets the snapshot of saved collection items. /// /// The snapshot. IList INameController.Snapshot { get { return _cachedState; } } #endregion } }