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;
57 // If CaseSensitive property is changed once it does not anymore follow owner DataSet's
58 // CaseSensitive property. So when you lost you virginity it's gone for ever
59 private bool _virginCaseSensitive = true;
62 /// Initializes a new instance of the DataTable class with no arguments.
68 _columnCollection = new DataColumnCollection(this);
69 _constraintCollection = new ConstraintCollection(this);
70 _extendedProperties = new PropertyCollection();
73 _caseSensitive = false; //default value
74 _displayExpression = null;
77 _rows = new DataRowCollection (this);
78 _locale = CultureInfo.CurrentCulture;
80 //LAMESPEC: spec says 25 impl does 50
81 _minimumCapacity = 50;
83 _childRelations = new DataRelationCollection.DataTableRelationCollection (this);
84 _parentRelations = new DataRelationCollection.DataTableRelationCollection (this);
87 _defaultView = new DataView(this);
91 /// Intitalizes a new instance of the DataTable class with the specified table name.
93 public DataTable (string tableName) : this ()
95 _tableName = tableName;
99 /// Initializes a new instance of the DataTable class with the SerializationInfo and the StreamingContext.
102 protected DataTable (SerializationInfo info, StreamingContext context)
106 // TODO: Add constructor logic here
111 /// Indicates whether string comparisons within the table are case-sensitive.
113 [DataSysDescription ("Indicates whether comparing strings within the table is case sensitive.")]
114 public bool CaseSensitive {
115 get { return _caseSensitive; }
117 _virginCaseSensitive = false;
118 _caseSensitive = value;
122 internal bool VirginCaseSensitive {
123 get { return _virginCaseSensitive; }
124 set { _virginCaseSensitive = value; }
127 internal void ChangedDataColumn (DataRow dr, DataColumn dc, object pv)
129 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
133 internal void ChangingDataColumn (DataRow dr, DataColumn dc, object pv)
135 DataColumnChangeEventArgs e = new DataColumnChangeEventArgs (dr, dc, pv);
136 OnColumnChanging (e);
139 internal void DeletedDataRow (DataRow dr, DataRowAction action)
141 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
145 internal void ChangedDataRow (DataRow dr, DataRowAction action)
147 DataRowChangeEventArgs e = new DataRowChangeEventArgs (dr, action);
152 /// Gets the collection of child relations for this DataTable.
155 [DataSysDescription ("Returns the child relations for this table.")]
156 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
157 public DataRelationCollection ChildRelations {
159 return _childRelations;
164 /// Gets the collection of columns that belong to this table.
166 [DataCategory ("Data")]
167 [DataSysDescription ("The collection that holds the columns for this table.")]
168 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
169 public DataColumnCollection Columns {
170 get { return _columnCollection; }
174 /// Gets the collection of constraints maintained by this table.
176 [DataCategory ("Data")]
177 [DataSysDescription ("The collection that holds the constraints for this table.")]
178 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
179 public ConstraintCollection Constraints {
180 get { return _constraintCollection; }
184 /// Gets the DataSet that this table belongs to.
187 [DataSysDescription ("Indicates the DataSet to which this table belongs.")]
188 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
189 public DataSet DataSet {
190 get { return dataSet; }
196 /// Gets a customized view of the table which may
197 /// include a filtered view, or a cursor position.
201 [DataSysDescription ("This is the default DataView for the table.")]
202 public DataView DefaultView {
203 get { return _defaultView; }
208 /// Gets or sets the expression that will return
209 /// a value used to represent this table in the user interface.
211 [DataCategory ("Data")]
212 [DataSysDescription ("The expression used to compute the data-bound value of this row.")]
214 public string DisplayExpression {
215 get { return "" + _displayExpression; }
216 set { _displayExpression = value; }
220 /// Gets the collection of customized user information.
223 [DataCategory ("Data")]
224 [DataSysDescription ("The collection that holds custom user information.")]
225 public PropertyCollection ExtendedProperties {
226 get { return _extendedProperties; }
230 /// Gets a value indicating whether there are errors in
231 /// any of the_rows in any of the tables of the DataSet to
232 /// which the table belongs.
235 [DataSysDescription ("Returns whether the table has errors.")]
236 public bool HasErrors {
237 get { return _hasErrors; }
241 /// Gets or sets the locale information used to
242 /// compare strings within the table.
244 [DataSysDescription ("Indicates a locale under which to compare strings within the table.")]
245 public CultureInfo Locale {
246 get { return _locale; }
247 set { _locale = value; }
251 /// Gets or sets the initial starting size for this table.
253 [DataCategory ("Data")]
254 [DataSysDescription ("Indicates an initial starting size for this table.")]
256 public int MinimumCapacity {
257 get { return _minimumCapacity; }
258 set { _minimumCapacity = value; }
262 /// Gets or sets the namespace for the XML represenation
263 /// of the data stored in the DataTable.
265 [DataCategory ("Data")]
266 [DataSysDescription ("Indicates the XML uri namespace for the elements contained in this table.")]
267 public string Namespace {
268 get { return "" + _nameSpace; }
269 set { _nameSpace = value; }
273 /// Gets the collection of parent relations for
277 [DataSysDescription ("Returns the parent relations for this table.")]
278 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
279 public DataRelationCollection ParentRelations {
281 return _parentRelations;
286 /// Gets or sets the namespace for the XML represenation
287 /// of the data stored in the DataTable.
289 [DataCategory ("Data")]
290 [DataSysDescription ("Indicates the Prefix of the namespace used for this table in XML representation.")]
292 public string Prefix {
293 get { return "" + _prefix; }
294 set { _prefix = value; }
298 /// Gets or sets an array of columns that function as
299 /// primary keys for the data table.
301 [DataCategory ("Data")]
302 [DataSysDescription ("Indicates the column(s) that represent the primary key for this table.")]
303 public DataColumn[] PrimaryKey {
305 UniqueConstraint uc = UniqueConstraint.GetPrimaryKeyConstraint( Constraints);
306 if (null == uc) return new DataColumn[] {};
311 //YUK: msft removes a previous unique constraint if it is flagged as a pk
312 //when a new pk is set
314 //clear Primary Key if value == null
317 RemoveUniqueConstraints ();
321 //Does constraint exist for these columns
322 UniqueConstraint uc = UniqueConstraint.GetUniqueConstraintForColumnSet(
323 this.Constraints, (DataColumn[]) value);
325 //if constraint doesn't exist for columns
326 //create new unique primary key constraint
329 RemoveUniqueConstraints ();
331 foreach (DataColumn Col in (DataColumn[]) value) {
333 if (Col.Table == null)
336 if (Columns.IndexOf (Col) < 0)
337 throw new ArgumentException ("PrimaryKey columns do not belong to this table.");
341 uc = new UniqueConstraint( (DataColumn[]) value, true);
343 Constraints.Add (uc);
345 else { //set existing constraint as the new primary key
346 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
353 /// Gets the collection of_rows that belong to this table.
356 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
357 public DataRowCollection Rows {
358 get { return _rows; }
362 /// Gets or sets an System.ComponentModel.ISite
363 /// for the DataTable.
366 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
367 public override ISite Site {
368 get { return _site; }
369 set { _site = value; }
373 /// Gets or sets the name of the the DataTable.
375 [DataCategory ("Data")]
376 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
378 [RefreshProperties (RefreshProperties.All)]
379 public string TableName {
380 get { return "" + _tableName; }
381 set { _tableName = value; }
384 bool IListSource.ContainsListCollection {
386 // the collection is a DataView
392 /// Commits all the changes made to this table since the
393 /// last time AcceptChanges was called.
395 public void AcceptChanges ()
397 //FIXME: Do we need to validate anything here or
398 //try to catch any errors to deal with them?
400 foreach(DataRow myRow in _rows) {
401 myRow.AcceptChanges();
406 /// Begins the initialization of a DataTable that is used
407 /// on a form or used by another component. The initialization
408 /// occurs at runtime.
410 public void BeginInit ()
415 /// Turns off notifications, index maintenance, and
416 /// constraints while loading data.
419 public void BeginLoadData ()
424 /// Clears the DataTable of all data.
426 public void Clear () {
427 // TODO: thow an exception if any rows that
428 // have enforced child relations
429 // that would result in child rows being orphaned
430 // TODO: throw a NotSupportedException if the DataTable is part
431 // of a DataSet that is binded to an XmlDataDocument
437 /// Clones the structure of the DataTable, including
438 /// all DataTable schemas and constraints.
441 public virtual DataTable Clone ()
443 DataTable Copy = new DataTable ();
444 CopyProperties (Copy);
449 /// Computes the given expression on the current_rows that
450 /// pass the filter criteria.
453 public object Compute (string expression, string filter)
455 // TODO: implement this function based
456 // on Select (expression)
458 // expression is an aggregate function
459 // filter is an expression used to limit rows
464 ExpressionElement Expression = new ExpressionMainElement (filter);
466 ArrayList List = new ArrayList ();
467 foreach (DataRow Row in Rows) {
469 if (Expression.Test (Row))
473 DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
475 // TODO: with the filtered rows, execute the aggregate function
476 // mentioned in expression
482 /// Copies both the structure and data for this DataTable.
485 public DataTable Copy ()
487 DataTable Copy = new DataTable ();
488 CopyProperties (Copy);
490 foreach (DataRow Row in Rows) {
491 DataRow NewRow = Copy.NewRow ();
492 NewRow.RowError = Row.RowError;
493 foreach (DataColumn C in Copy.Columns) {
494 NewRow [C.ColumnName] = Row [C.ColumnName];
496 Copy.Rows.Add (NewRow);
503 private void CopyProperties (DataTable Copy)
505 Copy.CaseSensitive = CaseSensitive;
506 Copy.VirginCaseSensitive = VirginCaseSensitive;
508 // Copy.ChildRelations
513 Copy.DisplayExpression = DisplayExpression;
514 // Copy.ExtendedProperties
515 Copy.Locale = Locale;
516 Copy.MinimumCapacity = MinimumCapacity;
517 Copy.Namespace = Namespace;
518 // Copy.ParentRelations
519 Copy.Prefix = Prefix;
520 //Copy.PrimaryKey = PrimaryKey;
522 Copy.TableName = TableName;
525 foreach (DataColumn Column in Columns) {
526 Copy.Columns.Add (CopyColumn (Column));
531 /// Ends the initialization of a DataTable that is used
532 /// on a form or used by another component. The
533 /// initialization occurs at runtime.
536 public void EndInit ()
542 /// Turns on notifications, index maintenance, and
543 /// constraints after loading data.
546 public void EndLoadData()
551 /// Gets a copy of the DataTable that contains all
552 /// changes made to it since it was loaded or
553 /// AcceptChanges was last called.
556 public DataTable GetChanges()
563 /// Gets a copy of the DataTable containing all
564 /// changes made to it since it was last loaded, or
565 /// since AcceptChanges was called, filtered by DataRowState.
568 public DataTable GetChanges(DataRowState rowStates)
575 /// Gets an array of DataRow objects that contain errors.
578 public DataRow[] GetErrors ()
580 throw new NotImplementedException ();
584 /// This member is only meant to support Mono's infrastructure
586 protected virtual DataTable CreateInstance ()
588 return Activator.CreateInstance (this.GetType (), true) as DataTable;
592 /// This member is only meant to support Mono's infrastructure
594 protected virtual Type GetRowType ()
596 return typeof (DataRow);
600 /// This member is only meant to support Mono's infrastructure
602 /// Used for Data Binding between System.Web.UI. controls
605 /// System.Windows.Forms controls like a DataGrid
607 IList IListSource.GetList ()
609 IList list = (IList) _defaultView;
614 /// Copies a DataRow into a DataTable, preserving any
615 /// property settings, as well as original and current values.
618 public void ImportRow (DataRow row)
623 /// This member is only meant to support Mono's infrastructure
626 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
631 /// Finds and updates a specific row. If no matching row
632 /// is found, a new row is created using the given values.
635 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
638 if (PrimaryKey.Length == 0) {
639 row = Rows.Add (values);
641 row.AcceptChanges ();
644 throw new NotImplementedException ();
649 /// Creates a new DataRow with the same schema as the table.
651 public DataRow NewRow ()
653 return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
657 /// This member supports the .NET Framework infrastructure
658 /// and is not intended to be used directly from your code.
660 protected internal DataRow[] NewRowArray (int size)
662 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
666 /// Creates a new row from an existing row.
668 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
670 return new DataRow (builder);
675 /// Rolls back all changes that have been made to the
676 /// table since it was loaded, or the last time AcceptChanges
680 public void RejectChanges ()
682 //foreach(DataRow myRow in _rows)
684 for (int i = _rows.Count - 1; i >= 0; i--) {
685 DataRow row = _rows [i];
686 if (row.RowState != DataRowState.Unchanged)
687 _rows [i].RejectChanges ();
692 /// Resets the DataTable to its original state.
695 public virtual void Reset ()
700 /// Gets an array of all DataRow objects.
702 public DataRow[] Select ()
704 DataRow[] dataRows = new DataRow[_rows.Count];
705 _rows.CopyTo (dataRows, 0);
710 /// Gets an array of all DataRow objects that match
711 /// the filter criteria in order of primary key (or
712 /// lacking one, order of addition.)
714 public DataRow[] Select (string filterExpression)
716 ExpressionElement Expression = new ExpressionMainElement (filterExpression);
718 ArrayList List = new ArrayList ();
719 foreach (DataRow Row in Rows) {
721 if (Expression.Test (Row))
725 return (DataRow [])List.ToArray (typeof (DataRow));
729 /// Gets an array of all DataRow objects that
730 /// match the filter criteria, in the the
731 /// specified sort order.
733 public DataRow[] Select (string filterExpression, string sort)
735 DataRow[] dataRows = null;
737 if (filterExpression != null && filterExpression.Equals (String.Empty) == false)
738 dataRows = Select (filterExpression);
740 dataRows = Select ();
742 if (sort != null && !sort.Equals (String.Empty)) {
743 SortableColumn[] sortableColumns = null;
\r
745 sortableColumns = ParseTheSortString (sort);
\r
746 if (sortableColumns == null)
\r
747 throw new Exception ("sort expression result is null");
\r
748 if (sortableColumns.Length == 0)
\r
749 throw new Exception("sort expression result is 0");
\r
751 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
\r
752 dataRows = rowSorter.SortRows ();
754 sortableColumns = null;
762 /// Gets an array of all DataRow objects that match
763 /// the filter in the order of the sort, that match
764 /// the specified state.
767 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
769 DataRow[] dataRows = null;
771 // TODO: do something with recordStates
772 dataRows = Select (filterExpression, sort);
778 /// Gets the TableName and DisplayExpression, if
779 /// there is one as a concatenated string.
781 public override string ToString()
783 //LAMESPEC: spec says concat the two. impl puts a
784 //plus sign infront of DisplayExpression
785 return TableName + " + " + DisplayExpression;
789 #region Events /////////////////
792 /// Raises the ColumnChanged event.
794 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
795 if (null != ColumnChanged) {
796 ColumnChanged (this, e);
801 /// Raises the ColumnChanging event.
803 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
804 if (null != ColumnChanging) {
805 ColumnChanging (this, e);
810 /// Raises the PropertyChanging event.
813 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
814 // if (null != PropertyChanging)
816 // PropertyChanging (this, e);
821 /// Notifies the DataTable that a DataColumn is being removed.
824 protected internal virtual void OnRemoveColumn (DataColumn column) {
825 // if (null != RemoveColumn)
827 // RemoveColumn(this, e);
832 /// Raises the RowChanged event.
834 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
835 if (null != RowChanged) {
842 /// Raises the RowChanging event.
844 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
845 if (null != RowChanging) {
846 RowChanging(this, e);
851 /// Raises the RowDeleted event.
853 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
854 if (null != RowDeleted) {
860 /// Raises the RowDeleting event.
862 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
863 if (null != RowDeleting) {
864 RowDeleting(this, e);
869 private DataColumn CopyColumn (DataColumn Column) {
870 DataColumn Copy = new DataColumn ();
872 // Copy all the properties of column
873 Copy.AllowDBNull = Column.AllowDBNull;
874 Copy.AutoIncrement = Column.AutoIncrement;
875 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
876 Copy.AutoIncrementStep = Column.AutoIncrementStep;
877 Copy.Caption = Column.Caption;
878 Copy.ColumnMapping = Column.ColumnMapping;
879 Copy.ColumnName = Column.ColumnName;
883 Copy.Expression = Column.Expression;
884 //Copy.ExtendedProperties
885 Copy.MaxLength = Column.MaxLength;
886 Copy.Namespace = Column.Namespace;
887 Copy.Prefix = Column.Prefix;
888 Copy.ReadOnly = Column.ReadOnly;
890 Copy.Unique = Column.Unique;
896 /// Occurs when after a value has been changed for
897 /// the specified DataColumn in a DataRow.
899 [DataCategory ("Data")]
900 [DataSysDescription ("Occurs when a value has been changed for this column.")]
901 public event DataColumnChangeEventHandler ColumnChanged;
904 /// Occurs when a value is being changed for the specified
905 /// DataColumn in a DataRow.
907 [DataCategory ("Data")]
908 [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.")]
909 public event DataColumnChangeEventHandler ColumnChanging;
912 /// Occurs after a DataRow has been changed successfully.
914 [DataCategory ("Data")]
915 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
916 public event DataRowChangeEventHandler RowChanged;
919 /// Occurs when a DataRow is changing.
921 [DataCategory ("Data")]
922 [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.")]
923 public event DataRowChangeEventHandler RowChanging;
926 /// Occurs after a row in the table has been deleted.
928 [DataCategory ("Data")]
929 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
930 public event DataRowChangeEventHandler RowDeleted;
933 /// Occurs before a row in the table is about to be deleted.
935 [DataCategory ("Data")]
936 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
937 public event DataRowChangeEventHandler RowDeleting;
942 /// Removes all UniqueConstraints
944 private void RemoveUniqueConstraints ()
946 foreach (Constraint Cons in Constraints) {
948 if (Cons is UniqueConstraint) {
949 Constraints.Remove (Cons);
954 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
957 // to parse the sort string for DataTable:Select(expression,sort)
958 // into sortable columns (think ORDER BY,
959 // such as, "customer ASC, price DESC" )
960 private SortableColumn[] ParseTheSortString (string sort)
\r
962 SortableColumn[] sortColumns = null;
\r
963 ArrayList columns = null;
\r
965 if (sort != null && !sort.Equals ("")) {
\r
966 columns = new ArrayList ();
\r
967 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
969 for (int c = 0; c < columnExpression.Length; c++) {
\r
970 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
972 string columnName = columnSortInfo[0].Trim ();
\r
973 string sortOrder = "ASC";
\r
974 if (columnSortInfo.Length > 1)
\r
975 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
\r
977 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
978 switch (sortOrder) {
\r
980 sortDirection = ListSortDirection.Ascending;
\r
983 sortDirection = ListSortDirection.Descending;
\r
986 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
990 ord = Int32.Parse (columnName);
\r
992 catch (FormatException) {
\r
995 DataColumn dc = null;
\r
997 dc = _columnCollection[columnName];
\r
999 dc = _columnCollection[ord];
\r
1000 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
\r
1001 columns.Add (sortCol);
\r
1003 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
\r
1005 return sortColumns;
\r
1008 private class SortableColumn
\r
1010 private DataColumn col;
\r
1011 private ListSortDirection dir;
\r
1013 internal SortableColumn (DataColumn column,
\r
1014 ListSortDirection direction)
\r
1020 public DataColumn Column {
\r
1026 public ListSortDirection SortDirection {
\r
1033 private class RowSorter : IComparer
\r
1035 private SortableColumn[] sortColumns;
\r
1036 private DataRow[] rowsToSort;
\r
1038 internal RowSorter(DataRow[] unsortedRows,
\r
1039 SortableColumn[] sortColumns)
\r
1041 this.sortColumns = sortColumns;
\r
1042 this.rowsToSort = unsortedRows;
\r
1045 public SortableColumn[] SortColumns {
\r
1047 return sortColumns;
\r
1051 public DataRow[] SortRows ()
\r
1053 Array.Sort (rowsToSort, this);
\r
1054 return rowsToSort;
\r
1057 int IComparer.Compare (object x, object y)
\r
1060 throw new Exception ("Object to compare is null: x");
\r
1062 throw new Exception ("Object to compare is null: y");
\r
1063 if(!(x is DataRow))
\r
1064 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
\r
1065 if(!(y is DataRow))
\r
1066 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
\r
1068 DataRow rowx = (DataRow) x;
\r
1069 DataRow rowy = (DataRow) y;
\r
1071 for(int i = 0; i < sortColumns.Length; i++) {
\r
1072 SortableColumn sortColumn = sortColumns[i];
\r
1073 DataColumn dc = sortColumn.Column;
\r
1075 IComparable objx = (IComparable) rowx[dc];
\r
1076 object objy = rowy[dc];
\r
1078 int result = CompareObjects (objx, objy);
\r
1079 if (result != 0) {
\r
1080 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
\r
1091 private int CompareObjects (object a, object b)
\r
1097 else if (a == DBNull.Value)
1101 else if (b == DBNull.Value)
1104 if((a is string) && (b is string)) {
1105 a = ((string) a).ToUpper ();
1106 b = ((string) b).ToUpper ();
1109 if (a is IComparable)
1110 return ((a as IComparable).CompareTo (b));
1111 else if (b is IComparable)
1112 return -((b as IComparable).CompareTo (a));
1114 throw new ArgumentException ("Neither a nor b IComparable");