//-------------------------------------------------------------
//
// 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
}
}