2 // System.Data.DataView.cs
\r
5 // Daniel Morgan <danmorg@sc.rr.com>
\r
6 // Tim Coleman (tim@timcoleman.com)
\r
7 // Punit Todi (punits_mailbox@yahoo.com)
\r
8 // Atsushi Enomoto <atsushi@ximian.com>
\r
9 // Konstantin Triger (kostat@mainsoft.com)
\r
11 // Copyright (C) Daniel Morgan, 2002, 2003
\r
12 // (C) Ximian, Inc 2002
\r
13 // Copyright (C) Tim Coleman, 2002-2003
\r
16 using System.Collections;
\r
17 using System.ComponentModel;
\r
18 using System.Reflection;
\r
19 using System.Data.Common;
\r
20 using System.Globalization;
\r
21 using Mono.Data.SqlExpressions;
\r
24 namespace System.Data
\r
27 /// A DataView is used in the binding of data between
\r
28 /// a DataTable and Windows Forms or Web Forms allowing
\r
29 /// a view of a DataTable for editing, filtering,
\r
30 /// navigation, searching, and sorting.
\r
33 [Editor ("Microsoft.VSDesigner.Data.Design.DataSourceEditor, " + Consts.AssemblyMicrosoft_VSDesigner,
\r
34 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
\r
35 [DefaultEvent ("PositionChanged")]
\r
36 [DefaultProperty ("Table")]
\r
37 [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.DataViewDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
\r
39 public class DataView : MarshalByValueComponent, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitialize, IBindingListView, ISupportInitializeNotification
\r
41 public class DataView : MarshalByValueComponent, IBindingList, IList, ICollection, IEnumerable, ITypedList, ISupportInitialize
\r
44 internal DataTable dataTable = null;
\r
45 string rowFilter = String.Empty;
\r
46 IExpression rowFilterExpr;
\r
47 string sort = String.Empty;
\r
48 ListSortDirection [] sortOrder = null;
\r
49 PropertyDescriptor sortProperty = null;
\r
50 DataColumn [] sortColumns = null;
\r
51 internal DataViewRowState rowState;
\r
52 internal DataRowView[] rowCache = new DataRowView [0];
\r
54 private bool dataViewInitialized = true;
\r
57 // BeginInit() support
\r
58 bool isInitPhase = false;
\r
59 bool inEndInit = false;
\r
60 DataTable initTable;
\r
61 bool initApplyDefaultSort;
\r
63 string initRowFilter;
\r
64 DataViewRowState initRowState;
\r
66 // FIXME: what are the default values?
\r
67 bool allowNew = true;
\r
68 bool allowEdit = true;
\r
69 bool allowDelete = true;
\r
70 bool applyDefaultSort = false;
\r
71 //bool isSorted = false;
\r
73 bool isOpen = false;
\r
75 bool useDefaultSort = true;
\r
78 internal DataRow _lastAdded = null;
\r
80 private DataViewManager dataViewManager = null;
\r
81 internal static ListChangedEventArgs ListResetEventArgs = new ListChangedEventArgs (ListChangedType.Reset,-1,-1);
\r
83 #region Constructors
\r
87 rowState = DataViewRowState.CurrentRows;
\r
91 public DataView (DataTable table)
\r
92 : this (table, (DataViewManager) null)
\r
96 internal DataView (DataTable table, DataViewManager manager)
\r
99 rowState = DataViewRowState.CurrentRows;
\r
100 dataViewManager = manager;
\r
104 public DataView (DataTable table, string rowFilter,
\r
105 string sort, DataViewRowState rowState)
\r
106 : this (table, null, rowFilter, sort, rowState)
\r
110 internal DataView (DataTable table, DataViewManager manager,
\r
111 string RowFilter, string Sort, DataViewRowState RowState)
\r
114 dataViewManager = manager;
\r
115 rowState = DataViewRowState.CurrentRows;
\r
116 this.RowFilter = RowFilter;
\r
118 rowState = RowState;
\r
121 #endregion // Constructors
\r
123 #region PublicProperties
\r
125 [DataCategory ("Data")]
\r
127 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows deletes.")]
\r
129 [DefaultValue (true)]
\r
130 public bool AllowDelete {
\r
132 return allowDelete;
\r
135 allowDelete = value;
\r
139 [DataCategory ("Data")]
\r
141 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows edits.")]
\r
143 [DefaultValue (true)]
\r
144 public bool AllowEdit {
\r
153 [DataCategory ("Data")]
\r
155 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows new rows to be added.")]
\r
157 [DefaultValue (true)]
\r
158 public bool AllowNew {
\r
168 [DataCategory ("Data")]
\r
170 [DataSysDescription ("Indicates whether to use the default sort if the Sort property is not set.")]
\r
172 [DefaultValue (false)]
\r
173 [RefreshProperties (RefreshProperties.All)]
\r
174 public bool ApplyDefaultSort {
\r
175 get { return applyDefaultSort; }
\r
178 initApplyDefaultSort = value;
\r
181 if (applyDefaultSort == value)
\r
184 applyDefaultSort = value;
\r
185 if (applyDefaultSort == true &&
\r
186 (sort == null || sort == string.Empty))
\r
187 PopulateDefaultSort ();
\r
189 UpdateIndex (true);
\r
190 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
194 // get the count of rows in the DataView after RowFilter
\r
195 // and RowStateFilter have been applied
\r
196 [Browsable (false)]
\r
198 [DataSysDescription ("Returns the number of items currently in this view.")]
\r
202 return rowCache.Length;
\r
206 [Browsable (false)]
\r
208 [DataSysDescription ("This returns a pointer to back to the DataViewManager that owns this DataSet (if any).")]
\r
210 public DataViewManager DataViewManager {
\r
212 return dataViewManager;
\r
217 // the compiler creates a DefaultMemeberAttribute from
\r
218 // this IndexerNameAttribute
\r
219 [System.Runtime.CompilerServices.IndexerName("Item")]
\r
220 public DataRowView this[int recordIndex] {
\r
222 if (recordIndex > rowCache.Length)
\r
223 throw new IndexOutOfRangeException ("There is no row at " +
\r
224 "position: " + recordIndex + ".");
\r
225 return rowCache [recordIndex];
\r
229 [DataCategory ("Data")]
\r
231 [DataSysDescription ("Indicates an expression used to filter the data returned by this DataView.")]
\r
233 [DefaultValue ("")]
\r
234 public virtual string RowFilter {
\r
235 get { return rowFilter; }
\r
238 value = String.Empty;
\r
240 initRowFilter = value;
\r
244 CultureInfo info = (Table != null) ? Table.Locale : CultureInfo.CurrentCulture;
\r
245 if (String.Compare(rowFilter, value, false, info) == 0)
\r
248 if (value == String.Empty)
\r
249 rowFilterExpr = null;
\r
251 Parser parser = new Parser ();
\r
252 rowFilterExpr = parser.Compile (value);
\r
256 UpdateIndex (true);
\r
260 [DataCategory ("Data")]
\r
262 [DataSysDescription ("Indicates the versions of data returned by this DataView.")]
\r
264 [DefaultValue (DataViewRowState.CurrentRows)]
\r
265 public DataViewRowState RowStateFilter {
\r
266 get { return rowState; }
\r
269 initRowState = value;
\r
273 if (value == rowState)
\r
278 UpdateIndex (true);
\r
279 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
284 [DataCategory ("Data")]
\r
286 [DataSysDescription ("Indicates the order in which data is returned by this DataView.")]
\r
288 [DefaultValue ("")]
\r
289 public string Sort {
\r
291 if (useDefaultSort)
\r
292 return String.Empty;
\r
305 if ((value == null) || (value.Equals (String.Empty))) {
\r
306 /* if given value is null useDefaultSort */
\r
307 useDefaultSort = true;
\r
308 /* if ApplyDefault sort is true try appling it */
\r
309 if (ApplyDefaultSort == true)
\r
310 PopulateDefaultSort ();
\r
313 /* else donot useDefaultSort. set it as false */
\r
314 /* sort is set to value specified */
\r
315 useDefaultSort = false;
\r
317 //sortedColumns = SortableColumn.ParseSortString (dataTable, value, true);
\r
321 UpdateIndex (true);
\r
322 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
327 [TypeConverter (typeof (DataTableTypeConverter))]
\r
328 [DataCategory ("Data")]
\r
330 [DataSysDescription ("Indicates the table this DataView uses to get data.")]
\r
332 [DefaultValue (null)]
\r
333 [RefreshProperties (RefreshProperties.All)]
\r
334 public DataTable Table {
\r
335 get { return dataTable; }
\r
337 if (value == dataTable)
\r
345 if (value != null && value.TableName.Equals("")) {
\r
346 throw new DataException("Cannot bind to DataTable with no name.");
\r
349 if (dataTable != null) {
\r
350 UnregisterEventHandlers ();
\r
355 if (dataTable != null) {
\r
356 RegisterEventHandlers ();
\r
357 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged, 0, 0));
\r
361 UpdateIndex (true);
\r
362 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
369 [Browsable (false)]
\r
370 public bool IsInitialized {
\r
371 get { return dataViewInitialized;}
\r
374 #endregion // PublicProperties
\r
376 #region PublicMethods
\r
378 public virtual DataRowView AddNew()
\r
381 throw new DataException ("DataView is not open.");
\r
383 throw new DataException ("Cannot call AddNew on a DataView where AllowNew is false.");
\r
385 if (_lastAdded != null) {
\r
386 // FIXME : finish last added
\r
387 CompleteLastAdded(true);
\r
390 _lastAdded = dataTable.NewRow ();
\r
392 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1, -1));
\r
394 return this[Count - 1];
\r
397 internal void CompleteLastAdded(bool add)
\r
399 DataRow dr = _lastAdded;
\r
403 dataTable.Rows.Add(_lastAdded);
\r
404 //OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1, -1));
\r
408 catch(Exception e) {
\r
414 _lastAdded.CancelEdit();
\r
417 OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, Count, -1));
\r
421 public void BeginInit()
\r
424 initApplyDefaultSort = ApplyDefaultSort;
\r
426 initRowFilter = RowFilter;
\r
427 initRowState = RowStateFilter;
\r
429 isInitPhase = true;
\r
431 dataViewInitialized = false;
\r
435 public void CopyTo (Array array, int index)
\r
437 if (index + rowCache.Length > array.Length) {
\r
438 throw new IndexOutOfRangeException();
\r
442 for (; row < rowCache.Length && row < array.Length; row++) {
\r
443 array.SetValue (rowCache[row], index + row);
\r
447 public void Delete(int index)
\r
450 throw new DataException ("DataView is not open.");
\r
452 if (_lastAdded != null && index == Count) {
\r
453 CompleteLastAdded(false);
\r
458 throw new DataException ("Cannot delete on a DataSource where AllowDelete is false.");
\r
460 if (index > rowCache.Length)
\r
461 throw new IndexOutOfRangeException ("There is no row at " +
\r
462 "position: " + index + ".");
\r
463 DataRowView row = rowCache [index];
\r
468 public virtual bool Equals (DataView dv)
\r
472 if (!(this.Table == dv.Table && this.Sort == dv.Sort &&
\r
473 this.RowFilter == dv.RowFilter &&
\r
474 this.RowStateFilter == dv.RowStateFilter &&
\r
475 this.AllowEdit == dv.AllowEdit &&
\r
476 this.AllowNew == dv.AllowNew &&
\r
477 this.AllowDelete == dv.AllowDelete &&
\r
478 this.Count == dv.Count))
\r
481 for (int i=0; i < Count; ++i)
\r
482 if (!this [i].Equals (dv [i]))
\r
488 public void EndInit()
\r
490 isInitPhase = false;
\r
495 ApplyDefaultSort = initApplyDefaultSort;
\r
497 RowFilter = initRowFilter;
\r
498 RowStateFilter = initRowState;
\r
502 UpdateIndex (true);
\r
505 dataViewInitialized = true;
\r
506 DataViewInitialized ();
\r
510 public int Find(object key)
\r
512 object [] keys = new object[] { key };
\r
516 public int Find(object[] keys)
\r
518 if (sort == null || sort == string.Empty) {
\r
519 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");
\r
522 if (Index == null) {
\r
528 index = Index.FindIndex(keys);
\r
530 catch(FormatException) {
\r
531 // suppress exception
\r
533 catch(InvalidCastException) {
\r
534 // suppress exception
\r
539 public DataRowView[] FindRows(object key)
\r
541 return FindRows(new object[] {key});
\r
544 public DataRowView[] FindRows(object[] keys)
\r
546 if (sort == null || sort == string.Empty) {
\r
547 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");
\r
550 if (Index == null) {
\r
554 int[] indexes = Index.FindAllIndexes(keys);
\r
556 DataRowView[] rowViewArr = new DataRowView[indexes.Length];
\r
557 for (int r = 0; r < indexes.Length; r++) {
\r
558 rowViewArr[r] = rowCache[indexes[r]];
\r
563 public IEnumerator GetEnumerator()
\r
565 DataRowView[] dataRowViews = new DataRowView[Count];
\r
566 CopyTo(dataRowViews,0);
\r
567 return dataRowViews.GetEnumerator();
\r
570 #endregion // PublicMethods
\r
572 [DataCategory ("Data")]
\r
574 [DataSysDescription ("Indicates that the data returned by this DataView has somehow changed.")]
\r
576 public event ListChangedEventHandler ListChanged;
\r
579 public event EventHandler Initialized;
\r
582 [Browsable (false)]
\r
584 [DataSysDescription ("Indicates whether the view is open. ")]
\r
586 protected bool IsOpen {
\r
587 get { return isOpen; }
\r
590 internal Index Index
\r
597 if (_index != null) {
\r
598 _index.RemoveRef();
\r
599 Table.DropIndex(_index);
\r
604 if (_index != null) {
\r
610 protected void Close ()
\r
612 if (dataTable != null)
\r
613 UnregisterEventHandlers ();
\r
615 rowCache = new DataRowView [0];
\r
619 protected override void Dispose (bool disposing)
\r
624 base.Dispose (disposing);
\r
627 protected virtual void IndexListChanged (
\r
628 object sender, ListChangedEventArgs e)
\r
632 protected virtual void OnListChanged(ListChangedEventArgs e)
\r
634 // Yes, under MS.NET, when it is overriden, the
\r
635 // events are not fired (even if it is essential
\r
636 // to internal processing).
\r
638 if (ListChanged != null)
\r
639 ListChanged (this, e);
\r
644 internal void ChangedList(ListChangedType listChangedType, int newIndex,int oldIndex)
\r
646 ListChangedEventArgs e = new ListChangedEventArgs(listChangedType,newIndex,oldIndex);
\r
650 protected void Open()
\r
652 // I wonder if this comment is still valid, but keep
\r
653 // in the meantime.
\r
655 // FIXME: create the initial index cache to the DataTable, and
\r
656 // only refresh the index when the DataTable
\r
657 // has changes via column, row, or constraint
\r
658 // changed events. the index cache is generally
\r
659 // a DataViewRow array that points to the actual
\r
660 // DataRows in the this DataTable's DataRowCollection;
\r
661 // this index is really a cache that gets
\r
662 // created during Open(), gets Updated
\r
663 // when various properties of this view
\r
664 // changes, gets Updated when this DataTable's
\r
665 // row, column, or constraint collections have changed.
\r
666 // I'm not sure what else.
\r
667 // The data view will know one of the DataTable's
\r
668 // collections have changed via one of
\r
669 // its changed events.
\r
670 // Otherwise, if getting a/the DataRowView(s),
\r
671 // Count, or other properties, then just use the
\r
673 // dataTable.ColumnChanged += new DataColumnChangeEventHandler(OnColumnChanged);
\r
675 UpdateIndex (true);
\r
676 if (dataTable != null) {
\r
677 RegisterEventHandlers();
\r
682 private void RegisterEventHandlers()
\r
684 //dataTable.ColumnChanging += new DataColumnChangeEventHandler(OnColumnChanging);
\r
685 dataTable.ColumnChanged += new DataColumnChangeEventHandler(OnColumnChanged);
\r
686 dataTable.RowChanged += new DataRowChangeEventHandler(OnRowChanged);
\r
687 //dataTable.RowDeleting += new DataRowChangeEventHandler(OnRowDeleting);
\r
688 dataTable.RowDeleted += new DataRowChangeEventHandler(OnRowDeleted);
\r
689 dataTable.Columns.CollectionChanged += new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
690 dataTable.Columns.CollectionMetaDataChanged += new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
691 dataTable.Constraints.CollectionChanged += new CollectionChangeEventHandler(OnConstraintCollectionChanged);
\r
692 dataTable.ChildRelations.CollectionChanged += new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
693 dataTable.ParentRelations.CollectionChanged += new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
695 dataTable.Rows.ListChanged += new ListChangedEventHandler (OnRowCollectionChanged);
\r
698 private void OnRowCollectionChanged (object sender, ListChangedEventArgs args)
\r
700 if (args.ListChangedType == ListChangedType.Reset) {
\r
701 rowCache = new DataRowView [0];
\r
702 UpdateIndex (true);
\r
703 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));
\r
707 private void UnregisterEventHandlers()
\r
709 // dataTable.ColumnChanging -= new DataColumnChangeEventHandler(OnColumnChanging);
\r
710 dataTable.ColumnChanged -= new DataColumnChangeEventHandler(OnColumnChanged);
\r
711 dataTable.RowChanged -= new DataRowChangeEventHandler(OnRowChanged);
\r
712 // dataTable.RowDeleting -= new DataRowChangeEventHandler(OnRowDeleting);
\r
713 dataTable.RowDeleted -= new DataRowChangeEventHandler(OnRowDeleted);
\r
714 dataTable.Columns.CollectionChanged -= new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
715 dataTable.Columns.CollectionMetaDataChanged -= new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
716 dataTable.Constraints.CollectionChanged -= new CollectionChangeEventHandler(OnConstraintCollectionChanged);
\r
717 dataTable.ChildRelations.CollectionChanged -= new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
718 dataTable.ParentRelations.CollectionChanged -= new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
720 dataTable.Rows.ListChanged -= new ListChangedEventHandler (OnRowCollectionChanged);
\r
723 // These index storing and rowView preservation must be done
\r
724 // before the actual row value is changed; thus we can't use
\r
725 // RowChanging which accepts "already modified" DataRow.
\r
727 private void OnColumnChanged(object sender, DataColumnChangeEventArgs args)
\r
729 //UpdateIndex(true);
\r
732 private void OnRowChanged(object sender, DataRowChangeEventArgs args)
\r
734 int oldIndex,newIndex;
\r
735 oldIndex = newIndex = -1;
\r
736 oldIndex = IndexOf (args.Row);
\r
737 UpdateIndex (true);
\r
738 newIndex = IndexOf (args.Row);
\r
741 if(args.Action == DataRowAction.Add)
\r
743 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, newIndex, -1));
\r
746 /* ItemChanged or ItemMoved */
\r
747 if (args.Action == DataRowAction.Change) {
\r
748 if (oldIndex == newIndex)
\r
749 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemChanged, newIndex, -1));
\r
751 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemMoved, newIndex, oldIndex));
\r
755 private void OnRowDeleted (object sender, DataRowChangeEventArgs args)
\r
759 newIndex = IndexOf (args.Row);
\r
760 UpdateIndex (true);
\r
761 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, newIndex, -1));
\r
765 private void DataViewInitialized ()
\r
767 EventArgs e = new EventArgs ();
\r
768 OnDataViewInitialized (e);
\r
771 private void OnDataViewInitialized (EventArgs e) {
\r
772 if (null != Initialized) {
\r
773 Initialized (this, e);
\r
777 protected virtual void ColumnCollectionChanged (object sender, CollectionChangeEventArgs args)
\r
779 // UpdateIndex() is not invoked here (even if the sort
\r
780 // column is being removed).
\r
782 /* PropertyDescriptor Add */
\r
783 if (args.Action == CollectionChangeAction.Add)
\r
784 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorAdded,0,0));
\r
785 /* PropertyDescriptor Removed */
\r
786 if (args.Action == CollectionChangeAction.Remove)
\r
787 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorDeleted,0,0));
\r
788 /* PropertyDescriptor Changed, comes from DataColumnCollection.CollectionMetaDataChanged */
\r
789 if (args.Action == CollectionChangeAction.Refresh)
\r
790 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged,0,0));
\r
793 private void OnConstraintCollectionChanged(object sender, CollectionChangeEventArgs args)
\r
795 // The Sort variable is set to the UniqueConstraint column.
\r
796 // if ApplyDefault Sort is true and Sort is null or is not set Explicitly
\r
798 // FIXME: The interal cache may change as result of change in Constraint collection
\r
799 // one such scenerio is taken care.
\r
800 // There may be more. I dont know what else can be done.
\r
801 /* useDefaultSort is set to false when Sort is set explicitly */
\r
802 if (args.Action == CollectionChangeAction.Add && args.Element is UniqueConstraint) {
\r
803 if (ApplyDefaultSort == true && useDefaultSort == true)
\r
804 PopulateDefaultSort ((UniqueConstraint) args.Element);
\r
806 // UpdateIndex() is not invoked here.
\r
809 private void OnRelationCollectionChanged(object sender, CollectionChangeEventArgs args)
\r
811 /* PropertyDescriptor Add */
\r
812 if (args.Action == CollectionChangeAction.Add)
\r
813 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorAdded,0,0));
\r
814 /* PropertyDescriptor Removed */
\r
815 if (args.Action == CollectionChangeAction.Remove)
\r
816 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorDeleted,0,0));
\r
817 /* FIXME: PropertyDescriptor Changed ???*/
\r
818 if (args.Action == CollectionChangeAction.Refresh)
\r
819 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged,0,0));
\r
822 // internal use by Mono
\r
823 protected void Reset()
\r
825 // TODO: what really happens?
\r
827 rowCache = new DataRowView [0];
\r
829 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));
\r
833 public DataTable ToTable ()
\r
835 return this.ToTable (Table.TableName, false, new string[] {});
\r
838 public DataTable ToTable (string tableName)
\r
840 return this.ToTable (tableName, false, new string[] {});
\r
843 public DataTable ToTable (bool isDistinct, params string[] columnNames)
\r
845 return this.ToTable (Table.TableName, isDistinct, columnNames);
\r
848 public DataTable ToTable (string tablename, bool isDistinct, params string[] columnNames)
\r
850 if (columnNames == null)
\r
851 throw new ArgumentNullException ("columnNames", "'columnNames' argument cannot be null.");
\r
853 DataTable newTable = new DataTable (tablename);
\r
855 DataColumn[] columns;
\r
856 ListSortDirection[] sortDirection = null;
\r
857 if (columnNames.Length != 0) {
\r
858 columns = new DataColumn [columnNames.Length];
\r
859 for (int i=0; i < columnNames.Length; ++i)
\r
860 columns [i] = Table.Columns [columnNames [i]];
\r
862 if (sortColumns != null) {
\r
863 sortDirection = new ListSortDirection [columnNames.Length];
\r
864 for (int i=0; i < columnNames.Length; ++i) {
\r
865 sortDirection [i] = ListSortDirection.Ascending;
\r
866 for (int j=0; j < sortColumns.Length; ++j) {
\r
867 if (sortColumns [j] != columns [i])
\r
869 sortDirection [i] = sortOrder [j];
\r
874 columns = (DataColumn[]) Table.Columns.ToArray (typeof (DataColumn));
\r
875 sortDirection = sortOrder;
\r
878 ArrayList expressionCols = new ArrayList ();
\r
879 for (int i=0; i < columns.Length; ++i) {
\r
880 DataColumn col = columns [i].Clone ();
\r
881 if (col.Expression != String.Empty) {
\r
882 col.Expression = "";
\r
883 expressionCols.Add (col);
\r
886 col.ReadOnly = false;
\r
887 newTable.Columns.Add (col);
\r
891 Index index = new Index (new Key(Table, columns, sortDirection, RowStateFilter, rowFilterExpr));
\r
893 rows = index.GetDistinctRows ();
\r
895 rows = index.GetAllRows ();
\r
897 foreach (DataRow row in rows) {
\r
898 DataRow newRow = newTable.NewNotInitializedRow ();
\r
899 newTable.Rows.AddInternal (newRow);
\r
900 newRow.Original = -1;
\r
901 if (row.HasVersion (DataRowVersion.Current))
\r
902 newRow.Current = newTable.RecordCache.CopyRecord (Table, row.Current, -1);
\r
903 else if (row.HasVersion (DataRowVersion.Original))
\r
904 newRow.Current = newTable.RecordCache.CopyRecord (Table, row.Original, -1);
\r
906 foreach (DataColumn col in expressionCols)
\r
907 newRow [col] = row [col.ColumnName];
\r
908 newRow.Original = -1;
\r
913 protected void UpdateIndex()
\r
915 UpdateIndex(false);
\r
918 // This is method is internal to
\r
919 // the Mono implementation of DataView; it
\r
920 // is not to be used from your code.
\r
922 // Update the DataRowView array which is an index cache
\r
923 // into the DataTable's DataRowCollection.
\r
925 // I assume this is what UpdateIndex is used for
\r
926 protected virtual void UpdateIndex(bool force)
\r
928 if (Table == null) {
\r
933 if (Index == null || force) {
\r
934 sortColumns = DataTable.ParseSortString(Table, Sort, out sortOrder, false);
\r
935 Index = dataTable.GetIndex(sortColumns,sortOrder,RowStateFilter,FilterExpression,true);
\r
938 Index.Key.RowStateFilter = RowStateFilter;
\r
942 int[] records = Index.GetAll();
\r
944 if (records != null) {
\r
945 InitDataRowViewArray(records,Index.Size);
\r
948 rowCache = new DataRowView[0];
\r
952 internal virtual IExpression FilterExpression
\r
955 return rowFilterExpr;
\r
959 private void InitDataRowViewArray(int[] records,int size)
\r
961 if (_lastAdded != null) {
\r
962 rowCache = new DataRowView[size + 1];
\r
965 rowCache = new DataRowView[size];
\r
968 for (int r = 0; r < size; r++) {
\r
969 rowCache[r] = new DataRowView (this, Table.RecordCache[records[r]],r);
\r
972 if(_lastAdded != null) {
\r
973 rowCache[size] = new DataRowView(this,_lastAdded,size);
\r
977 PropertyDescriptorCollection ITypedList.GetItemProperties (PropertyDescriptor[] listAccessors)
\r
979 if (dataTable == null)
\r
980 return new PropertyDescriptorCollection (new PropertyDescriptor[0]);
\r
982 // FIXME: use listAccessors somehow
\r
983 PropertyDescriptor [] descriptors =
\r
984 new PropertyDescriptor [dataTable.Columns.Count + dataTable.ChildRelations.Count];
\r
987 for (int col = 0; col < dataTable.Columns.Count; col ++) {
\r
988 DataColumn dataColumn = dataTable.Columns[col];
\r
989 DataColumnPropertyDescriptor descriptor;
\r
991 descriptor = new DataColumnPropertyDescriptor(
\r
992 dataColumn.ColumnName, col, null);
\r
993 descriptor.SetComponentType (typeof (System.Data.DataRowView));
\r
994 descriptor.SetPropertyType (dataColumn.DataType);
\r
995 descriptor.SetReadOnly (dataColumn.ReadOnly);
\r
996 descriptors [d++] = descriptor;
\r
998 for (int rel = 0; rel < dataTable.ChildRelations.Count; rel ++) {
\r
999 DataRelation dataRelation = dataTable.ChildRelations[rel];
\r
1000 DataRelationPropertyDescriptor descriptor;
\r
1002 descriptor = new DataRelationPropertyDescriptor(dataRelation);
\r
1003 descriptors [d++] = descriptor;
\r
1006 return new PropertyDescriptorCollection (descriptors);
\r
1010 string ITypedList.GetListName (PropertyDescriptor[] listAccessors)
\r
1012 if (dataTable != null) {
\r
1013 return dataTable.TableName;
\r
1018 bool ICollection.IsSynchronized {
\r
1024 object ICollection.SyncRoot {
\r
1030 bool IList.IsFixedSize {
\r
1036 bool IList.IsReadOnly {
\r
1042 object IList.this[int recordIndex] {
\r
1044 return this[recordIndex];
\r
1049 throw new InvalidOperationException();
\r
1053 int IList.Add (object value)
\r
1055 throw new ArgumentException ("Cannot add external objects to this list.");
\r
1058 void IList.Clear ()
\r
1060 throw new ArgumentException ("Cannot clear this list.");
\r
1063 bool IList.Contains (object value)
\r
1065 DataRowView drv = value as DataRowView;
\r
1069 return drv.DataView == this;
\r
1072 int IList.IndexOf (object value)
\r
1074 DataRowView drv = value as DataRowView;
\r
1075 if (drv != null && drv.DataView == this) {
\r
1082 void IList.Insert(int index,object value)
\r
1084 throw new ArgumentException ("Cannot insert external objects to this list.");
\r
1087 void IList.Remove(object value)
\r
1089 DataRowView drv = value as DataRowView;
\r
1090 if (drv != null && drv.DataView == this) {
\r
1091 ((IList)this).RemoveAt(drv.Index);
\r
1094 throw new ArgumentException ("Cannot remove external objects to this list.");
\r
1097 void IList.RemoveAt(int index)
\r
1102 #region IBindingList implementation
\r
1105 void IBindingList.AddIndex (PropertyDescriptor property)
\r
1107 throw new NotImplementedException ();
\r
1110 object IBindingList.AddNew ()
\r
1112 return this.AddNew ();
\r
1115 void IBindingList.ApplySort (PropertyDescriptor property, ListSortDirection direction)
\r
1117 if (! (property is DataColumnPropertyDescriptor))
\r
1118 throw new ArgumentException ("Dataview accepts only DataColumnPropertyDescriptors", "property");
\r
1119 sortProperty = property;
\r
1120 string sort = String.Format ("[{0}]" , property.Name);
\r
1121 if (direction == ListSortDirection.Descending)
\r
1126 int IBindingList.Find (PropertyDescriptor property, object key)
\r
1128 DataColumn dc = Table.Columns [property.Name];
\r
1129 Index index = Table.FindIndex (new DataColumn [] {dc}, sortOrder, RowStateFilter, FilterExpression);
\r
1130 if (index == null)
\r
1131 index = new Index (new Key (Table, new DataColumn [] {dc}, sortOrder, RowStateFilter, FilterExpression));
\r
1133 return index.FindIndex (new object [] {key});
\r
1137 void IBindingList.RemoveIndex (PropertyDescriptor property)
\r
1139 throw new NotImplementedException ();
\r
1142 void IBindingList.RemoveSort ()
\r
1144 sortProperty = null;
\r
1145 this.Sort = String.Empty;
\r
1148 bool IBindingList.AllowEdit {
\r
1149 get { return AllowEdit; }
\r
1152 bool IBindingList.AllowNew {
\r
1153 get { return AllowNew; }
\r
1156 bool IBindingList.AllowRemove {
\r
1158 get { return AllowDelete; }
\r
1161 bool IBindingList.IsSorted {
\r
1162 get { return (Sort != null && Sort != String.Empty); }
\r
1165 ListSortDirection IBindingList.SortDirection {
\r
1167 if (sortOrder != null && sortOrder.Length > 0)
\r
1168 return sortOrder [0];
\r
1169 return ListSortDirection.Ascending;
\r
1173 PropertyDescriptor IBindingList.SortProperty {
\r
1175 if (sortProperty == null && sortColumns != null && sortColumns.Length > 0) {
\r
1176 // return property from Sort String
\r
1177 PropertyDescriptorCollection properties = ((ITypedList)this).GetItemProperties (null);
\r
1178 return properties.Find (sortColumns [0].ColumnName, false);
\r
1180 return sortProperty;
\r
1184 bool IBindingList.SupportsChangeNotification {
\r
1185 get { return true; }
\r
1188 bool IBindingList.SupportsSearching {
\r
1189 get { return true; }
\r
1192 bool IBindingList.SupportsSorting {
\r
1193 get { return true; }
\r
1196 #endregion // IBindingList implementation
\r
1199 #region IBindingListView implementation
\r
1200 string IBindingListView.Filter {
\r
1201 get { return ((DataView)this).RowFilter; }
\r
1202 set { ((DataView)this).RowFilter = value; }
\r
1205 ListSortDescriptionCollection IBindingListView.SortDescriptions {
\r
1207 ListSortDescriptionCollection col = new ListSortDescriptionCollection ();
\r
1208 for (int i=0; i < sortColumns.Length; ++i) {
\r
1209 ListSortDescription ldesc = new ListSortDescription (
\r
1210 new DataColumnPropertyDescriptor (sortColumns [i]),
\r
1212 ((IList)col).Add (ldesc);
\r
1218 bool IBindingListView.SupportsAdvancedSorting {
\r
1219 get { return true; }
\r
1222 bool IBindingListView.SupportsFiltering {
\r
1223 get { return true; }
\r
1227 void IBindingListView.ApplySort (ListSortDescriptionCollection sorts)
\r
1229 StringBuilder sb = new StringBuilder ();
\r
1230 foreach (ListSortDescription ldesc in sorts)
\r
1231 sb.AppendFormat ("[{0}]{1},", ldesc.PropertyDescriptor.Name,
\r
1232 (ldesc.SortDirection == ListSortDirection.Descending ? " DESC" : ""));
\r
1233 this.Sort = sb.ToString (0, sb.Length-1);
\r
1236 void IBindingListView.RemoveFilter ()
\r
1238 ((IBindingListView)this).Filter = "";
\r
1241 #endregion //IBindingViewList implementation
\r
1244 private int IndexOf(DataRow dr)
\r
1246 for (int i=0; i < rowCache.Length; i++)
\r
1247 if (dr.Equals (rowCache [i].Row))
\r
1252 private void PopulateDefaultSort () {
\r
1254 foreach (Constraint c in dataTable.Constraints) {
\r
1255 if (c is UniqueConstraint) {
\r
1256 PopulateDefaultSort ((UniqueConstraint) c);
\r
1262 private void PopulateDefaultSort (UniqueConstraint uc) {
\r
1266 DataColumn[] columns = uc.Columns;
\r
1267 if (columns.Length == 0) {
\r
1268 sort = String.Empty;
\r
1272 StringBuilder builder = new StringBuilder();
\r
1273 builder.Append(columns[0].ColumnName);
\r
1274 for (int i = 1; i<columns.Length; i++) {
\r
1275 builder.Append(", ");
\r
1276 builder.Append(columns[i].ColumnName);
\r
1278 sort = builder.ToString();
\r
1281 internal DataView CreateChildView (DataRelation relation, int index)
\r
1283 if (relation == null || relation.ParentTable != Table) {
\r
1284 throw new ArgumentException("The relation is not parented to the table to which this DataView points.");
\r
1287 int record = GetRecord(index);
\r
1288 object[] keyValues = new object[relation.ParentColumns.Length];
\r
1289 for(int i=0; i < relation.ParentColumns.Length; i++)
\r
1290 keyValues [i] = relation.ParentColumns [i][record];
\r
1292 return new RelatedDataView(relation.ChildColumns,keyValues);
\r
1295 private int GetRecord(int index) {
\r
1296 if (index < 0 || index >= Count)
\r
1297 throw new IndexOutOfRangeException(String.Format("There is no row at position {0}.", index));
\r
1299 return(index == Index.Size) ?
\r
1300 _lastAdded.IndexFromVersion(DataRowVersion.Default) :
\r
1301 Index.IndexToRecord(index);
\r
1304 internal DataRowVersion GetRowVersion(int index) {
\r
1305 int record = GetRecord(index);
\r
1306 return Table.RecordCache[record].VersionFromIndex(record);
\r