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<T>. 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<T>"/> 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<T>"/> 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