1 //------------------------------------------------------------------------------ 2 // <copyright file="DataColumnCollection.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // <owner current="true" primary="true">Microsoft</owner> 6 // <owner current="true" primary="false">Microsoft</owner> 7 // <owner current="false" primary="false">Microsoft</owner> 8 //------------------------------------------------------------------------------ 9 10 namespace System.Data { 11 using System; 12 using System.Xml; 13 using System.Collections; 14 using System.Collections.Generic; 15 using System.ComponentModel; 16 using System.Data.Common; 17 using System.Diagnostics; 18 19 /// <devdoc> 20 /// <para>Represents a collection of <see cref='System.Data.DataColumn'/> 21 /// objects for a <see cref='System.Data.DataTable'/>.</para> 22 /// </devdoc> 23 [ 24 DefaultEvent("CollectionChanged"), 25 Editor("Microsoft.VSDesigner.Data.Design.ColumnsCollectionEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), 26 ] 27 public sealed class DataColumnCollection : InternalDataCollectionBase { 28 29 private readonly DataTable table; 30 private readonly ArrayList _list = new ArrayList(); 31 private int defaultNameIndex = 1; 32 private DataColumn[] delayedAddRangeColumns; 33 34 private readonly Dictionary<string, DataColumn> columnFromName; // Links names to columns 35 private CollectionChangeEventHandler onCollectionChangedDelegate; 36 private CollectionChangeEventHandler onCollectionChangingDelegate; 37 38 private CollectionChangeEventHandler onColumnPropertyChangedDelegate; 39 40 private bool fInClear; 41 42 private DataColumn[] columnsImplementingIChangeTracking = DataTable.zeroColumns; 43 private int nColumnsImplementingIChangeTracking = 0; 44 private int nColumnsImplementingIRevertibleChangeTracking = 0; 45 46 /// <devdoc> 47 /// DataColumnCollection constructor. Used only by DataTable. 48 /// </devdoc> DataColumnCollection(DataTable table)49 internal DataColumnCollection(DataTable table) { 50 this.table = table; 51 columnFromName = new Dictionary<string, DataColumn>(); 52 } 53 54 /// <devdoc> 55 /// <para>Gets the list of the collection items.</para> 56 /// </devdoc> 57 protected override ArrayList List { 58 get { 59 return _list; 60 } 61 } 62 63 internal DataColumn[] ColumnsImplementingIChangeTracking { 64 get { 65 return columnsImplementingIChangeTracking; 66 } 67 } 68 internal int ColumnsImplementingIChangeTrackingCount{ 69 get { 70 return nColumnsImplementingIChangeTracking; 71 } 72 } 73 internal int ColumnsImplementingIRevertibleChangeTrackingCount { 74 get { 75 return nColumnsImplementingIRevertibleChangeTracking; 76 } 77 } 78 /// <devdoc> 79 /// <para> 80 /// Gets the <see cref='System.Data.DataColumn'/> 81 /// from the collection at the specified index. 82 /// </para> 83 /// </devdoc> 84 public DataColumn this[int index] { 85 get { 86 try { // Perf: use the readonly _list field directly and let ArrayList check the range 87 return (DataColumn)_list[index]; 88 } 89 catch(ArgumentOutOfRangeException) { 90 throw ExceptionBuilder.ColumnOutOfRange(index); 91 } 92 } 93 } 94 95 /// <devdoc> 96 /// <para>Gets the <see cref='System.Data.DataColumn'/> from the collection with the specified name.</para> 97 /// </devdoc> 98 public DataColumn this[string name] { 99 get { 100 if (null == name) { 101 throw ExceptionBuilder.ArgumentNull("name"); 102 } 103 DataColumn column; 104 if ((!columnFromName.TryGetValue(name, out column)) || (column == null)) { 105 // Case-Insensitive compares 106 int index = IndexOfCaseInsensitive(name); 107 if (0 <= index) { 108 column = (DataColumn)_list[index]; 109 } 110 else if (-2 == index) { 111 throw ExceptionBuilder.CaseInsensitiveNameConflict(name); 112 } 113 } 114 return column; 115 } 116 } 117 118 internal DataColumn this[string name, string ns] { 119 get { 120 DataColumn column; 121 if ((columnFromName.TryGetValue(name, out column)) && (column != null) && (column.Namespace == ns)) { 122 return column; 123 } 124 125 return null; 126 } 127 } 128 EnsureAdditionalCapacity(int capacity)129 internal void EnsureAdditionalCapacity(int capacity) { 130 if (_list.Capacity < capacity + _list.Count) { 131 _list.Capacity = capacity + _list.Count; 132 } 133 } 134 135 /// <devdoc> 136 /// <para>Adds the specified <see cref='System.Data.DataColumn'/> 137 /// to the columns collection.</para> 138 /// </devdoc> Add(DataColumn column)139 public void Add(DataColumn column) { 140 AddAt(-1, column); 141 } 142 AddAt(int index, DataColumn column)143 internal void AddAt(int index, DataColumn column) { 144 if (column != null && column.ColumnMapping == MappingType.SimpleContent) { 145 if (table.XmlText != null && table.XmlText != column) 146 throw ExceptionBuilder.CannotAddColumn3(); 147 if (table.ElementColumnCount > 0) 148 throw ExceptionBuilder.CannotAddColumn4(column.ColumnName); 149 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column)); 150 BaseAdd(column); 151 if (index != -1) 152 ArrayAdd(index, column); 153 else 154 ArrayAdd(column); 155 156 table.XmlText = column; 157 } 158 else { 159 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Add, column)); 160 BaseAdd(column); 161 if (index != -1) 162 ArrayAdd(index, column); 163 else 164 ArrayAdd(column); 165 // if the column is an element increase the internal dataTable counter 166 if (column.ColumnMapping == MappingType.Element) 167 table.ElementColumnCount ++; 168 } 169 if (!table.fInitInProgress && column != null && column.Computed) { 170 column.Expression = column.Expression; 171 } 172 OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Add, column)); 173 } 174 /// <devdoc> 175 /// <para>[To be supplied.]</para> 176 /// </devdoc> AddRange(DataColumn[] columns)177 public void AddRange(DataColumn[] columns) { 178 if (table.fInitInProgress) { 179 delayedAddRangeColumns = columns; 180 return; 181 } 182 183 if (columns != null) { 184 foreach(DataColumn column in columns) { 185 if (column != null) { 186 Add(column); 187 } 188 } 189 } 190 } 191 192 /// <devdoc> 193 /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> 194 /// with 195 /// the specified name, type, and compute expression to the columns collection.</para> 196 /// </devdoc> Add(string columnName, Type type, string expression)197 public DataColumn Add(string columnName, Type type, string expression) { 198 DataColumn column = new DataColumn(columnName, type, expression); 199 Add(column); 200 return column; 201 } 202 203 /// <devdoc> 204 /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> 205 /// with the 206 /// specified name and type to the columns collection.</para> 207 /// </devdoc> Add(string columnName, Type type)208 public DataColumn Add(string columnName, Type type) { 209 DataColumn column = new DataColumn(columnName, type); 210 Add(column); 211 return column; 212 } 213 214 /// <devdoc> 215 /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> 216 /// with the specified name to the columns collection.</para> 217 /// </devdoc> Add(string columnName)218 public DataColumn Add(string columnName) { 219 DataColumn column = new DataColumn(columnName); 220 Add(column); 221 return column; 222 } 223 224 /// <devdoc> 225 /// <para>Creates and adds a <see cref='System.Data.DataColumn'/> to a columns collection.</para> 226 /// </devdoc> Add()227 public DataColumn Add() { 228 DataColumn column = new DataColumn(); 229 Add(column); 230 return column; 231 } 232 233 234 /// <devdoc> 235 /// <para>Occurs when the columns collection changes, either by adding or removing a column.</para> 236 /// </devdoc> 237 [ResDescriptionAttribute(Res.collectionChangedEventDescr)] 238 public event CollectionChangeEventHandler CollectionChanged { 239 add { 240 onCollectionChangedDelegate += value; 241 } 242 remove { 243 onCollectionChangedDelegate -= value; 244 } 245 } 246 247 internal event CollectionChangeEventHandler CollectionChanging { 248 add { 249 onCollectionChangingDelegate += value; 250 } 251 remove { 252 onCollectionChangingDelegate -= value; 253 } 254 } 255 256 internal event CollectionChangeEventHandler ColumnPropertyChanged { 257 add { 258 onColumnPropertyChangedDelegate += value; 259 } 260 remove { 261 onColumnPropertyChangedDelegate -= value; 262 } 263 } 264 265 /// <devdoc> 266 /// Adds the column to the columns array. 267 /// </devdoc> ArrayAdd(DataColumn column)268 private void ArrayAdd(DataColumn column) { 269 _list.Add(column); 270 column.SetOrdinalInternal(_list.Count - 1); 271 CheckIChangeTracking(column); 272 } 273 ArrayAdd(int index, DataColumn column)274 private void ArrayAdd(int index, DataColumn column) { 275 _list.Insert(index, column); 276 CheckIChangeTracking(column); 277 } 278 ArrayRemove(DataColumn column)279 private void ArrayRemove(DataColumn column) { 280 column.SetOrdinalInternal(-1); 281 _list.Remove(column); 282 283 int count = _list.Count; 284 for (int i =0; i < count; i++) { 285 ((DataColumn) _list[i]).SetOrdinalInternal(i); 286 } 287 if (column.ImplementsIChangeTracking) { 288 RemoveColumnsImplementingIChangeTrackingList(column); 289 } 290 } 291 292 /// <devdoc> 293 /// Creates a new default name. 294 /// </devdoc> AssignName()295 internal string AssignName() { 296 string newName = MakeName(defaultNameIndex++); 297 298 while (columnFromName.ContainsKey(newName)) { 299 newName = MakeName(defaultNameIndex++); 300 } 301 302 return newName; 303 } 304 305 /// <devdoc> 306 /// Does verification on the column and it's name, and points the column at the dataSet that owns this collection. 307 /// An ArgumentNullException is thrown if this column is null. An ArgumentException is thrown if this column 308 /// already belongs to this collection, belongs to another collection. 309 /// A DuplicateNameException is thrown if this collection already has a column with the same 310 /// name (case insensitive). 311 /// </devdoc> BaseAdd(DataColumn column)312 private void BaseAdd(DataColumn column) { 313 if (column == null) 314 throw ExceptionBuilder.ArgumentNull("column"); 315 if (column.table == table) 316 throw ExceptionBuilder.CannotAddColumn1(column.ColumnName); 317 if (column.table != null) 318 throw ExceptionBuilder.CannotAddColumn2(column.ColumnName); 319 320 if (column.ColumnName.Length == 0) { 321 column.ColumnName = AssignName(); 322 } 323 RegisterColumnName(column.ColumnName, column); 324 try { 325 column.SetTable(table); 326 if (!table.fInitInProgress && column.Computed) { 327 if (column.DataExpression.DependsOn(column)) { 328 throw ExceptionBuilder.ExpressionCircular(); 329 } 330 } 331 332 if (0 < table.RecordCapacity) { 333 // adding a column to table with existing rows 334 column.SetCapacity(table.RecordCapacity); 335 } 336 337 // fill column with default value. 338 for (int record = 0; record < table.RecordCapacity; record++) { 339 column.InitializeRecord(record); 340 } 341 342 if (table.DataSet != null) { 343 column.OnSetDataSet(); 344 } 345 } 346 catch (Exception e) { 347 // 348 if (ADP.IsCatchableOrSecurityExceptionType(e)) { 349 UnregisterName(column.ColumnName); 350 } 351 throw; 352 } 353 } 354 355 /// <devdoc> 356 /// BaseGroupSwitch will intelligently remove and add tables from the collection. 357 /// </devdoc> BaseGroupSwitch(DataColumn[] oldArray, int oldLength, DataColumn[] newArray, int newLength)358 private void BaseGroupSwitch(DataColumn[] oldArray, int oldLength, DataColumn[] newArray, int newLength) { 359 // We're doing a smart diff of oldArray and newArray to find out what 360 // should be removed. We'll pass through oldArray and see if it exists 361 // in newArray, and if not, do remove work. newBase is an opt. in case 362 // the arrays have similar prefixes. 363 int newBase = 0; 364 for (int oldCur = 0; oldCur < oldLength; oldCur++) { 365 bool found = false; 366 for (int newCur = newBase; newCur < newLength; newCur++) { 367 if (oldArray[oldCur] == newArray[newCur]) { 368 if (newBase == newCur) { 369 newBase++; 370 } 371 found = true; 372 break; 373 } 374 } 375 if (!found) { 376 // This means it's in oldArray and not newArray. Remove it. 377 if (oldArray[oldCur].Table == table) { 378 BaseRemove(oldArray[oldCur]); 379 _list.Remove(oldArray[oldCur]); 380 oldArray[oldCur].SetOrdinalInternal(-1); 381 } 382 } 383 } 384 385 // Now, let's pass through news and those that don't belong, add them. 386 for (int newCur = 0; newCur < newLength; newCur++) { 387 if (newArray[newCur].Table != table) { 388 BaseAdd(newArray[newCur]); 389 _list.Add(newArray[newCur]); 390 } 391 newArray[newCur].SetOrdinalInternal(newCur); 392 } 393 } 394 395 /// <devdoc> 396 /// Does verification on the column and it's name, and clears the column's dataSet pointer. 397 /// An ArgumentNullException is thrown if this column is null. An ArgumentException is thrown 398 /// if this column doesn't belong to this collection or if this column is part of a relationship. 399 /// An ArgumentException is thrown if another column's compute expression depends on this column. 400 /// </devdoc> BaseRemove(DataColumn column)401 private void BaseRemove(DataColumn column) { 402 if (CanRemove(column, true)) { 403 404 // remove 405 if (column.errors > 0) { 406 for (int i = 0; i < table.Rows.Count; i++) { 407 table.Rows[i].ClearError(column); 408 } 409 } 410 UnregisterName(column.ColumnName); 411 column.SetTable(null); 412 } 413 } 414 415 /// <devdoc> 416 /// <para>Checks 417 /// if 418 /// a given column can be removed from the collection.</para> 419 /// </devdoc> CanRemove(DataColumn column)420 public bool CanRemove(DataColumn column) { 421 return CanRemove(column, false); 422 } 423 CanRemove(DataColumn column, bool fThrowException)424 internal bool CanRemove(DataColumn column, bool fThrowException) { 425 if (column == null) { 426 if (!fThrowException) 427 return false; 428 else 429 throw ExceptionBuilder.ArgumentNull("column"); 430 } 431 if (column.table != table) { 432 if (!fThrowException) 433 return false; 434 else 435 throw ExceptionBuilder.CannotRemoveColumn(); 436 } 437 438 // allow subclasses to complain first. 439 table.OnRemoveColumnInternal(column); 440 441 // We need to make sure the column is not involved in any Relations or Constriants 442 if (table.primaryKey != null && table.primaryKey.Key.ContainsColumn(column)) { 443 if (!fThrowException) 444 return false; 445 else 446 throw ExceptionBuilder.CannotRemovePrimaryKey(); 447 } 448 for (int i = 0; i < table.ParentRelations.Count; i++) { 449 if (table.ParentRelations[i].ChildKey.ContainsColumn(column)) { 450 if (!fThrowException) 451 return false; 452 else 453 throw ExceptionBuilder.CannotRemoveChildKey(table.ParentRelations[i].RelationName); 454 } 455 } 456 for (int i = 0; i < table.ChildRelations.Count; i++) { 457 if (table.ChildRelations[i].ParentKey.ContainsColumn(column)) { 458 if (!fThrowException) 459 return false; 460 else 461 throw ExceptionBuilder.CannotRemoveChildKey(table.ChildRelations[i].RelationName); 462 } 463 } 464 for (int i = 0; i < table.Constraints.Count; i++) { 465 if (table.Constraints[i].ContainsColumn(column)) 466 if (!fThrowException) 467 return false; 468 else 469 throw ExceptionBuilder.CannotRemoveConstraint(table.Constraints[i].ConstraintName, table.Constraints[i].Table.TableName); 470 } 471 if (table.DataSet != null) { 472 for (ParentForeignKeyConstraintEnumerator en = new ParentForeignKeyConstraintEnumerator(table.DataSet, table); en.GetNext();) { 473 Constraint constraint = en.GetConstraint(); 474 if (((ForeignKeyConstraint)constraint).ParentKey.ContainsColumn(column)) 475 if (!fThrowException) 476 return false; 477 else 478 throw ExceptionBuilder.CannotRemoveConstraint(constraint.ConstraintName, constraint.Table.TableName); 479 } 480 } 481 482 if (column.dependentColumns != null) { 483 for (int i = 0; i < column.dependentColumns.Count; i++) { 484 DataColumn col = column.dependentColumns[i]; 485 if (fInClear && (col.Table == table || col.Table == null)) 486 continue; 487 if (col.Table == null) 488 continue; 489 Debug.Assert(col.Computed, "invalid (non an expression) column in the expression dependent columns"); 490 DataExpression expr = col.DataExpression; 491 if ((expr!= null) && (expr.DependsOn(column))) { 492 if (!fThrowException) 493 return false; 494 else 495 throw ExceptionBuilder.CannotRemoveExpression(col.ColumnName, col.Expression); 496 } 497 } 498 } 499 500 // SQLBU 429176: you can't remove a column participating in an index, 501 // while index events are suspended else the indexes won't be properly maintained. 502 // However, all the above checks should catch those participating columns. 503 // except when a column is in a DataView RowFilter or Sort clause 504 foreach (Index index in table.LiveIndexes) { 505 #if false 506 if (!Object.ReferenceEquals(index, column.sortIndex)) { 507 foreach (IndexField field in index.IndexFields) { 508 if (Object.ReferenceEquals(field.Column, column)) { 509 if (fThrowException) { 510 throw ExceptionBuilder.CannotRemoveExpression("DataView", column.ColumnName); 511 } 512 return false; 513 } 514 } 515 } 516 #endif 517 } 518 519 return true; 520 } 521 CheckIChangeTracking(DataColumn column)522 private void CheckIChangeTracking(DataColumn column) { 523 if (column.ImplementsIRevertibleChangeTracking) { 524 nColumnsImplementingIRevertibleChangeTracking++; 525 nColumnsImplementingIChangeTracking++; 526 AddColumnsImplementingIChangeTrackingList(column); 527 } 528 else if (column.ImplementsIChangeTracking) { 529 nColumnsImplementingIChangeTracking++; 530 AddColumnsImplementingIChangeTrackingList(column); 531 } 532 } 533 534 /// <devdoc> 535 /// <para> 536 /// Clears the collection of any columns. 537 /// </para> 538 /// </devdoc> Clear()539 public void Clear() { 540 int oldLength = _list.Count; 541 542 DataColumn[] columns = new DataColumn[_list.Count]; 543 _list.CopyTo(columns, 0); 544 545 OnCollectionChanging(RefreshEventArgs); 546 547 if (table.fInitInProgress && delayedAddRangeColumns != null) { 548 delayedAddRangeColumns = null; 549 } 550 551 try { 552 // this will smartly add and remove the appropriate tables. 553 fInClear = true; 554 BaseGroupSwitch(columns, oldLength, null, 0); 555 fInClear = false; 556 } 557 catch (Exception e) { 558 // 559 if (ADP.IsCatchableOrSecurityExceptionType(e)) { 560 // something messed up: restore to old values and throw 561 fInClear = false; 562 BaseGroupSwitch(null, 0, columns, oldLength); 563 _list.Clear(); 564 for (int i = 0; i < oldLength; i++) 565 _list.Add(columns[i]); 566 } 567 throw; 568 } 569 _list.Clear(); 570 table.ElementColumnCount = 0; 571 OnCollectionChanged(RefreshEventArgs); 572 } 573 574 /// <devdoc> 575 /// <para>Checks whether the collection contains a column with the specified name.</para> 576 /// </devdoc> Contains(string name)577 public bool Contains(string name) { 578 DataColumn column; 579 if ((columnFromName.TryGetValue(name, out column)) && (column != null)) { 580 return true; 581 } 582 583 return (IndexOfCaseInsensitive(name) >= 0); 584 } 585 Contains(string name, bool caseSensitive)586 internal bool Contains(string name, bool caseSensitive) { 587 DataColumn column; 588 if ((columnFromName.TryGetValue(name, out column)) && (column != null)) { 589 return true; 590 } 591 592 if (caseSensitive) { // above check did case sensitive check 593 return false; 594 } 595 else { 596 return (IndexOfCaseInsensitive(name) >= 0); 597 } 598 } 599 CopyTo(DataColumn[] array, int index)600 public void CopyTo(DataColumn[] array, int index) { 601 if (array==null) 602 throw ExceptionBuilder.ArgumentNull("array"); 603 if (index < 0) 604 throw ExceptionBuilder.ArgumentOutOfRange("index"); 605 if (array.Length - index < _list.Count) 606 throw ExceptionBuilder.InvalidOffsetLength(); 607 for(int i = 0; i < _list.Count; ++i) { 608 array[index + i] = (DataColumn)_list[i]; 609 } 610 } 611 612 /// <devdoc> 613 /// <para> 614 /// Returns the index of a specified <see cref='System.Data.DataColumn'/>. 615 /// </para> 616 /// </devdoc> IndexOf(DataColumn column)617 public int IndexOf(DataColumn column) { 618 int columnCount = _list.Count; 619 for (int i = 0; i < columnCount; ++i) { 620 if (column == (DataColumn) _list[i]) { 621 return i; 622 } 623 } 624 return -1; 625 } 626 627 /// <devdoc> 628 /// <para>Returns the index of 629 /// a column specified by name.</para> 630 /// </devdoc> IndexOf(string columnName)631 public int IndexOf(string columnName) { 632 633 if ((null != columnName) && (0 < columnName.Length)) { 634 int count = Count; 635 DataColumn column; 636 if ((columnFromName.TryGetValue(columnName, out column)) && (column != null)) { 637 for (int j = 0; j < count; j++) 638 if (column == _list[j]) { 639 return j; 640 } 641 } 642 else { 643 int res = IndexOfCaseInsensitive(columnName); 644 return (res < 0) ? -1 : res; 645 } 646 } 647 return -1; 648 } 649 IndexOfCaseInsensitive(string name)650 internal int IndexOfCaseInsensitive (string name) { 651 int hashcode = table.GetSpecialHashCode(name); 652 int cachedI = -1; 653 DataColumn column = null; 654 for (int i = 0; i < Count; i++) { 655 column = (DataColumn) _list[i]; 656 if ( (hashcode == 0 || column._hashCode == 0 || column._hashCode == hashcode) && 657 NamesEqual(column.ColumnName, name, false, table.Locale) != 0 ) { 658 if (cachedI == -1) 659 cachedI = i; 660 else 661 return -2; 662 } 663 } 664 return cachedI; 665 } 666 FinishInitCollection()667 internal void FinishInitCollection() { 668 if (delayedAddRangeColumns != null) { 669 foreach(DataColumn column in delayedAddRangeColumns) { 670 if (column != null) { 671 Add(column); 672 } 673 } 674 675 foreach(DataColumn column in delayedAddRangeColumns) { 676 if (column != null) { 677 column.FinishInitInProgress(); 678 } 679 } 680 681 delayedAddRangeColumns = null; 682 } 683 } 684 685 /// <devdoc> 686 /// Makes a default name with the given index. e.g. Column1, Column2, ... Columni 687 /// </devdoc> MakeName(int index)688 private string MakeName(int index) { 689 if (1 == index) { 690 return "Column1"; 691 } 692 return "Column" + index.ToString(System.Globalization.CultureInfo.InvariantCulture); 693 } 694 MoveTo(DataColumn column, int newPosition)695 internal void MoveTo(DataColumn column, int newPosition) { 696 if (0 > newPosition || newPosition > Count -1) { 697 throw ExceptionBuilder.InvalidOrdinal("ordinal", newPosition); 698 } 699 if (column.ImplementsIChangeTracking) { 700 RemoveColumnsImplementingIChangeTrackingList(column); 701 } 702 _list.Remove(column); 703 _list.Insert(newPosition, column); 704 int count = _list.Count; 705 for (int i =0; i < count; i++) { 706 ((DataColumn) _list[i]).SetOrdinalInternal(i); 707 } 708 CheckIChangeTracking(column); 709 OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Refresh, column)); 710 } 711 712 /// <devdoc> 713 /// <para> 714 /// Raises the <see cref='System.Data.DataColumnCollection.OnCollectionChanged'/> event. 715 /// </para> 716 /// </devdoc> OnCollectionChanged(CollectionChangeEventArgs ccevent)717 private void OnCollectionChanged(CollectionChangeEventArgs ccevent) { 718 table.UpdatePropertyDescriptorCollectionCache(); 719 720 if ((null != ccevent) && !table.SchemaLoading && !table.fInitInProgress) { 721 DataColumn column = (DataColumn)ccevent.Element; 722 } 723 if (onCollectionChangedDelegate != null) { 724 onCollectionChangedDelegate(this, ccevent); 725 } 726 } 727 728 /// <devdoc> 729 /// <para>[To be supplied.]</para> 730 /// </devdoc> OnCollectionChanging(CollectionChangeEventArgs ccevent)731 private void OnCollectionChanging(CollectionChangeEventArgs ccevent) { 732 if (onCollectionChangingDelegate != null) { 733 onCollectionChangingDelegate(this, ccevent); 734 } 735 } 736 OnColumnPropertyChanged(CollectionChangeEventArgs ccevent)737 internal void OnColumnPropertyChanged(CollectionChangeEventArgs ccevent) { 738 table.UpdatePropertyDescriptorCollectionCache(); 739 if (onColumnPropertyChangedDelegate != null) { 740 onColumnPropertyChangedDelegate(this, ccevent); 741 } 742 } 743 744 /// <devdoc> 745 /// Registers this name as being used in the collection. Will throw an ArgumentException 746 /// if the name is already being used. Called by Add, All property, and Column.ColumnName property. 747 /// if the name is equivalent to the next default name to hand out, we increment our defaultNameIndex. 748 /// NOTE: To add a child table, pass column as null 749 /// </devdoc> RegisterColumnName(string name, DataColumn column)750 internal void RegisterColumnName(string name, DataColumn column) { 751 Debug.Assert (name != null); 752 753 try { 754 columnFromName.Add(name, column); 755 756 if (null != column) { 757 column._hashCode = table.GetSpecialHashCode(name); 758 } 759 } 760 catch (ArgumentException) { // Argument exception means that there is already an existing key 761 if (columnFromName[name] != null) { 762 if (column != null) { 763 throw ExceptionBuilder.CannotAddDuplicate(name); 764 } 765 else { 766 throw ExceptionBuilder.CannotAddDuplicate3(name); 767 } 768 } 769 throw ExceptionBuilder.CannotAddDuplicate2(name); 770 } 771 772 // If we're adding a child table, then update defaultNameIndex to avoid colisions between the child table and auto-generated column names 773 if ((column == null) && NamesEqual(name, MakeName(defaultNameIndex), true, table.Locale) != 0) { 774 do { 775 defaultNameIndex++; 776 } while (Contains(MakeName(defaultNameIndex))); 777 } 778 } 779 CanRegisterName(string name)780 internal bool CanRegisterName(string name) { 781 Debug.Assert (name != null, "Must specify a name"); 782 return (!columnFromName.ContainsKey(name)); 783 } 784 785 /// <devdoc> 786 /// <para>Removes the specified <see cref='System.Data.DataColumn'/> 787 /// from the collection.</para> 788 /// </devdoc> Remove(DataColumn column)789 public void Remove(DataColumn column) { 790 OnCollectionChanging(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column)); 791 BaseRemove(column); 792 ArrayRemove(column); 793 OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, column)); 794 // if the column is an element decrease the internal dataTable counter 795 if (column.ColumnMapping == MappingType.Element) 796 table.ElementColumnCount --; 797 } 798 799 /// <devdoc> 800 /// <para>Removes the 801 /// column at the specified index from the collection.</para> 802 /// </devdoc> RemoveAt(int index)803 public void RemoveAt(int index) { 804 DataColumn dc = this[index]; 805 if (dc == null) 806 throw ExceptionBuilder.ColumnOutOfRange(index); 807 Remove(dc); 808 } 809 810 /// <devdoc> 811 /// <para>Removes the 812 /// column with the specified name from the collection.</para> 813 /// </devdoc> Remove(string name)814 public void Remove(string name) { 815 DataColumn dc = this[name]; 816 if (dc == null) 817 throw ExceptionBuilder.ColumnNotInTheTable(name, table.TableName); 818 Remove(dc); 819 } 820 821 /// <devdoc> 822 /// Unregisters this name as no longer being used in the collection. Called by Remove, All property, and 823 /// Column.ColumnName property. If the name is equivalent to the last proposed default name, we walk backwards 824 /// to find the next proper default name to use. 825 /// </devdoc> UnregisterName(string name)826 internal void UnregisterName(string name) { 827 columnFromName.Remove(name); 828 829 if (NamesEqual(name, MakeName(defaultNameIndex - 1), true, table.Locale) != 0) { 830 do { 831 defaultNameIndex--; 832 } while (defaultNameIndex > 1 && 833 !Contains(MakeName(defaultNameIndex - 1))); 834 } 835 } 836 AddColumnsImplementingIChangeTrackingList(DataColumn dataColumn)837 private void AddColumnsImplementingIChangeTrackingList(DataColumn dataColumn) { 838 DataColumn[] columns = columnsImplementingIChangeTracking; 839 DataColumn[] tempColumns = new DataColumn[columns.Length +1]; 840 columns.CopyTo(tempColumns, 0); 841 tempColumns[columns.Length] = dataColumn; 842 columnsImplementingIChangeTracking = tempColumns; 843 } 844 RemoveColumnsImplementingIChangeTrackingList(DataColumn dataColumn)845 private void RemoveColumnsImplementingIChangeTrackingList(DataColumn dataColumn) { 846 DataColumn[] columns = columnsImplementingIChangeTracking; 847 DataColumn[] tempColumns = new DataColumn[columns.Length - 1]; 848 for(int i = 0, j = 0; i < columns.Length; i++) { 849 if (columns[i] != dataColumn) { 850 tempColumns[j++] = columns[i]; 851 } 852 } 853 columnsImplementingIChangeTracking = tempColumns; 854 } 855 } 856 } 857