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();
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
316 UniqueConstraint.SetAsPrimaryKey(this.Constraints, null);
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
328 uc = new UniqueConstraint( (DataColumn[]) value, true);
330 else { //set existing constraint as the new primary key
331 UniqueConstraint.SetAsPrimaryKey(this.Constraints, uc);
338 /// Gets the collection of_rows that belong to this table.
341 [DataSysDescription ("Indicates the collection that holds the rows of data for this table.")]
342 public DataRowCollection Rows {
343 get { return _rows; }
347 /// Gets or sets an System.ComponentModel.ISite
348 /// for the DataTable.
351 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
352 public override ISite Site {
353 get { return _site; }
354 set { _site = value; }
358 /// Gets or sets the name of the the DataTable.
360 [DataCategory ("Data")]
361 [DataSysDescription ("Indicates the name used to look up this table in the Tables collection of a DataSet.")]
363 [RefreshProperties (RefreshProperties.All)]
364 public string TableName {
365 get { return "" + _tableName; }
366 set { _tableName = value; }
369 bool IListSource.ContainsListCollection {
371 // the collection is a DataView
377 /// Commits all the changes made to this table since the
378 /// last time AcceptChanges was called.
380 public void AcceptChanges ()
382 //FIXME: Do we need to validate anything here or
383 //try to catch any errors to deal with them?
385 foreach(DataRow myRow in _rows) {
386 myRow.AcceptChanges();
391 /// Begins the initialization of a DataTable that is used
392 /// on a form or used by another component. The initialization
393 /// occurs at runtime.
395 public void BeginInit ()
400 /// Turns off notifications, index maintenance, and
401 /// constraints while loading data.
404 public void BeginLoadData ()
409 /// Clears the DataTable of all data.
411 public void Clear () {
412 // TODO: thow an exception if any rows that
413 // have enforced child relations
414 // that would result in child rows being orphaned
415 // TODO: throw a NotSupportedException if the DataTable is part
416 // of a DataSet that is binded to an XmlDataDocument
422 /// Clones the structure of the DataTable, including
423 /// all DataTable schemas and constraints.
426 public virtual DataTable Clone ()
428 DataTable Copy = new DataTable ();
429 CopyProperties (Copy);
434 /// Computes the given expression on the current_rows that
435 /// pass the filter criteria.
438 public object Compute (string expression, string filter)
440 // TODO: implement this function based
441 // on Select (expression)
443 // expression is an aggregate function
444 // filter is an expression used to limit rows
449 ExpressionElement Expression = new ExpressionMainElement (filter);
451 ArrayList List = new ArrayList ();
452 foreach (DataRow Row in Rows) {
454 if (Expression.Test (Row))
458 DataRow[] rows = (DataRow [])List.ToArray (typeof (DataRow));
460 // TODO: with the filtered rows, execute the aggregate function
461 // mentioned in expression
467 /// Copies both the structure and data for this DataTable.
470 public DataTable Copy ()
472 DataTable Copy = new DataTable ();
473 CopyProperties (Copy);
475 foreach (DataRow Row in Rows) {
476 DataRow NewRow = Copy.NewRow ();
477 NewRow.RowError = Row.RowError;
478 foreach (DataColumn C in Copy.Columns) {
479 NewRow [C.ColumnName] = Row [C.ColumnName];
481 Copy.Rows.Add (NewRow);
488 private void CopyProperties (DataTable Copy)
490 Copy.CaseSensitive = CaseSensitive;
491 Copy.VirginCaseSensitive = VirginCaseSensitive;
493 // Copy.ChildRelations
498 Copy.DisplayExpression = DisplayExpression;
499 // Copy.ExtendedProperties
500 Copy.Locale = Locale;
501 Copy.MinimumCapacity = MinimumCapacity;
502 Copy.Namespace = Namespace;
503 // Copy.ParentRelations
504 Copy.Prefix = Prefix;
505 //Copy.PrimaryKey = PrimaryKey;
507 Copy.TableName = TableName;
510 foreach (DataColumn Column in Columns) {
511 Copy.Columns.Add (CopyColumn (Column));
516 /// Ends the initialization of a DataTable that is used
517 /// on a form or used by another component. The
518 /// initialization occurs at runtime.
521 public void EndInit ()
527 /// Turns on notifications, index maintenance, and
528 /// constraints after loading data.
531 public void EndLoadData()
536 /// Gets a copy of the DataTable that contains all
537 /// changes made to it since it was loaded or
538 /// AcceptChanges was last called.
541 public DataTable GetChanges()
548 /// Gets a copy of the DataTable containing all
549 /// changes made to it since it was last loaded, or
550 /// since AcceptChanges was called, filtered by DataRowState.
553 public DataTable GetChanges(DataRowState rowStates)
560 /// Gets an array of DataRow objects that contain errors.
563 public DataRow[] GetErrors ()
565 throw new NotImplementedException ();
569 /// This member is only meant to support Mono's infrastructure
571 protected virtual DataTable CreateInstance ()
573 return Activator.CreateInstance (this.GetType (), true) as DataTable;
577 /// This member is only meant to support Mono's infrastructure
579 protected virtual Type GetRowType ()
581 return typeof (DataRow);
585 /// This member is only meant to support Mono's infrastructure
587 /// Used for Data Binding between System.Web.UI. controls
590 /// System.Windows.Forms controls like a DataGrid
592 IList IListSource.GetList ()
594 IList list = (IList) _defaultView;
599 /// Copies a DataRow into a DataTable, preserving any
600 /// property settings, as well as original and current values.
603 public void ImportRow (DataRow row)
608 /// This member is only meant to support Mono's infrastructure
611 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
616 /// Finds and updates a specific row. If no matching row
617 /// is found, a new row is created using the given values.
620 public DataRow LoadDataRow (object[] values, bool fAcceptChanges)
623 if (PrimaryKey.Length == 0) {
624 row = Rows.Add (values);
626 row.AcceptChanges ();
629 throw new NotImplementedException ();
634 /// Creates a new DataRow with the same schema as the table.
636 public DataRow NewRow ()
638 return this.NewRowFromBuilder (new DataRowBuilder (this, 0, 0));
642 /// This member supports the .NET Framework infrastructure
643 /// and is not intended to be used directly from your code.
645 protected internal DataRow[] NewRowArray (int size)
647 return (DataRow[]) Array.CreateInstance (GetRowType (), size);
651 /// Creates a new row from an existing row.
653 protected virtual DataRow NewRowFromBuilder (DataRowBuilder builder)
655 return new DataRow (builder);
660 /// Rolls back all changes that have been made to the
661 /// table since it was loaded, or the last time AcceptChanges
665 public void RejectChanges ()
667 //foreach(DataRow myRow in _rows)
669 for (int i = _rows.Count - 1; i >= 0; i--) {
670 DataRow row = _rows [i];
671 if (row.RowState != DataRowState.Unchanged)
672 _rows [i].RejectChanges ();
677 /// Resets the DataTable to its original state.
680 public virtual void Reset ()
685 /// Gets an array of all DataRow objects.
687 public DataRow[] Select ()
689 DataRow[] dataRows = new DataRow[_rows.Count];
690 _rows.CopyTo (dataRows, 0);
695 /// Gets an array of all DataRow objects that match
696 /// the filter criteria in order of primary key (or
697 /// lacking one, order of addition.)
699 public DataRow[] Select (string filterExpression)
701 ExpressionElement Expression = new ExpressionMainElement (filterExpression);
703 ArrayList List = new ArrayList ();
704 foreach (DataRow Row in Rows) {
706 if (Expression.Test (Row))
710 return (DataRow [])List.ToArray (typeof (DataRow));
714 /// Gets an array of all DataRow objects that
715 /// match the filter criteria, in the the
716 /// specified sort order.
718 public DataRow[] Select (string filterExpression, string sort)
720 DataRow[] dataRows = null;
722 if (filterExpression != null && filterExpression.Equals (String.Empty) == false)
723 dataRows = Select (filterExpression);
725 dataRows = Select ();
727 if (sort != null && !sort.Equals (String.Empty)) {
728 SortableColumn[] sortableColumns = null;
\r
730 sortableColumns = ParseTheSortString (sort);
\r
731 if (sortableColumns == null)
\r
732 throw new Exception ("sort expression result is null");
\r
733 if (sortableColumns.Length == 0)
\r
734 throw new Exception("sort expression result is 0");
\r
736 RowSorter rowSorter = new RowSorter (dataRows, sortableColumns);
\r
737 dataRows = rowSorter.SortRows ();
739 sortableColumns = null;
747 /// Gets an array of all DataRow objects that match
748 /// the filter in the order of the sort, that match
749 /// the specified state.
752 public DataRow[] Select(string filterExpression, string sort, DataViewRowState recordStates)
754 DataRow[] dataRows = null;
756 // TODO: do something with recordStates
757 dataRows = Select (filterExpression, sort);
763 /// Gets the TableName and DisplayExpression, if
764 /// there is one as a concatenated string.
766 public override string ToString()
768 //LAMESPEC: spec says concat the two. impl puts a
769 //plus sign infront of DisplayExpression
770 return TableName + " + " + DisplayExpression;
774 #region Events /////////////////
777 /// Raises the ColumnChanged event.
779 protected virtual void OnColumnChanged (DataColumnChangeEventArgs e) {
780 if (null != ColumnChanged) {
781 ColumnChanged (this, e);
786 /// Raises the ColumnChanging event.
788 protected virtual void OnColumnChanging (DataColumnChangeEventArgs e) {
789 if (null != ColumnChanging) {
790 ColumnChanging (this, e);
795 /// Raises the PropertyChanging event.
798 protected internal virtual void OnPropertyChanging (PropertyChangedEventArgs pcevent) {
799 // if (null != PropertyChanging)
801 // PropertyChanging (this, e);
806 /// Notifies the DataTable that a DataColumn is being removed.
809 protected internal virtual void OnRemoveColumn (DataColumn column) {
810 // if (null != RemoveColumn)
812 // RemoveColumn(this, e);
817 /// Raises the RowChanged event.
819 protected virtual void OnRowChanged (DataRowChangeEventArgs e) {
820 if (null != RowChanged) {
827 /// Raises the RowChanging event.
829 protected virtual void OnRowChanging (DataRowChangeEventArgs e) {
830 if (null != RowChanging) {
831 RowChanging(this, e);
836 /// Raises the RowDeleted event.
838 protected virtual void OnRowDeleted (DataRowChangeEventArgs e) {
839 if (null != RowDeleted) {
845 /// Raises the RowDeleting event.
847 protected virtual void OnRowDeleting (DataRowChangeEventArgs e) {
848 if (null != RowDeleting) {
849 RowDeleting(this, e);
854 private DataColumn CopyColumn (DataColumn Column) {
855 DataColumn Copy = new DataColumn ();
857 // Copy all the properties of column
858 Copy.AllowDBNull = Column.AllowDBNull;
859 Copy.AutoIncrement = Column.AutoIncrement;
860 Copy.AutoIncrementSeed = Column.AutoIncrementSeed;
861 Copy.AutoIncrementStep = Column.AutoIncrementStep;
862 Copy.Caption = Column.Caption;
863 Copy.ColumnMapping = Column.ColumnMapping;
864 Copy.ColumnName = Column.ColumnName;
868 Copy.Expression = Column.Expression;
869 //Copy.ExtendedProperties
870 Copy.MaxLength = Column.MaxLength;
871 Copy.Namespace = Column.Namespace;
872 Copy.Prefix = Column.Prefix;
873 Copy.ReadOnly = Column.ReadOnly;
875 Copy.Unique = Column.Unique;
881 /// Occurs when after a value has been changed for
882 /// the specified DataColumn in a DataRow.
884 [DataCategory ("Data")]
885 [DataSysDescription ("Occurs when a value has been changed for this column.")]
886 public event DataColumnChangeEventHandler ColumnChanged;
889 /// Occurs when a value is being changed for the specified
890 /// DataColumn in a DataRow.
892 [DataCategory ("Data")]
893 [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.")]
894 public event DataColumnChangeEventHandler ColumnChanging;
897 /// Occurs after a DataRow has been changed successfully.
899 [DataCategory ("Data")]
900 [DataSysDescription ("Occurs after a row in the table has been successfully edited.")]
901 public event DataRowChangeEventHandler RowChanged;
904 /// Occurs when a DataRow is changing.
906 [DataCategory ("Data")]
907 [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.")]
908 public event DataRowChangeEventHandler RowChanging;
911 /// Occurs after a row in the table has been deleted.
913 [DataCategory ("Data")]
914 [DataSysDescription ("Occurs after a row in the table has been successfully deleted.")]
915 public event DataRowChangeEventHandler RowDeleted;
918 /// Occurs before a row in the table is about to be deleted.
920 [DataCategory ("Data")]
921 [DataSysDescription ("Occurs when a row in the table marked for deletion. Throw an exception to cancel the deletion.")]
922 public event DataRowChangeEventHandler RowDeleting;
926 // to parse the sort string for DataTable:Select(expression,sort)
927 // into sortable columns (think ORDER BY,
928 // such as, "customer ASC, price DESC" )
929 private SortableColumn[] ParseTheSortString (string sort)
\r
931 SortableColumn[] sortColumns = null;
\r
932 ArrayList columns = null;
\r
934 if (sort != null && !sort.Equals ("")) {
\r
935 columns = new ArrayList ();
\r
936 string[] columnExpression = sort.Trim ().Split (new char[1] {','});
\r
938 for (int c = 0; c < columnExpression.Length; c++) {
\r
939 string[] columnSortInfo = columnExpression[c].Trim ().Split (new char[1] {' '});
\r
941 string columnName = columnSortInfo[0].Trim ();
\r
942 string sortOrder = "ASC";
\r
943 if (columnSortInfo.Length > 1)
\r
944 sortOrder = columnSortInfo[1].Trim ().ToUpper ();
\r
946 ListSortDirection sortDirection = ListSortDirection.Ascending;
\r
947 switch (sortOrder) {
\r
949 sortDirection = ListSortDirection.Ascending;
\r
952 sortDirection = ListSortDirection.Descending;
\r
955 throw new IndexOutOfRangeException ("Could not find column: " + columnExpression[c]);
\r
959 ord = Int32.Parse (columnName);
\r
961 catch (FormatException) {
\r
964 DataColumn dc = null;
\r
966 dc = _columnCollection[columnName];
\r
968 dc = _columnCollection[ord];
\r
969 SortableColumn sortCol = new SortableColumn (dc,sortDirection);
\r
970 columns.Add (sortCol);
\r
972 sortColumns = (SortableColumn[]) columns.ToArray (typeof (SortableColumn));
\r
974 return sortColumns;
\r
977 private class SortableColumn
\r
979 private DataColumn col;
\r
980 private ListSortDirection dir;
\r
982 internal SortableColumn (DataColumn column,
\r
983 ListSortDirection direction)
\r
989 public DataColumn Column {
\r
995 public ListSortDirection SortDirection {
\r
1002 private class RowSorter : IComparer
\r
1004 private SortableColumn[] sortColumns;
\r
1005 private DataRow[] rowsToSort;
\r
1007 internal RowSorter(DataRow[] unsortedRows,
\r
1008 SortableColumn[] sortColumns)
\r
1010 this.sortColumns = sortColumns;
\r
1011 this.rowsToSort = unsortedRows;
\r
1014 public SortableColumn[] SortColumns {
\r
1016 return sortColumns;
\r
1020 public DataRow[] SortRows ()
\r
1022 Array.Sort (rowsToSort, this);
\r
1023 return rowsToSort;
\r
1026 int IComparer.Compare (object x, object y)
\r
1029 throw new Exception ("Object to compare is null: x");
\r
1031 throw new Exception ("Object to compare is null: y");
\r
1032 if(!(x is DataRow))
\r
1033 throw new Exception ("Object to compare is not DataRow: x is " + x.GetType().ToString());
\r
1034 if(!(y is DataRow))
\r
1035 throw new Exception ("Object to compare is not DataRow: y is " + x.GetType().ToString());
\r
1037 DataRow rowx = (DataRow) x;
\r
1038 DataRow rowy = (DataRow) y;
\r
1040 for(int i = 0; i < sortColumns.Length; i++) {
\r
1041 SortableColumn sortColumn = sortColumns[i];
\r
1042 DataColumn dc = sortColumn.Column;
\r
1044 IComparable objx = (IComparable) rowx[dc];
\r
1045 object objy = rowy[dc];
\r
1047 int result = CompareObjects (objx, objy);
\r
1048 if (result != 0) {
\r
1049 if (sortColumn.SortDirection == ListSortDirection.Ascending) {
\r
1060 private int CompareObjects (object a, object b)
\r
1066 else if (a == DBNull.Value)
1070 else if (b == DBNull.Value)
1073 if((a is string) && (b is string)) {
1074 a = ((string) a).ToUpper ();
1075 b = ((string) b).ToUpper ();
1078 if (a is IComparable)
1079 return ((a as IComparable).CompareTo (b));
1080 else if (b is IComparable)
1081 return -((b as IComparable).CompareTo (a));
1083 throw new ArgumentException ("Neither a nor b IComparable");