1 //-------------------------------------------------------------
2 // <copyright company=’Microsoft Corporation’>
3 //   Copyright © Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant, victark
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Collections.ObjectModel;
11 using System.Text;
12 using System.Globalization;
13 using System.Diagnostics.CodeAnalysis;
14 using System.Collections;
15 
16 #if Microsoft_CONTROL
17 namespace System.Windows.Forms.DataVisualization.Charting
18 #else
19 namespace System.Web.UI.DataVisualization.Charting
20 #endif
21 {
22 
23     /// <summary>
24     /// Base class for all chart element collections
25     /// </summary>
26 #if ASPPERM_35
27 	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
28     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
29 #endif
30     public abstract class ChartElementCollection<T> : Collection<T>, IChartElement, IDisposable
31         where T : ChartElement
32     {
33         #region Member variables
34 
35         private IChartElement _parent = null;
36         private CommonElements _common = null;
37         internal int _suspendUpdates = 0;
38         #endregion
39 
40         #region Properties
41 
42         /// <summary>
43         /// Gets or sets the parent.
44         /// </summary>
45         internal IChartElement Parent
46         {
47             get { return _parent; }
48             set
49             {
50                 _parent = value;
51                 Invalidate();
52             }
53         }
54         /// <summary>
55         /// Gets the CommonElements of the chart.
56         /// </summary>
57         internal CommonElements Common
58         {
59             get
60             {
61                 if (_common == null && _parent != null)
62                 {
63                     _common = _parent.Common;
64                 }
65                 return _common;
66             }
67         }
68 
69         /// <summary>
70         /// Gets the chart.
71         /// </summary>
72         internal Chart Chart
73         {
74             get
75             {
76                 if (Common != null)
77                     return Common.Chart;
78                 else
79                     return null;
80             }
81         }
82 
83         /// <summary>
84         /// Gets the items as List&lt;T&gt;. Use this property to perform advanced List specific operations (Sorting, etc)
85         /// </summary>
86         internal List<T> ItemList
87         {
88             get { return Items as List<T>; }
89         }
90 
91         internal bool IsSuspended
92         {
93             get { return _suspendUpdates > 0; }
94         }
95         #endregion
96 
97         #region Constructors
98 
99         /// <summary>
100         /// Initializes a new instance of the <see cref="ChartElementCollection&lt;T&gt;"/> class.
101         /// </summary>
102         /// <param name="parent">The parent chart element.</param>
ChartElementCollection(IChartElement parent)103         internal ChartElementCollection(IChartElement parent)
104         {
105             _parent = parent;
106         }
107 
108         #endregion
109 
110         #region Methods
111 
112         /// <summary>
113         /// Forces the invalidation of the parent chart element
114         /// </summary>
Invalidate()115         public virtual void Invalidate()
116         {
117             if (_parent != null && !IsSuspended)
118                 _parent.Invalidate();
119         }
120 
121         /// <summary>
122         /// Suspends invalidation
123         /// </summary>
SuspendUpdates()124         public virtual void SuspendUpdates()
125         {
126             _suspendUpdates++;
127         }
128 
129         /// <summary>
130         /// Resumes invalidation.
131         /// </summary>
ResumeUpdates()132         public virtual void ResumeUpdates()
133         {
134             if (_suspendUpdates>0)
135                 _suspendUpdates--;
136 
137             if (_suspendUpdates==0)
138                 this.Invalidate();
139         }
140 
141         /// <summary>
142         /// Removes all elements from the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
143         /// </summary>
144         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
ClearItems()145         protected override void ClearItems()
146         {
147             SuspendUpdates();
148             while (this.Count > 0)
149             {
150                 this.RemoveItem(0);
151             }
152             ResumeUpdates();
153         }
154 
155         /// <summary>
156         /// Deinitializes the specified item.
157         /// </summary>
158         /// <param name="item">The item.</param>
Deinitialize( T item)159         internal virtual void Deinitialize( T item)
160         {
161 
162         }
163 
164         /// <summary>
165         /// Initializes the specified item.
166         /// </summary>
167         /// <param name="item">The item.</param>
Initialize(T item)168         internal virtual void Initialize(T item)
169         {
170 
171         }
172 
173         /// <summary>
174         /// Removes the element at the specified index of the <see cref="T:System.Collections.ObjectModel.Collection`1"/>.
175         /// </summary>
176         /// <param name="index">The zero-based index of the element to remove.</param>
177         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
RemoveItem(int index)178         protected override void RemoveItem(int index)
179         {
180             this.Deinitialize(this[index]);
181             this[index].Parent = null;
182             base.RemoveItem(index);
183             Invalidate();
184         }
185 
186         /// <summary>
187         /// Inserts an element into the <see cref="T:System.Collections.ObjectModel.Collection`1"/> at the specified index.
188         /// </summary>
189         /// <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
190         /// <param name="item">The object to insert. The value can be null for reference types.</param>
191         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
InsertItem(int index, T item)192         protected override void InsertItem(int index, T item)
193         {
194             this.Initialize(item);
195             item.Parent = this;
196             base.InsertItem(index, item);
197             Invalidate();
198         }
199 
200         /// <summary>
201         /// Replaces the element at the specified index.
202         /// </summary>
203         /// <param name="index">The zero-based index of the element to replace.</param>
204         /// <param name="item">The new value for the element at the specified index. The value can be null for reference types.</param>
205         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
SetItem(int index, T item)206         protected override void SetItem(int index, T item)
207         {
208             this.Initialize(item);
209             item.Parent = this;
210             base.SetItem(index, item);
211             Invalidate();
212         }
213 
214         #endregion
215 
216         #region IChartElement Members
217 
218         IChartElement IChartElement.Parent
219         {
220             get { return this.Parent; }
221             set { this.Parent = value; }
222         }
223 
IChartElement.Invalidate()224         void IChartElement.Invalidate()
225         {
226             this.Invalidate();
227         }
228 
229         CommonElements IChartElement.Common
230         {
231             get{ return this.Common; }
232         }
233 
234         #endregion
235 
236         #region IDisposable Members
237 
238         /// <summary>
239         /// Releases unmanaged and - optionally - managed resources
240         /// </summary>
241         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
Dispose(bool disposing)242         protected virtual void Dispose(bool disposing)
243         {
244             if (disposing)
245             {
246                 // Dispose managed resources
247                 foreach (T element in this)
248                 {
249                     element.Dispose();
250                 }
251             }
252         }
253 
254         /// <summary>
255         /// Performs freeing, releasing, or resetting managed resources.
256         /// </summary>
257         [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
Dispose()258         public void Dispose()
259         {
260             this.Dispose(true);
261             GC.SuppressFinalize(this);
262         }
263         #endregion
264 
265     }
266 
267     /// <summary>
268     /// Base class for all collections of named chart elements. Performs the name management and enforces the uniquness of the names
269     /// </summary>
270     /// <typeparam name="T"></typeparam>
271 #if ASPPERM_35
272 	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
273     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
274 #endif
275     public abstract class ChartNamedElementCollection<T> : ChartElementCollection<T>, INameController
276         where T : ChartNamedElement
277     {
278 
279         #region Fields
280         private List<T> _cachedState = null;
281         private int _disableDeleteCount = 0;
282         #endregion
283 
284         #region Properties
285 
286         /// <summary>
287         /// Gets the name prefix that is used to create unique chart element names.
288         /// </summary>
289         /// <value>The default name prefix of the chart elements stored in the collection.</value>
290         protected virtual string NamePrefix
291         {
292             get { return typeof(T).Name; }
293         }
294 
295         /// <summary>
296         /// Gets or sets the chart element with the specified name.
297         /// </summary>
298         /// <value></value>
299         public T this[string name]
300         {
301             get
302             {
303                 int index = this.IndexOf(name);
304                 if (index != -1)
305                 {
306                     return this[index];
307                 }
308                 throw new ArgumentException(SR.ExceptionNameNotFound(name, this.GetType().Name));
309             }
310             set
311             {
312                 int nameIndex = this.IndexOf(name);
313                 int itemIndex = this.IndexOf(value);
314                 bool nameFound = nameIndex > -1;
315                 bool itemFound = itemIndex > -1;
316 
317                 if (!nameFound && !itemFound)
318                     this.Add(value);
319 
320                 else if (nameFound && !itemFound)
321                     this[nameIndex] = value;
322 
323                 else if (!nameFound && itemFound)
324                     throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(name, this.GetType().Name));
325 
326                 else if (nameFound && itemFound && nameIndex != itemIndex)
327                     throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(name, this.GetType().Name));
328 
329             }
330         }
331         #endregion
332 
333         #region Constructors
334 
335         /// <summary>
336         /// Initializes a new instance of the <see cref="ChartNamedElementCollection&lt;T&gt;"/> class.
337         /// </summary>
338         /// <param name="parent">The parent chart element.</param>
ChartNamedElementCollection(IChartElement parent)339         internal ChartNamedElementCollection(IChartElement parent)
340             : base(parent)
341         {
342         }
343 
344         #endregion
345 
346         #region Events
347 
348         internal event EventHandler<NameReferenceChangedEventArgs> NameReferenceChanged;
349         internal event EventHandler<NameReferenceChangedEventArgs> NameReferenceChanging;
350 
351         #endregion
352 
353         #region Methods
354 
355         /// <summary>
356         /// Determines whether the chart element with the specified name already exists in the collection.
357         /// </summary>
358         /// <param name="name">The new chart element name.</param>
359         /// <returns>
360         /// 	<c>true</c> if new chart element name is unique; otherwise, <c>false</c>.
361         /// </returns>
IsUniqueName(string name)362         public virtual bool IsUniqueName(string name)
363         {
364             return FindByName(name)==null;
365         }
366 
367         /// <summary>
368         /// Finds the unique name for a new element being added to the collection
369         /// </summary>
370         /// <returns>Next unique chart element name</returns>
NextUniqueName()371         public virtual string NextUniqueName()
372         {
373             // Find unique name
374             string result = string.Empty;
375             string prefix = this.NamePrefix;
376             for (int i = 1; i < System.Int32.MaxValue; i++)
377             {
378                 result = prefix + i.ToString(CultureInfo.InvariantCulture);
379                 // Check whether the name is unique
380                 if (IsUniqueName(result))
381                 {
382                     break;
383                 }
384             }
385             return result;
386         }
387 
388         /// <summary>
389         /// Indexes the of chart element with the specified name.
390         /// </summary>
391         /// <param name="name">The name.</param>
392         /// <returns></returns>
IndexOf(string name)393         public int IndexOf(string name)
394         {
395             int i = 0;
396             foreach (T namedObj in this)
397             {
398                 if (namedObj.Name == name)
399                     return i;
400                 i++;
401             }
402             return -1;
403         }
404 
405         /// <summary>
406         /// Verifies the name reference to a chart named element stored in this collection and throws the argument exception if its not valid.
407         /// </summary>
408         /// <param name="name">Chart element name.</param>
VerifyNameReference(string name)409         internal void VerifyNameReference(string name)
410         {
411             if (Chart!=null && !Chart.serializing && !IsNameReferenceValid(name))
412                 throw new ArgumentException(SR.ExceptionNameNotFound(name, this.GetType().Name));
413         }
414 
415         /// <summary>
416         /// Verifies the name reference to a chart named element stored in this collection.
417         /// </summary>
418         /// <param name="name">Chart element name.</param>
IsNameReferenceValid(string name)419         internal bool IsNameReferenceValid(string name)
420         {
421             return  String.IsNullOrEmpty(name) ||
422                     name == Constants.NotSetValue ||
423                     IndexOf(name) >= 0;
424         }
425 
426         /// <summary>
427         /// Finds the chart element by the name.
428         /// </summary>
429         /// <param name="name">The name.</param>
430         /// <returns></returns>
FindByName(string name)431         public virtual T FindByName(string name)
432         {
433             foreach (T namedObj in this)
434             {
435                 if (namedObj.Name == name)
436                     return namedObj;
437             }
438             return null;
439         }
440 
441         /// <summary>
442         /// Inserts the specified item in the collection at the specified index.
443         /// </summary>
444         /// <param name="index">The zero-based index where the item is to be inserted.</param>
445         /// <param name="item">The object to insert.</param>
InsertItem(int index, T item)446         protected override void InsertItem(int index, T item)
447         {
448             if (String.IsNullOrEmpty(item.Name))
449                 item.Name = this.NextUniqueName();
450             else if (!IsUniqueName(item.Name))
451                 throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(item.Name, this.GetType().Name));
452 
453             //If the item references other named references we might need to fix the references
454             FixNameReferences(item);
455 
456             base.InsertItem(index, item);
457 
458             if (this.Count == 1 && item != null)
459             {
460                 // First element is added to the list -> fire the NameReferenceChanged event to update all the dependent elements
461                 ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(null, item));
462             }
463         }
464 
465         /// <summary>
466         /// Replaces the element at the specified index.
467         /// </summary>
468         /// <param name="index">The zero-based index of the element to replace.</param>
469         /// <param name="item">The new value for the element at the specified index.</param>
SetItem(int index, T item)470         protected override void SetItem(int index, T item)
471         {
472             if (String.IsNullOrEmpty(item.Name))
473                 item.Name = this.NextUniqueName();
474             else if (!IsUniqueName(item.Name) && IndexOf(item.Name) != index)
475                 throw new ArgumentException(SR.ExceptionNameAlreadyExistsInCollection(item.Name, this.GetType().Name));
476 
477             //If the item references other named references we might need to fix the references
478             FixNameReferences(item);
479 
480             // Remember the removedElement
481             ChartNamedElement removedElement = index<Count ? this[index] : null;
482 
483             ((INameController)this).OnNameReferenceChanging(new NameReferenceChangedEventArgs(removedElement, item));
484             base.SetItem(index, item);
485             // Fire the NameReferenceChanged event to update all the dependent elements
486             ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(removedElement, item));
487         }
488 
489         /// <summary>
490         /// Removes the element at the specified index of the collection.
491         /// </summary>
492         /// <param name="index">The zero-based index of the element to remove.</param>
RemoveItem(int index)493         protected override void RemoveItem(int index)
494         {
495             // Remember the removedElement
496             ChartNamedElement removedElement = index < Count ? this[index] : null;
497             if (_disableDeleteCount == 0)
498             {
499                 ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(removedElement, null));
500             }
501             base.RemoveItem(index);
502             if (_disableDeleteCount == 0)
503             {
504                 // All elements referencing the removed element will be redirected to the first element in collection
505                 // Fire the NameReferenceChanged event to update all the dependent elements
506                 ChartNamedElement defaultElement = this.Count > 0 ? this[0] : null;
507                 ((INameController)this).OnNameReferenceChanged(new NameReferenceChangedEventArgs(removedElement, defaultElement));
508             }
509         }
510 
511         /// <summary>
512         /// Fixes the name references of the item.
513         /// </summary>
FixNameReferences(T item)514         internal virtual void FixNameReferences(T item)
515         {
516             //Nothing to fix at the base class...
517         }
518 
519         #endregion
520 
521         #region INameController Members
522 
523         /// <summary>
524         /// Determines whether is the name us unique.
525         /// </summary>
526         /// <param name="name">The name.</param>
527         /// <returns>
528         /// 	<c>true</c> if is the name us unique; otherwise, <c>false</c>.
529         /// </returns>
INameController.IsUniqueName(string name)530         bool INameController.IsUniqueName(string name)
531         {
532             return this.IsUniqueName(name);
533         }
534 
535         /// <summary>
536         /// Gets or sets a value indicating whether this instance is in edit mode by collecrtion editor.
537         /// </summary>
538         /// <value>
539         /// 	<c>true</c> if this instance the colection is editing; otherwise, <c>false</c>.
540         /// </value>
541         bool INameController.IsColectionEditing
542         {
543             get
544             {
545                 return _disableDeleteCount == 0;
546             }
547             set
548             {
549                 _disableDeleteCount += value ? 1 : -1;
550             }
551         }
552 
553         /// <summary>
554         /// Raises the <see cref="E:NameReferenceChanging"/> event.
555         /// </summary>
556         /// <param name="e">The <see cref="NameReferenceChangedEventArgs"/> instance containing the event data.</param>
INameController.OnNameReferenceChanging(NameReferenceChangedEventArgs e)557         void INameController.OnNameReferenceChanging(NameReferenceChangedEventArgs e)
558         {
559             if (!IsSuspended)
560             {
561                 if (this.NameReferenceChanging != null)
562                     this.NameReferenceChanging(this, e);
563             }
564         }
565 
566         /// <summary>
567         /// Raises the <see cref="E:NameReferenceChanged"/> event.
568         /// </summary>
569         /// <param name="e">The <see cref="NameReferenceChangedEventArgs"/> instance containing the event data.</param>
INameController.OnNameReferenceChanged(NameReferenceChangedEventArgs e)570         void INameController.OnNameReferenceChanged(NameReferenceChangedEventArgs e)
571         {
572             if (!IsSuspended)
573             {
574                 if (this.NameReferenceChanged != null)
575                     this.NameReferenceChanged(this, e);
576             }
577         }
578 
579         /// <summary>
580         /// Does the snapshot of collection items.
581         /// </summary>
582         /// <param name="save">if set to <c>true</c> collection items will be saved.</param>
583         /// <param name="changingCallback">The changing callback.</param>
584         /// <param name="changedCallback">The changed callback.</param>
INameController.DoSnapshot(bool save, EventHandler<NameReferenceChangedEventArgs> changingCallback, EventHandler<NameReferenceChangedEventArgs> changedCallback)585         void INameController.DoSnapshot(bool save,
586             EventHandler<NameReferenceChangedEventArgs> changingCallback,
587             EventHandler<NameReferenceChangedEventArgs> changedCallback)
588         {
589             if (save)
590             {
591                 _cachedState = new List<T>(this);
592                 if (changingCallback != null) this.NameReferenceChanging += changingCallback;
593                 if (changedCallback  != null) this.NameReferenceChanged += changedCallback;
594             }
595             else
596             {
597                 if (changingCallback != null) this.NameReferenceChanging -= changingCallback;
598                 if (changedCallback != null) this.NameReferenceChanged -= changedCallback;
599                 _cachedState.Clear();
600                 _cachedState = null;
601             }
602         }
603 
604         /// <summary>
605         /// Gets the snapshot of saved collection items.
606         /// </summary>
607         /// <value>The snapshot.</value>
608         IList INameController.Snapshot
609         {
610             get { return _cachedState; }
611         }
612 
613 
614         #endregion
615 
616 
617     }
618 
619 }
620