2 // System.Data.DataTable.cs
5 // Franklin Wise <gracenote@earthlink.net>
6 // Christopher Podurgiel (cpodurgiel@msn.com)
7 // Daniel Morgan <danmorg@sc.rr.com>
8 // Rodrigo Moya <rodrigo@ximian.com>
9 // Tim Coleman (tim@timcoleman.com)
10 // Ville Palo <vi64pa@koti.soon.fi>
12 // (C) Chris Podurgiel
13 // (C) Ximian, Inc 2002
14 // Copyright (C) Tim Coleman, 2002
15 // Copyright (C) Daniel Morgan, 2002-2003
19 using System.Collections;
20 using System.ComponentModel;
21 using System.Globalization;
22 using System.Runtime.Serialization;
24 namespace System.Data {
27 [DefaultEvent ("RowChanging")]
28 [DefaultProperty ("TableName")]
29 [DesignTimeVisible (false)]
31 public class DataTable : MarshalByValueComponent, IListSource, ISupportInitialize, ISerializable
33 internal DataSet dataSet;
35 private bool _caseSensitive;
36 private DataColumnCollection _columnCollection;
37 private ConstraintCollection _constraintCollection;
38 private DataView _defaultView;
40 private string _displayExpression;
41 private PropertyCollection _extendedProperties;
42 private bool _hasErrors;
43 private CultureInfo _locale;
44 private int _minimumCapacity;
45 private string _nameSpace;
46 private DataRelationCollection _childRelations;
47 private DataRelationCollection _parentRelations;
48 private string _prefix;
49 private DataColumn[] _primaryKey;
50 private DataRowCollection _rows;
52 private string _tableName;
53 private bool _containsListCollection;
54 private string _encodedTableName;
55 internal bool _duringDataLoad;
56 private bool dataSetPrevEnforceConstraints;
60 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
61 // CaseSensitive property. So when you lost you virginity it's gone for ever
62 private bool _virginCaseSensitive = true;
65 /// Initializes a new instance of the DataTable class with no arguments.
71 _columnCollection = new DataColumnCollection(this);
72 _constraintCollection = new ConstraintCollection(this);
73 _extendedProperties = new PropertyCollection();
76 _caseSensitive = false; //default value
77 _displayExpression = null;
80 _rows = new DataRowCollection (this);
82 //LAMESPEC: spec says 25 impl does 50
83 _minimumCapacity = 50;
85 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
86 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
89 _defaultView = new DataView(this);
93 /// Intitalizes a new instance of the DataTable class with the specified table name.
95 public DataTable (string tableName) : this ()
97 _tableName = tableName;
101 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
104 protected DataTable (SerializationInfo info, StreamingContext context)
108 // TODO: Add constructor logic here
113 /// Indicates whether string comparisons within the table are case-sensitive.
115 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
116 public bool CaseSensitive {
117 get { return _caseSensitive; }
119 _virginCaseSensitive = false;
120 _caseSensitive = value;
124 internal bool VirginCaseSensitive {
125 get { return _virginCaseSensitive; }
126 set { _virginCaseSensitive = value; }
129 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
131 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
135 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
137 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
138 OnColumnChanging (e);
141 internal void DeletedDataRow (DataRow dr, DataRowAction action)
143 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
147 internal void DeletingDataRow (DataRow dr, DataRowAction action)
149 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
153 internal void ChangedDataRow (DataRow dr, DataRowAction action)
155 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
159 internal void ChangingDataRow (DataRow dr, DataRowAction action)
161 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
166 /// Gets the collection of child relations for this DataTable.
169 [DataSysDescription ("Returns the child relations for this table.")]
170 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
171 public DataRelationCollection ChildRelations {
173 return _childRelations;
178 /// Gets the collection of columns that belong to this table.
180 [DataCategory ("Data")]
181 [DataSysDescription ("The collection that holds the columns for this table.")]
182 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
183 public DataColumnCollection Columns {
184 get { return _columnCollection; }
188 /// Gets the collection of constraints maintained by this table.
190 [DataCategory ("Data")]
191 [DataSysDescription ("The collection that holds the constraints for this table.")]
192 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
193 public ConstraintCollection Constraints {
194 get { return _constraintCollection; }
198 /// Gets the DataSet that this table belongs to.
201 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
202 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
203 public DataSet DataSet {
204 get { return dataSet; }
210 /// Gets a customized view of the table which may
211 /// include a filtered view, or a cursor position.
215 [DataSysDescription ("This is the default DataView for the table.")]
216 public DataView DefaultView {
217 get { return _defaultView; }
222 /// Gets or sets the expression that will return
223 /// a value used to represent this table in the user interface.
225 [DataCategory ("Data")]
226 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
228 public string DisplayExpression {
229 get { return "" + _displayExpression; }
230 set { _displayExpression = value; }
234 /// Gets the collection of customized user information.
237 [DataCategory ("Data")]
238 [DataSysDescription ("The collection that holds custom user information.")]
239 public PropertyCollection ExtendedProperties {
240 get { return _extendedProperties; }
244 /// Gets a value indicating whether there are errors in
245 /// any of the_rows in any of the tables of the DataSet to
246 /// which the table belongs.
249 [DataSysDescription ("Returns whether the table has errors.")]
250 public bool HasErrors {
252 // we can not use the _hasError flag because we do not know when to turn it off!
253 for (int i = 0; i < _rows.Count; i++)
255 if (_rows[i].HasErrors)
263 /// Gets or sets the locale information used to
264 /// compare strings within the table.
266 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
267 public CultureInfo Locale {
269 // if the locale is null, we check for the DataSet locale
270 // and if the DataSet is null we return the current culture.
271 // this way if DataSet locale is changed, only if there is no locale for
272 // the DataTable it influece the Locale get;
276 return DataSet.Locale;
277 return CultureInfo.CurrentCulture;
280 if (_locale == null || !_locale.Equals(value))
286 /// Gets or sets the initial starting size for this table.
288 [DataCategory ("Data")]
289 [DataSysDescription ("Indicates an initial starting size for this table.")]
291 public int MinimumCapacity {
292 get { return _minimumCapacity; }
293 set { _minimumCapacity = value; }
297 /// Gets or sets the namespace for the XML represenation
298 /// of the data stored in the DataTable.
300 [DataCategory ("Data")]
301 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
302 public string Namespace {
303 get { return "" + _nameSpace; }
304 set { _nameSpace = value; }
308 /// Gets the collection of parent relations for
312 [DataSysDescription ("Returns the parent relations for this table.")]
313 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
314 public DataRelationCollection ParentRelations {
316 return _parentRelations;
321 /// Gets or sets the namespace for the XML represenation
322 /// of the data stored in the DataTable.
324 [DataCategory ("Data")]
325 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
327 public string Prefix {
328 get { return "" + _prefix; }
329 set { _prefix = value; }
333 /// Gets or sets an array of columns that function as
334 /// primary keys for the data table.
336 [DataCategory ("Data")]
337 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
338 public DataColumn[] PrimaryKey {
340 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
341 if (null == uc) return new DataColumn[] {};
346 //YUK: msft removes a previous unique constraint if it is flagged as a pk
347 //when a new pk is set
349 //clear Primary Key if value == null
352 RemoveUniqueConstraints ();
356 //Does constraint exist for these columns
357 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
358 this.Constraints, (DataColumn[]) value);
360 //if constraint doesn't exist for columns
361 //create new unique primary key constraint
364 RemoveUniqueConstraints ();
366 foreach (DataColumn Col in (DataColumn[]) value) {
368 if (Col.Table == null)
371 if (Columns.IndexOf (Col) < 0)
372 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
376 uc = new UniqueConstraint( (DataColumn[]) value, true);
378 Constraints.Add (uc);
380 else { //set existing constraint as the new primary key
381 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
388 /// Gets the collection of_rows that belong to this table.
391 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
392 public DataRowCollection Rows {
393 get { return _rows; }
397 /// Gets or sets an System.ComponentModel.ISite
398 /// for the DataTable.
401 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
402 public override ISite Site {
403 get { return _site; }
404 set { _site = value; }
408 /// Gets or sets the name of the the DataTable.
410 [DataCategory ("Data")]
411 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
413 [RefreshProperties (RefreshProperties.All)]
414 public string TableName {
415 get { return "" + _tableName; }
416 set { _tableName = value; }
419 bool IListSource.ContainsListCollection {
421 // the collection is a DataView
427 /// Commits all the changes made to this table since the
428 /// last time AcceptChanges was called.
430 public void AcceptChanges ()
432 //FIXME: Do we need to validate anything here or
433 //try to catch any errors to deal with them?
435 // we do not use foreach because if one of the rows is in Delete state
436 // it will be romeved from Rows and we get an exception.
438 for (int i = 0; i < Rows.Count; )
441 myRow.AcceptChanges();
443 // if the row state is Detached it meens that it was removed from row list (Rows)
444 // so we should not increase 'i'.
445 if (myRow.RowState != DataRowState.Detached)
451 /// Begins the initialization of a DataTable that is used
452 /// on a form or used by another component. The initialization
453 /// occurs at runtime.
455 public void BeginInit ()
460 /// Turns off notifications, index maintenance, and
461 /// constraints while loading data.
464 public void BeginLoadData ()
466 if (!this._duringDataLoad)
468 //duringDataLoad is important to EndLoadData and
469 //for not throwing unexpected exceptions.
470 this._duringDataLoad = true;
472 if (this.dataSet != null)
474 //Saving old Enforce constraints state for later
475 //use in the EndLoadData.
476 this.dataSetPrevEnforceConstraints = this.dataSet.EnforceConstraints;
477 this.dataSet.EnforceConstraints = false;
484 /// Clears the DataTable of all data.
486 public void Clear () {
487 // TODO: thow an exception if any rows that
488 // have enforced child relations
489 // that would result in child rows being orphaned
490 // now we check if any ForeignKeyConstraint is referncing 'table'.
493 IEnumerator tableEnumerator = DataSet.Tables.GetEnumerator();
495 // loop on all tables in dataset
496 while (tableEnumerator.MoveNext())
498 IEnumerator constraintEnumerator = ((DataTable) tableEnumerator.Current).Constraints.GetEnumerator();
499 // loop on all constrains in the current table
500 while (constraintEnumerator.MoveNext())
502 Object o = constraintEnumerator.Current;
503 // we only check ForeignKeyConstraint
504 if (o is ForeignKeyConstraint)
506 ForeignKeyConstraint fc = (ForeignKeyConstraint) o;
507 if(fc.RelatedTable == this && fc.Table.Rows.Count > 0)
508 throw new InvalidConstraintException("Cannot clear table " + fc.RelatedTable + " because ForeignKeyConstraint " + fc.ConstraintName + " enforces constraints and there are child rows in " + fc.Table);
514 // TODO: throw a NotSupportedException if the DataTable is part
515 // of a DataSet that is binded to an XmlDataDocument
521 /// Clones the structure of the DataTable, including
522 /// all DataTable schemas and constraints.
525 public virtual DataTable Clone ()
527 DataTable Copy = new DataTable ();
529 CopyProperties (Copy);
534 /// Computes the given expression on the current_rows that
535 /// pass the filter criteria.
538 public object Compute (string expression, string filter)
540 // TODO: implement this function based
541 // on Select (expression)
543 // expression is an aggregate function
544 // filter is an expression used to limit rows
549 ExpressionElement Expression = new ExpressionMainElement (filter);
551 ArrayList List = new ArrayList ();
552 foreach (DataRow Row in Rows) {
554 if (Expression.Test (Row))
558 DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
560 // TODO: with the filtered rows, execute the aggregate function
561 // mentioned in expression
567 /// Copies both the structure and data for this DataTable.
570 public DataTable Copy ()
572 DataTable Copy = new DataTable ();
573 CopyProperties (Copy);
575 // we can not simply copy the row values (NewRow [C.ColumnName] = Row [C.ColumnName])
576 // because if the row state is deleted we get an exception.
577 foreach (DataRow Row in Rows) {
578 DataRow NewRow = Copy.NewRow ();
579 Copy.Rows.Add (NewRow);
580 Row.CopyValuesToRow(NewRow);
587 private void CopyProperties (DataTable Copy)
590 Copy.CaseSensitive = CaseSensitive;
591 Copy.VirginCaseSensitive = VirginCaseSensitive;
593 // Copy.ChildRelations
598 Copy.DisplayExpression = DisplayExpression;
599 // Copy.ExtendedProperties
600 Copy.Locale = Locale;
601 Copy.MinimumCapacity = MinimumCapacity;
602 Copy.Namespace = Namespace;
603 // Copy.ParentRelations
604 Copy.Prefix = Prefix;
606 Copy.TableName = TableName;
611 foreach (DataColumn Column in Columns) {
613 Copy.Columns.Add (CopyColumn (Column));
616 CopyConstraints(Copy);
617 // add primary key to the copy
618 if (PrimaryKey.Length > 0)
620 DataColumn[] pColumns = new DataColumn[PrimaryKey.Length];
621 for (int i = 0; i < pColumns.Length; i++)
622 pColumns[i] = Copy.Columns[PrimaryKey[i].ColumnName];
624 Copy.PrimaryKey = pColumns;
628 private void CopyConstraints(DataTable copy)
630 UniqueConstraint origUc;
631 UniqueConstraint copyUc;
632 for (int i = 0; i < this.Constraints.Count; i++)
634 if (this.Constraints[i] is UniqueConstraint)
636 origUc = (UniqueConstraint)this.Constraints[i];
637 DataColumn[] columns = new DataColumn[origUc.Columns.Length];
638 for (int j = 0; j < columns.Length; j++)
639 columns[j] = copy.Columns[origUc.Columns[j].ColumnName];
641 copyUc = new UniqueConstraint(origUc.ConstraintName, columns, origUc.IsPrimaryKey);
642 copy.Constraints.Add(copyUc);
647 /// Ends the initialization of a DataTable that is used
648 /// on a form or used by another component. The
649 /// initialization occurs at runtime.
652 public void EndInit ()
658 /// Turns on notifications, index maintenance, and
659 /// constraints after loading data.
662 public void EndLoadData()
665 if (this._duringDataLoad)
667 //Returning from loading mode, raising exceptions as usual
668 this._duringDataLoad = false;
670 if (this.dataSet !=null)
672 //Getting back to previous EnforceConstraint state
673 this.dataSet.EnforceConstraints = this.dataSetPrevEnforceConstraints;
675 for (i=0 ; i<this.Rows.Count ; i++)
677 if (this.Rows[i]._nullConstraintViolation )
679 throw new ConstraintException ("Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.");
689 /// Gets a copy of the DataTable that contains all
690 /// changes made to it since it was loaded or
691 /// AcceptChanges was last called.
694 public DataTable GetChanges()
696 return GetChanges(DataRowState.Added | DataRowState.Deleted | DataRowState.Modified);
700 /// Gets a copy of the DataTable containing all
701 /// changes made to it since it was last loaded, or
702 /// since AcceptChanges was called, filtered by DataRowState.
705 public DataTable GetChanges(DataRowState rowStates)
707 DataTable copyTable = null;
709 IEnumerator rowEnumerator = Rows.GetEnumerator();
710 while (rowEnumerator.MoveNext()) {
711 DataRow row = (DataRow)rowEnumerator.Current;
712 if (row.IsRowChanged(rowStates)) {
713 if (copyTable == null)
715 DataRow newRow = copyTable.NewRow();
716 copyTable.Rows.Add(newRow);
717 row.CopyValuesToRow(newRow);
725 /// Gets an array of DataRow objects that contain errors.
728 public DataRow[] GetErrors ()
730 ArrayList errors = new ArrayList();
731 for (int i = 0; i < _rows.Count; i++)
733 if (_rows[i].HasErrors)
734 errors.Add(_rows[i]);
737 return (DataRow[]) errors.ToArray(typeof(DataRow));
741 /// This member is only meant to support Mono's infrastructure
743 protected virtual DataTable CreateInstance ()
745 return Activator.CreateInstance (this.GetType (), true) as DataTable;
749 /// This member is only meant to support Mono's infrastructure
751 protected virtual Type GetRowType ()
753 return typeof (DataRow);
757 /// This member is only meant to support Mono's infrastructure
759 /// Used for Data Binding between System.Web.UI. controls
762 /// System.Windows.Forms controls like a DataGrid
764 IList IListSource.GetList ()
766 IList list = (IList) _defaultView;
771 /// Copies a DataRow into a DataTable, preserving any
772 /// property settings, as well as original and current values.
775 public void ImportRow (DataRow row)
777 DataRow newRow = NewRow();
779 row.CopyValuesToRow(newRow);
784 /// This member is only meant to support Mono's infrastructure
787 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
792 /// Finds and updates a specific row. If no matching row
793 /// is found, a new row is created using the given values.
796 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
799 if (PrimaryKey.Length == 0) {
800 row = Rows.Add (values);
802 row.AcceptChanges ();
805 bool hasPrimaryValues = true;
806 // initiate an array that has the values of the primary keys.
807 object[] keyValues = new object[PrimaryKey.Length];
808 for (int i = 0; i < keyValues.Length && hasPrimaryValues; i++)
810 if(PrimaryKey[i].Ordinal < values.Length)
811 keyValues[i] = values[PrimaryKey[i].Ordinal];
813 hasPrimaryValues = false;
816 if (hasPrimaryValues){
817 // find the row in the table.
818 row = Rows.Find(keyValues);
822 row = Rows.Add (values);
824 row.ItemArray = values;
827 row.AcceptChanges ();
834 /// Creates a new DataRow with the same schema as the table.
836 public DataRow NewRow ()
838 return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
842 /// This member supports the .NET Framework infrastructure
843 /// and is not intended to be used directly from your code.
845 protected internal DataRow[] NewRowArray (int size)
847 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
851 /// Creates a new row from an existing row.
853 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
855 return new DataRow (builder);
860 /// Rolls back all changes that have been made to the
861 /// table since it was loaded, or the last time AcceptChanges
865 public void RejectChanges ()
867 for (int i = _rows.Count - 1; i >= 0; i--) {
868 DataRow row = _rows [i];
869 if (row.RowState != DataRowState.Unchanged)
870 _rows [i].RejectChanges ();
875 /// Resets the DataTable to its original state.
878 public virtual void Reset ()
881 while (ParentRelations.Count > 0)
883 if (dataSet.Relations.Contains(ParentRelations[ParentRelations.Count - 1].RelationName))
884 dataSet.Relations.Remove(ParentRelations[ParentRelations.Count - 1]);
887 while (ChildRelations.Count > 0)
889 if (dataSet.Relations.Contains(ChildRelations[ChildRelations.Count - 1].RelationName))
890 dataSet.Relations.Remove(ChildRelations[ChildRelations.Count - 1]);
897 /// Gets an array of all DataRow objects.
899 public DataRow[] Select ()
901 return Select(String.Empty, String.Empty, DataViewRowState.CurrentRows);
905 /// Gets an array of all DataRow objects that match
906 /// the filter criteria in order of primary key (or
907 /// lacking one, order of addition.)
909 public DataRow[] Select (string filterExpression)
911 return Select(filterExpression, String.Empty, DataViewRowState.CurrentRows);
915 /// Gets an array of all DataRow objects that
916 /// match the filter criteria, in the the
917 /// specified sort order.
919 public DataRow[] Select (string filterExpression, string sort)
921 return Select(filterExpression, sort, DataViewRowState.CurrentRows);
925 /// Gets an array of all DataRow objects that match
926 /// the filter in the order of the sort, that match
927 /// the specified state.
930 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
932 DataRow[] dataRows = null;
933 if (filterExpression == null)
934 filterExpression = String.Empty;
936 ExpressionElement Expression = null;
937 if (filterExpression != null && filterExpression.Length != 0)
938 Expression = new ExpressionMainElement(filterExpression);
940 ArrayList List = new ArrayList();
941 int recordStateFilter = GetRowStateFilter(recordStates);
942 foreach (DataRow Row in Rows)
944 if (((int)Row.RowState & recordStateFilter) != 0)
946 if (Expression == null || Expression.Test(Row))
951 dataRows = (DataRow[])List.ToArray(typeof(DataRow));
954 if (sort != null && !sort.Equals(String.Empty))
956 SortableColumn[] sortableColumns = null;
958 sortableColumns = ParseTheSortString (sort);
959 if (sortableColumns == null)
960 throw new Exception ("sort expression result is null");
961 if (sortableColumns.Length == 0)
962 throw new Exception("sort expression result is 0");
964 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
965 dataRows = rowSorter.SortRows ();
967 sortableColumns = null;
975 private static int GetRowStateFilter(DataViewRowState recordStates)
979 if ((recordStates & DataViewRowState.Added) != 0)
980 flag |= (int)DataRowState.Added;
981 if ((recordStates & DataViewRowState.Deleted) != 0)
982 flag |= (int)DataRowState.Deleted;
983 if ((recordStates & DataViewRowState.ModifiedCurrent) != 0)
984 flag |= (int)DataRowState.Modified;
985 if ((recordStates & DataViewRowState.ModifiedOriginal) != 0)
986 flag |= (int)DataRowState.Modified;
987 if ((recordStates & DataViewRowState.Unchanged) != 0)
988 flag |= (int)DataRowState.Unchanged;
994 /// Gets the TableName and DisplayExpression, if
995 /// there is one as a concatenated string.
997 public override string ToString()
999 //LAMESPEC: spec says concat the two. impl puts a
1000 //plus sign infront of DisplayExpression
1001 string retVal = TableName;
1002 if(DisplayExpression != null && DisplayExpression != "")
1003 retVal += " + " + DisplayExpression;
1008 #region Events /////////////////
1011 /// Raises the ColumnChanged event.
1013 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
1014 if (null != ColumnChanged) {
1015 ColumnChanged (this, e);
1020 /// Raises the ColumnChanging event.
1022 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
1023 if (null != ColumnChanging) {
1024 ColumnChanging (this, e);
1029 /// Raises the PropertyChanging event.
1032 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
1033 // if (null != PropertyChanging)
1035 // PropertyChanging (this, e);
1040 /// Notifies the DataTable that a DataColumn is being removed.
1043 protected internal virtual void OnRemoveColumn (DataColumn column) {
1044 // if (null != RemoveColumn)
1046 // RemoveColumn(this, e);
1051 /// Raises the RowChanged event.
1053 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
1054 if (null != RowChanged) {
1055 RowChanged(this, e);
1061 /// Raises the RowChanging event.
1063 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
1064 if (null != RowChanging) {
1065 RowChanging(this, e);
1070 /// Raises the RowDeleted event.
1072 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
1073 if (null != RowDeleted) {
1074 RowDeleted(this, e);
1079 /// Raises the RowDeleting event.
1081 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
1082 if (null != RowDeleting) {
1083 RowDeleting(this, e);
1088 private DataColumn CopyColumn (DataColumn Column) {
1089 DataColumn Copy = new DataColumn ();
1091 // Copy all the properties of column
1092 Copy.AllowDBNull = Column.AllowDBNull;
1093 Copy.AutoIncrement = Column.AutoIncrement;
1094 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
1095 Copy.AutoIncrementStep = Column.AutoIncrementStep;
1096 Copy.Caption = Column.Caption;
1097 Copy.ColumnMapping = Column.ColumnMapping;
1098 Copy.ColumnName = Column.ColumnName;
1100 Copy.DataType = Column.DataType;
1101 Copy.DefaultValue = Column.DefaultValue;
1102 Copy.Expression = Column.Expression;
1103 //Copy.ExtendedProperties
1104 Copy.MaxLength = Column.MaxLength;
1105 Copy.Namespace = Column.Namespace;
1106 Copy.Prefix = Column.Prefix;
1107 Copy.ReadOnly = Column.ReadOnly;
1109 //we do not copy the unique value - it will be copyied when copying the constraints.
1110 //Copy.Unique = Column.Unique;
1116 /// Occurs when after a value has been changed for
1117 /// the specified DataColumn in a DataRow.
1119 [DataCategory ("Data")]
1120 [DataSysDescription ("Occurs when a value has been changed for this column.")]
1121 public event DataColumnChangeEventHandler ColumnChanged;
1124 /// Occurs when a value is being changed for the specified
1125 /// DataColumn in a DataRow.
1127 [DataCategory ("Data")]
1128 [DataSysDescription ("Occurs when a value has been submitted for this column. The user can modify the proposed value and should throw an exception to cancel the edit.")]
1129 public event DataColumnChangeEventHandler ColumnChanging;
1132 /// Occurs after a DataRow has been changed successfully.
1134 [DataCategory ("Data")]
1135 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
1136 public event DataRowChangeEventHandler RowChanged;
1139 /// Occurs when a DataRow is changing.
1141 [DataCategory ("Data")]
1142 [DataSysDescription ("Occurs when the row is being changed so that the event handler can modify or cancel the change. The user can modify values in the row and should throw an exception to cancel the edit.")]
1143 public event DataRowChangeEventHandler RowChanging;
1146 /// Occurs after a row in the table has been deleted.
1148 [DataCategory ("Data")]
1149 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
1150 public event DataRowChangeEventHandler RowDeleted;
1153 /// Occurs before a row in the table is about to be deleted.
1155 [DataCategory ("Data")]
1156 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
1157 public event DataRowChangeEventHandler RowDeleting;
1159 #endregion // Events
1162 /// Removes all UniqueConstraints
1164 private void RemoveUniqueConstraints ()
1166 foreach (Constraint Cons in Constraints) {
1168 if (Cons is UniqueConstraint) {
1169 Constraints.Remove (Cons);
1174 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
1177 // to parse the sort string for DataTable:Select(expression,sort)
1178 // into sortable columns (think ORDER BY,
1179 // such as, "customer ASC, price DESC" )
1180 private SortableColumn[] ParseTheSortString (string sort)
1182 SortableColumn[] sortColumns = null;
1183 ArrayList columns = null;
1185 if (sort != null && !sort.Equals ("")) {
1186 columns = new ArrayList ();
1187 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
1189 for (int c = 0; c < columnExpression.Length; c++) {
1190 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
1192 string columnName = columnSortInfo[0].Trim ();
1193 string sortOrder = "ASC";
1194 if (columnSortInfo.Length > 1)
1195 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
1197 ListSortDirection sortDirection = ListSortDirection.Ascending;
1198 switch (sortOrder) {
1200 sortDirection = ListSortDirection.Ascending;
1203 sortDirection = ListSortDirection.Descending;
1206 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
1210 ord = Int32.Parse (columnName);
1212 catch (FormatException) {
1215 DataColumn dc = null;
1217 dc = _columnCollection[columnName];
1219 dc = _columnCollection[ord];
1220 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
1221 columns.Add (sortCol);
1223 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
1228 private class SortableColumn
1230 private DataColumn col;
1231 private ListSortDirection dir;
1233 internal SortableColumn (DataColumn column,
1234 ListSortDirection direction)
1240 public DataColumn Column {
1246 public ListSortDirection SortDirection {
1253 private class RowSorter : IComparer
1255 private SortableColumn[] sortColumns;
1256 private DataRow[] rowsToSort;
1258 internal RowSorter(DataRow[] unsortedRows,
1259 SortableColumn[] sortColumns)
1261 this.sortColumns = sortColumns;
1262 this.rowsToSort = unsortedRows;
1265 public SortableColumn[] SortColumns {
1271 public DataRow[] SortRows ()
1273 Array.Sort (rowsToSort, this);
1277 int IComparer.Compare (object x, object y)
1280 throw new Exception ("Object to compare is null: x");
1282 throw new Exception ("Object to compare is null: y");
1284 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
1286 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
1288 DataRow rowx = (DataRow) x;
1289 DataRow rowy = (DataRow) y;
1291 for(int i = 0; i < sortColumns.Length; i++) {
1292 SortableColumn sortColumn = sortColumns[i];
1293 DataColumn dc = sortColumn.Column;
1295 IComparable objx = (IComparable) rowx[dc];
1296 object objy = rowy[dc];
1298 int result = CompareObjects (objx, objy);
1300 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
1311 private int CompareObjects (object a, object b)
1317 else if (a == DBNull.Value)
1321 else if (b == DBNull.Value)
1324 if((a is string) && (b is string)) {
1325 a = ((string) a).ToUpper ();
1326 b = ((string) b).ToUpper ();
1329 if (a is IComparable)
1330 return ((a as IComparable).CompareTo (b));
1331 else if (b is IComparable)
1332 return -((b as IComparable).CompareTo (a));
1334 throw new ArgumentException ("Neither a nor b IComparable");