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
38 public partial class DataView : MarshalByValueComponent, IEnumerable, ISupportInitialize {
\r
39 internal DataTable dataTable;
\r
40 string rowFilter = String.Empty;
\r
41 IExpression rowFilterExpr;
\r
42 string sort = String.Empty;
\r
43 ListSortDirection [] sortOrder;
\r
44 PropertyDescriptor sortProperty;
\r
45 DataColumn [] sortColumns;
\r
46 internal DataViewRowState rowState;
\r
47 internal DataRowView[] rowCache = new DataRowView [0];
\r
49 // BeginInit() support
\r
52 DataTable initTable;
\r
53 bool initApplyDefaultSort;
\r
55 string initRowFilter;
\r
56 DataViewRowState initRowState;
\r
58 // FIXME: what are the default values?
\r
59 bool allowNew = true;
\r
60 bool allowEdit = true;
\r
61 bool allowDelete = true;
\r
62 bool applyDefaultSort;
\r
63 //bool isSorted = false;
\r
67 bool useDefaultSort = true;
\r
70 internal DataRow _lastAdded;
\r
72 private DataViewManager dataViewManager;
\r
73 internal static ListChangedEventArgs ListResetEventArgs = new ListChangedEventArgs (ListChangedType.Reset,-1,-1);
\r
77 rowState = DataViewRowState.CurrentRows;
\r
81 public DataView (DataTable table)
\r
82 : this (table, (DataViewManager) null)
\r
86 internal DataView (DataTable table, DataViewManager manager)
\r
89 rowState = DataViewRowState.CurrentRows;
\r
90 dataViewManager = manager;
\r
94 public DataView (DataTable table, string RowFilter,
\r
95 string Sort, DataViewRowState RowState)
\r
96 : this (table, null, RowFilter, Sort, RowState)
\r
100 internal DataView (DataTable table, DataViewManager manager,
\r
101 string RowFilter, string Sort, DataViewRowState RowState)
\r
104 dataViewManager = manager;
\r
105 rowState = DataViewRowState.CurrentRows;
\r
106 this.RowFilter = RowFilter;
\r
108 rowState = RowState;
\r
112 [DataCategory ("Data")]
\r
114 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows deletes.")]
\r
116 [DefaultValue (true)]
\r
117 public bool AllowDelete {
\r
118 get { return allowDelete; }
\r
119 set { allowDelete = value; }
\r
122 [DataCategory ("Data")]
\r
124 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows edits.")]
\r
126 [DefaultValue (true)]
\r
127 public bool AllowEdit {
\r
128 get { return allowEdit; }
\r
129 set { allowEdit = value; }
\r
132 [DataCategory ("Data")]
\r
134 [DataSysDescription ("Indicates whether this DataView and the user interface associated with it allows new rows to be added.")]
\r
136 [DefaultValue (true)]
\r
137 public bool AllowNew {
\r
138 get { return allowNew; }
\r
139 set { allowNew = value; }
\r
142 [DataCategory ("Data")]
\r
144 [DataSysDescription ("Indicates whether to use the default sort if the Sort property is not set.")]
\r
146 [DefaultValue (false)]
\r
147 [RefreshProperties (RefreshProperties.All)]
\r
148 public bool ApplyDefaultSort {
\r
149 get { return applyDefaultSort; }
\r
152 initApplyDefaultSort = value;
\r
155 if (applyDefaultSort == value)
\r
158 applyDefaultSort = value;
\r
159 if (applyDefaultSort == true && (sort == null || sort == string.Empty))
\r
160 PopulateDefaultSort ();
\r
162 UpdateIndex (true);
\r
163 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
167 // get the count of rows in the DataView after RowFilter
\r
168 // and RowStateFilter have been applied
\r
169 [Browsable (false)]
\r
171 [DataSysDescription ("Returns the number of items currently in this view.")]
\r
174 get { return rowCache.Length; }
\r
177 [Browsable (false)]
\r
179 [DataSysDescription ("This returns a pointer to back to the DataViewManager that owns this DataSet (if any).")]
\r
181 public DataViewManager DataViewManager {
\r
182 get { return dataViewManager; }
\r
186 // the compiler creates a DefaultMemeberAttribute from
\r
187 // this IndexerNameAttribute
\r
188 [System.Runtime.CompilerServices.IndexerName("Item")]
\r
189 public DataRowView this [int recordIndex] {
\r
191 if (recordIndex > rowCache.Length)
\r
192 throw new IndexOutOfRangeException ("There is no row at position: " + recordIndex + ".");
\r
193 return rowCache [recordIndex];
\r
197 [DataCategory ("Data")]
\r
199 [DataSysDescription ("Indicates an expression used to filter the data returned by this DataView.")]
\r
201 [DefaultValue ("")]
\r
202 public virtual string RowFilter {
\r
203 get { return rowFilter; }
\r
206 value = String.Empty;
\r
208 initRowFilter = value;
\r
212 CultureInfo info = (Table != null) ? Table.Locale : CultureInfo.CurrentCulture;
\r
213 if (String.Compare (rowFilter, value, false, info) == 0)
\r
216 if (value.Length == 0) {
\r
217 rowFilterExpr = null;
\r
219 Parser parser = new Parser ();
\r
220 rowFilterExpr = parser.Compile (value);
\r
224 UpdateIndex (true);
\r
225 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
230 [DataCategory ("Data")]
\r
232 [DataSysDescription ("Indicates the versions of data returned by this DataView.")]
\r
234 [DefaultValue (DataViewRowState.CurrentRows)]
\r
235 public DataViewRowState RowStateFilter {
\r
236 get { return rowState; }
\r
239 initRowState = value;
\r
243 if (value == rowState)
\r
248 UpdateIndex (true);
\r
249 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
254 [DataCategory ("Data")]
\r
256 [DataSysDescription ("Indicates the order in which data is returned by this DataView.")]
\r
258 [DefaultValue ("")]
\r
259 public string Sort {
\r
261 if (useDefaultSort)
\r
262 return String.Empty;
\r
274 if (value == null || value.Length == 0) {
\r
275 /* if given value is null useDefaultSort */
\r
276 useDefaultSort = true;
\r
277 /* if ApplyDefault sort is true try appling it */
\r
278 if (ApplyDefaultSort)
\r
279 PopulateDefaultSort ();
\r
281 /* else donot useDefaultSort. set it as false */
\r
282 /* sort is set to value specified */
\r
283 useDefaultSort = false;
\r
285 //sortedColumns = SortableColumn.ParseSortString (dataTable, value, true);
\r
289 UpdateIndex (true);
\r
290 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
295 [TypeConverter (typeof (DataTableTypeConverter))]
\r
296 [DataCategory ("Data")]
\r
298 [DataSysDescription ("Indicates the table this DataView uses to get data.")]
\r
300 [DefaultValue (null)]
\r
301 [RefreshProperties (RefreshProperties.All)]
\r
302 public DataTable Table {
\r
303 get { return dataTable; }
\r
305 if (value == dataTable)
\r
313 if (value != null && value.TableName.Equals(string.Empty)) {
\r
314 throw new DataException("Cannot bind to DataTable with no name.");
\r
317 if (dataTable != null)
\r
318 UnregisterEventHandlers ();
\r
322 if (dataTable != null) {
\r
323 RegisterEventHandlers ();
\r
324 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged, 0, 0));
\r
325 sort = string.Empty;
\r
326 rowFilter = string.Empty;
\r
328 UpdateIndex (true);
\r
329 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1));
\r
335 public virtual DataRowView AddNew ()
\r
338 throw new DataException ("DataView is not open.");
\r
340 throw new DataException ("Cannot call AddNew on a DataView where AllowNew is false.");
\r
342 if (_lastAdded != null)
\r
343 // FIXME : finish last added
\r
344 CompleteLastAdded (true);
\r
346 _lastAdded = dataTable.NewRow ();
\r
347 UpdateIndex (true);
\r
348 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, Count - 1, -1));
\r
350 return this [Count - 1];
\r
353 internal void CompleteLastAdded (bool add)
\r
355 DataRow dr = _lastAdded;
\r
359 dataTable.Rows.Add (_lastAdded);
\r
360 //OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, Count - 1, -1));
\r
363 } catch (Exception) {
\r
368 _lastAdded.CancelEdit ();
\r
371 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, Count, -1));
\r
375 public void BeginInit ()
\r
378 initApplyDefaultSort = ApplyDefaultSort;
\r
380 initRowFilter = RowFilter;
\r
381 initRowState = RowStateFilter;
\r
383 isInitPhase = true;
\r
384 DataViewInitialized (false);
\r
387 partial void DataViewInitialized (bool value);
\r
389 public void CopyTo (Array array, int index)
\r
391 if (index + rowCache.Length > array.Length)
\r
392 throw new IndexOutOfRangeException ();
\r
395 for (; row < rowCache.Length && row < array.Length; row++)
\r
396 array.SetValue (rowCache [row], index + row);
\r
399 public void Delete (int index)
\r
402 throw new DataException ("DataView is not open.");
\r
404 if (_lastAdded != null && index == Count) {
\r
405 CompleteLastAdded (false);
\r
410 throw new DataException ("Cannot delete on a DataSource where AllowDelete is false.");
\r
412 if (index > rowCache.Length)
\r
413 throw new IndexOutOfRangeException ("There is no row at position: " + index + ".");
\r
414 DataRowView row = rowCache [index];
\r
418 public void EndInit ()
\r
420 isInitPhase = false;
\r
425 ApplyDefaultSort = initApplyDefaultSort;
\r
427 RowFilter = initRowFilter;
\r
428 RowStateFilter = initRowState;
\r
432 UpdateIndex (true);
\r
434 DataViewInitialized (true);
\r
437 public int Find (object key)
\r
439 object [] keys = new object[] { key };
\r
440 return Find (keys);
\r
443 public int Find (object [] key)
\r
445 if (sort == null || sort.Length == 0)
\r
446 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");
\r
449 UpdateIndex (true);
\r
453 index = Index.FindIndex (key);
\r
454 } catch (FormatException) {
\r
455 // suppress exception
\r
456 } catch (InvalidCastException) {
\r
457 // suppress exception
\r
462 public DataRowView [] FindRows (object key)
\r
464 return FindRows (new object[] {key});
\r
467 public DataRowView [] FindRows (object [] key)
\r
469 if (sort == null || sort.Length == 0)
\r
470 throw new ArgumentException ("Find finds a row based on a Sort order, and no Sort order is specified");
\r
473 UpdateIndex (true);
\r
475 int [] indexes = Index.FindAllIndexes (key);
\r
477 DataRowView[] rowViewArr = new DataRowView [indexes.Length];
\r
478 for (int r = 0; r < indexes.Length; r++)
\r
479 rowViewArr [r] = rowCache [indexes[r]];
\r
483 public IEnumerator GetEnumerator ()
\r
485 DataRowView[] dataRowViews = new DataRowView [Count];
\r
486 CopyTo (dataRowViews, 0);
\r
487 return dataRowViews.GetEnumerator ();
\r
490 [DataCategory ("Data")]
\r
492 [DataSysDescription ("Indicates that the data returned by this DataView has somehow changed.")]
\r
494 public event ListChangedEventHandler ListChanged;
\r
496 [Browsable (false)]
\r
498 [DataSysDescription ("Indicates whether the view is open. ")]
\r
500 protected bool IsOpen {
\r
501 get { return isOpen; }
\r
504 internal Index Index {
\r
505 get { return _index; }
\r
507 if (_index != null) {
\r
508 _index.RemoveRef ();
\r
509 Table.DropIndex (_index);
\r
514 if (_index != null)
\r
519 protected void Close ()
\r
521 if (dataTable != null)
\r
522 UnregisterEventHandlers ();
\r
524 rowCache = new DataRowView [0];
\r
528 protected override void Dispose (bool disposing)
\r
533 base.Dispose (disposing);
\r
536 protected virtual void IndexListChanged (object sender, ListChangedEventArgs e)
\r
540 protected virtual void OnListChanged (ListChangedEventArgs e)
\r
542 // Yes, under MS.NET, when it is overriden, the
\r
543 // events are not fired (even if it is essential
\r
544 // to internal processing).
\r
546 if (ListChanged != null)
\r
547 ListChanged (this, e);
\r
552 internal void ChangedList (ListChangedType listChangedType, int newIndex,int oldIndex)
\r
554 ListChangedEventArgs e = new ListChangedEventArgs (listChangedType,newIndex,oldIndex);
\r
558 protected void Open ()
\r
560 // I wonder if this comment is still valid, but keep
\r
561 // in the meantime.
\r
563 // FIXME: create the initial index cache to the DataTable, and
\r
564 // only refresh the index when the DataTable
\r
565 // has changes via column, row, or constraint
\r
566 // changed events. the index cache is generally
\r
567 // a DataViewRow array that points to the actual
\r
568 // DataRows in the this DataTable's DataRowCollection;
\r
569 // this index is really a cache that gets
\r
570 // created during Open(), gets Updated
\r
571 // when various properties of this view
\r
572 // changes, gets Updated when this DataTable's
\r
573 // row, column, or constraint collections have changed.
\r
574 // I'm not sure what else.
\r
575 // The data view will know one of the DataTable's
\r
576 // collections have changed via one of
\r
577 // its changed events.
\r
578 // Otherwise, if getting a/the DataRowView(s),
\r
579 // Count, or other properties, then just use the
\r
581 // dataTable.ColumnChanged += new DataColumnChangeEventHandler(OnColumnChanged);
\r
583 UpdateIndex (true);
\r
584 if (dataTable != null)
\r
585 RegisterEventHandlers ();
\r
589 private void RegisterEventHandlers ()
\r
591 //dataTable.ColumnChanging += new DataColumnChangeEventHandler(OnColumnChanging);
\r
592 dataTable.ColumnChanged += new DataColumnChangeEventHandler(OnColumnChanged);
\r
593 dataTable.RowChanged += new DataRowChangeEventHandler(OnRowChanged);
\r
594 //dataTable.RowDeleting += new DataRowChangeEventHandler(OnRowDeleting);
\r
595 dataTable.RowDeleted += new DataRowChangeEventHandler(OnRowDeleted);
\r
596 dataTable.Columns.CollectionChanged += new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
597 dataTable.Columns.CollectionMetaDataChanged += new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
598 dataTable.Constraints.CollectionChanged += new CollectionChangeEventHandler(OnConstraintCollectionChanged);
\r
599 dataTable.ChildRelations.CollectionChanged += new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
600 dataTable.ParentRelations.CollectionChanged += new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
602 dataTable.Rows.ListChanged += new ListChangedEventHandler (OnRowCollectionChanged);
\r
605 private void OnRowCollectionChanged (object sender, ListChangedEventArgs args)
\r
607 if (args.ListChangedType == ListChangedType.Reset) {
\r
608 rowCache = new DataRowView [0];
\r
609 UpdateIndex (true);
\r
610 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));
\r
614 private void UnregisterEventHandlers ()
\r
616 // dataTable.ColumnChanging -= new DataColumnChangeEventHandler(OnColumnChanging);
\r
617 dataTable.ColumnChanged -= new DataColumnChangeEventHandler(OnColumnChanged);
\r
618 dataTable.RowChanged -= new DataRowChangeEventHandler(OnRowChanged);
\r
619 // dataTable.RowDeleting -= new DataRowChangeEventHandler(OnRowDeleting);
\r
620 dataTable.RowDeleted -= new DataRowChangeEventHandler(OnRowDeleted);
\r
621 dataTable.Columns.CollectionChanged -= new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
622 dataTable.Columns.CollectionMetaDataChanged -= new CollectionChangeEventHandler(ColumnCollectionChanged);
\r
623 dataTable.Constraints.CollectionChanged -= new CollectionChangeEventHandler(OnConstraintCollectionChanged);
\r
624 dataTable.ChildRelations.CollectionChanged -= new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
625 dataTable.ParentRelations.CollectionChanged -= new CollectionChangeEventHandler(OnRelationCollectionChanged);
\r
627 dataTable.Rows.ListChanged -= new ListChangedEventHandler (OnRowCollectionChanged);
\r
630 // These index storing and rowView preservation must be done
\r
631 // before the actual row value is changed; thus we can't use
\r
632 // RowChanging which accepts "already modified" DataRow.
\r
634 private void OnColumnChanged (object sender, DataColumnChangeEventArgs args)
\r
636 //UpdateIndex(true);
\r
639 private void OnRowChanged (object sender, DataRowChangeEventArgs args)
\r
641 int oldIndex,newIndex;
\r
642 oldIndex = newIndex = -1;
\r
643 oldIndex = IndexOf (args.Row);
\r
644 UpdateIndex (true);
\r
645 newIndex = IndexOf (args.Row);
\r
648 if (args.Action == DataRowAction.Add && oldIndex != newIndex)
\r
649 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, newIndex, -1));
\r
651 /* ItemChanged or ItemDeleted */
\r
652 if (args.Action == DataRowAction.Change) {
\r
653 if (oldIndex != -1 && oldIndex == newIndex)
\r
654 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemChanged, newIndex, -1));
\r
655 else if (oldIndex != newIndex) {
\r
657 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, newIndex, oldIndex));
\r
659 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemMoved, newIndex, oldIndex));
\r
663 /* Rollback - ItemAdded or ItemDeleted */
\r
664 if (args.Action == DataRowAction.Rollback) {
\r
665 if (oldIndex < 0 && newIndex > -1)
\r
666 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemAdded, newIndex, -1));
\r
667 else if (oldIndex > -1 && newIndex < 0)
\r
668 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, newIndex, oldIndex));
\r
669 else if (oldIndex != -1 && oldIndex == newIndex)
\r
670 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemChanged, newIndex, -1));
\r
674 private void OnRowDeleted (object sender, DataRowChangeEventArgs args)
\r
677 int newIndex, oldCount;
\r
679 newIndex = IndexOf (args.Row);
\r
680 UpdateIndex (true);
\r
681 /* Fire ListChanged only when the RowFilter is affected */
\r
682 if (oldCount != Count)
\r
683 OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, newIndex, -1));
\r
686 protected virtual void ColumnCollectionChanged (object sender, CollectionChangeEventArgs e)
\r
688 // UpdateIndex() is not invoked here (even if the sort
\r
689 // column is being removed).
\r
691 // PropertyDescriptor Add
\r
692 if (e.Action == CollectionChangeAction.Add)
\r
693 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorAdded, 0, 0));
\r
695 // PropertyDescriptor Removed
\r
696 if (e.Action == CollectionChangeAction.Remove)
\r
697 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorDeleted, 0, 0));
\r
699 // PropertyDescriptor Changed
\r
700 if (e.Action == CollectionChangeAction.Refresh)
\r
701 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged, 0, 0));
\r
704 private void OnConstraintCollectionChanged (object sender, CollectionChangeEventArgs args)
\r
706 // The Sort variable is set to the UniqueConstraint column.
\r
707 // if ApplyDefault Sort is true and Sort is null or is not set Explicitly
\r
709 // FIXME: The interal cache may change as result of change in Constraint collection
\r
710 // one such scenerio is taken care.
\r
711 // There may be more. I dont know what else can be done.
\r
712 /* useDefaultSort is set to false when Sort is set explicitly */
\r
713 if (args.Action == CollectionChangeAction.Add && args.Element is UniqueConstraint) {
\r
714 if (ApplyDefaultSort && useDefaultSort)
\r
715 PopulateDefaultSort ((UniqueConstraint) args.Element);
\r
717 // UpdateIndex() is not invoked here.
\r
720 private void OnRelationCollectionChanged (object sender, CollectionChangeEventArgs args)
\r
722 /* PropertyDescriptor Add */
\r
723 if (args.Action == CollectionChangeAction.Add)
\r
724 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorAdded,0,0));
\r
726 /* PropertyDescriptor Removed */
\r
727 if (args.Action == CollectionChangeAction.Remove)
\r
728 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorDeleted,0,0));
\r
730 /* FIXME: PropertyDescriptor Changed ???*/
\r
731 if (args.Action == CollectionChangeAction.Refresh)
\r
732 OnListChanged (new ListChangedEventArgs (ListChangedType.PropertyDescriptorChanged,0,0));
\r
735 // internal use by Mono
\r
736 protected void Reset ()
\r
738 // TODO: what really happens?
\r
740 rowCache = new DataRowView [0];
\r
742 OnListChanged (new ListChangedEventArgs (ListChangedType.Reset, -1, -1 ));
\r
745 protected void UpdateIndex ()
\r
747 UpdateIndex (false);
\r
750 // This is method is internal to
\r
751 // the Mono implementation of DataView; it
\r
752 // is not to be used from your code.
\r
754 // Update the DataRowView array which is an index cache
\r
755 // into the DataTable's DataRowCollection.
\r
757 // I assume this is what UpdateIndex is used for
\r
758 protected virtual void UpdateIndex (bool force)
\r
764 if (Index == null || force) {
\r
765 sortColumns = DataTable.ParseSortString(Table, Sort, out sortOrder, false);
\r
766 Index = dataTable.GetIndex(sortColumns,sortOrder,RowStateFilter,FilterExpression,true);
\r
768 Index.Key.RowStateFilter = RowStateFilter;
\r
772 int[] records = Index.GetAll ();
\r
774 if (records != null)
\r
775 InitDataRowViewArray (records,Index.Size);
\r
777 rowCache = new DataRowView [0];
\r
780 internal virtual IExpression FilterExpression {
\r
781 get { return rowFilterExpr; }
\r
784 private void InitDataRowViewArray (int [] records, int size)
\r
786 if (_lastAdded != null)
\r
787 rowCache = new DataRowView [size + 1];
\r
789 rowCache = new DataRowView [size];
\r
791 for (int r = 0; r < size; r++)
\r
792 rowCache [r] = new DataRowView (this, Table.RecordCache [records [r]],r);
\r
794 if (_lastAdded != null)
\r
795 rowCache [size] = new DataRowView (this, _lastAdded, size);
\r
798 PropertyDescriptorCollection ITypedList.GetItemProperties (PropertyDescriptor [] listAccessors)
\r
800 if (dataTable == null)
\r
801 return new PropertyDescriptorCollection (new PropertyDescriptor [0]);
\r
803 // FIXME: use listAccessors somehow
\r
804 PropertyDescriptor [] descriptors =
\r
805 new PropertyDescriptor [dataTable.Columns.Count + dataTable.ChildRelations.Count];
\r
808 for (int col = 0; col < dataTable.Columns.Count; col ++) {
\r
809 DataColumn dataColumn = dataTable.Columns[col];
\r
810 DataColumnPropertyDescriptor descriptor;
\r
812 descriptor = new DataColumnPropertyDescriptor (dataColumn.ColumnName, col, null);
\r
813 descriptor.SetComponentType (typeof (System.Data.DataRowView));
\r
814 descriptor.SetPropertyType (dataColumn.DataType);
\r
815 descriptor.SetReadOnly (dataColumn.ReadOnly);
\r
816 descriptor.SetBrowsable (dataColumn.ColumnMapping != MappingType.Hidden);
\r
817 descriptors [d++] = descriptor;
\r
819 for (int rel = 0; rel < dataTable.ChildRelations.Count; rel ++) {
\r
820 DataRelation dataRelation = dataTable.ChildRelations [rel];
\r
821 DataRelationPropertyDescriptor descriptor;
\r
823 descriptor = new DataRelationPropertyDescriptor (dataRelation);
\r
824 descriptors [d++] = descriptor;
\r
827 return new PropertyDescriptorCollection (descriptors);
\r
831 private int IndexOf (DataRow dr)
\r
833 for (int i=0; i < rowCache.Length; i++)
\r
834 if (dr.Equals (rowCache [i].Row))
\r
839 private void PopulateDefaultSort ()
\r
841 sort = string.Empty;
\r
842 foreach (Constraint c in dataTable.Constraints) {
\r
843 if (c is UniqueConstraint) {
\r
844 PopulateDefaultSort ((UniqueConstraint) c);
\r
850 private void PopulateDefaultSort (UniqueConstraint uc)
\r
855 DataColumn [] columns = uc.Columns;
\r
856 if (columns.Length == 0) {
\r
857 sort = String.Empty;
\r
861 StringBuilder builder = new StringBuilder ();
\r
862 builder.Append (columns[0].ColumnName);
\r
863 for (int i = 1; i < columns.Length; i++) {
\r
864 builder.Append (", ");
\r
865 builder.Append (columns [i].ColumnName);
\r
867 sort = builder.ToString ();
\r
870 internal DataView CreateChildView (DataRelation relation, int index)
\r
872 if (relation == null || relation.ParentTable != Table)
\r
873 throw new ArgumentException("The relation is not parented to the table to which this DataView points.");
\r
875 int record = GetRecord (index);
\r
876 object[] keyValues = new object [relation.ParentColumns.Length];
\r
877 for (int i = 0; i < relation.ParentColumns.Length; i++)
\r
878 keyValues [i] = relation.ParentColumns [i] [record];
\r
880 return new RelatedDataView (relation.ChildColumns, keyValues);
\r
883 private int GetRecord (int index)
\r
885 if (index < 0 || index >= Count)
\r
886 throw new IndexOutOfRangeException(String.Format("There is no row at position {0}.", index));
\r
888 return (index == Index.Size) ?
\r
889 _lastAdded.IndexFromVersion (DataRowVersion.Default) :
\r
890 Index.IndexToRecord (index);
\r
893 internal DataRowVersion GetRowVersion (int index)
\r
895 int record = GetRecord (index);
\r
896 return Table.RecordCache [record].VersionFromIndex (record);
\r
900 partial class DataView : ITypedList {
\r
901 string ITypedList.GetListName (PropertyDescriptor [] listAccessors)
\r
903 if (dataTable != null)
\r
904 return dataTable.TableName;
\r
905 return string.Empty;
\r
909 partial class DataView : ICollection {
\r
910 bool ICollection.IsSynchronized {
\r
911 get { return false; }
\r
914 object ICollection.SyncRoot {
\r
915 get { return this; }
\r
919 partial class DataView : IList {
\r
920 bool IList.IsFixedSize {
\r
921 get { return false; }
\r
924 bool IList.IsReadOnly {
\r
925 get { return false; }
\r
928 object IList.this [int recordIndex] {
\r
929 get { return this [recordIndex]; }
\r
931 set { throw new InvalidOperationException (); }
\r
934 int IList.Add (object value)
\r
936 throw new ArgumentException ("Cannot add external objects to this list.");
\r
939 void IList.Clear ()
\r
941 throw new ArgumentException ("Cannot clear this list.");
\r
944 bool IList.Contains (object value)
\r
946 DataRowView drv = value as DataRowView;
\r
950 return drv.DataView == this;
\r
953 int IList.IndexOf (object value)
\r
955 DataRowView drv = value as DataRowView;
\r
956 if (drv != null && drv.DataView == this)
\r
961 void IList.Insert (int index,object value)
\r
963 throw new ArgumentException ("Cannot insert external objects to this list.");
\r
966 void IList.Remove (object value)
\r
968 DataRowView drv = value as DataRowView;
\r
969 if (drv != null && drv.DataView == this)
\r
970 ((IList) this).RemoveAt (drv.Index);
\r
972 throw new ArgumentException ("Cannot remove external objects to this list.");
\r
975 void IList.RemoveAt (int index)
\r
981 partial class DataView : IBindingList {
\r
983 void IBindingList.AddIndex (PropertyDescriptor property)
\r
985 throw new NotImplementedException ();
\r
988 object IBindingList.AddNew ()
\r
990 return this.AddNew ();
\r
993 void IBindingList.ApplySort (PropertyDescriptor property, ListSortDirection direction)
\r
995 if (!(property is DataColumnPropertyDescriptor))
\r
996 throw new ArgumentException ("Dataview accepts only DataColumnPropertyDescriptors", "property");
\r
997 sortProperty = property;
\r
998 string sort = String.Format ("[{0}]" , property.Name);
\r
999 if (direction == ListSortDirection.Descending)
\r
1004 int IBindingList.Find (PropertyDescriptor property, object key)
\r
1006 DataColumn dc = Table.Columns [property.Name];
\r
1007 Index index = Table.FindIndex (new DataColumn [] {dc}, sortOrder, RowStateFilter, FilterExpression);
\r
1008 if (index == null)
\r
1009 index = new Index (new Key (Table, new DataColumn [] {dc}, sortOrder, RowStateFilter, FilterExpression));
\r
1011 return index.FindIndex (new object [] {key});
\r
1015 void IBindingList.RemoveIndex (PropertyDescriptor property)
\r
1017 throw new NotImplementedException ();
\r
1020 void IBindingList.RemoveSort ()
\r
1022 sortProperty = null;
\r
1023 this.Sort = String.Empty;
\r
1026 bool IBindingList.AllowEdit {
\r
1027 get { return AllowEdit; }
\r
1030 bool IBindingList.AllowNew {
\r
1031 get { return AllowNew; }
\r
1034 bool IBindingList.AllowRemove {
\r
1036 get { return AllowDelete; }
\r
1039 bool IBindingList.IsSorted {
\r
1040 get { return (Sort != null && Sort.Length != 0); }
\r
1043 ListSortDirection IBindingList.SortDirection {
\r
1045 if (sortOrder != null && sortOrder.Length > 0)
\r
1046 return sortOrder [0];
\r
1047 return ListSortDirection.Ascending;
\r
1051 PropertyDescriptor IBindingList.SortProperty {
\r
1053 if (sortProperty == null && sortColumns != null && sortColumns.Length > 0) {
\r
1054 // return property from Sort String
\r
1055 PropertyDescriptorCollection properties = ((ITypedList)this).GetItemProperties (null);
\r
1056 return properties.Find (sortColumns [0].ColumnName, false);
\r
1058 return sortProperty;
\r
1062 bool IBindingList.SupportsChangeNotification {
\r
1063 get { return true; }
\r
1066 bool IBindingList.SupportsSearching {
\r
1067 get { return true; }
\r
1070 bool IBindingList.SupportsSorting {
\r
1071 get { return true; }
\r
1076 partial class DataView : IBindingListView {
\r
1077 string IBindingListView.Filter {
\r
1078 get { return ((DataView) this).RowFilter; }
\r
1079 set { ((DataView) this).RowFilter = value; }
\r
1082 ListSortDescriptionCollection IBindingListView.SortDescriptions {
\r
1084 ListSortDescriptionCollection col = new ListSortDescriptionCollection ();
\r
1085 for (int i = 0; i < sortColumns.Length; ++i) {
\r
1086 ListSortDescription ldesc = new ListSortDescription (
\r
1087 new DataColumnPropertyDescriptor (sortColumns [i]),
\r
1089 ((IList) col).Add (ldesc);
\r
1095 bool IBindingListView.SupportsAdvancedSorting {
\r
1096 get { return true; }
\r
1099 bool IBindingListView.SupportsFiltering {
\r
1100 get { return true; }
\r
1104 void IBindingListView.ApplySort (ListSortDescriptionCollection sorts)
\r
1106 StringBuilder sb = new StringBuilder ();
\r
1107 foreach (ListSortDescription ldesc in sorts)
\r
1108 sb.AppendFormat ("[{0}]{1},", ldesc.PropertyDescriptor.Name,
\r
1109 (ldesc.SortDirection == ListSortDirection.Descending ? " DESC" : string.Empty));
\r
1110 this.Sort = sb.ToString (0, sb.Length-1);
\r
1113 void IBindingListView.RemoveFilter ()
\r
1115 ((IBindingListView) this).Filter = string.Empty;
\r
1119 partial class DataView : ISupportInitializeNotification {
\r
1120 private bool dataViewInitialized = true;
\r
1122 [Browsable (false)]
\r
1123 public bool IsInitialized {
\r
1124 get { return dataViewInitialized; }
\r
1127 public event EventHandler Initialized;
\r
1129 partial void DataViewInitialized (bool value)
\r
1131 dataViewInitialized = value;
\r
1133 OnDataViewInitialized (new EventArgs ());
\r
1136 private void OnDataViewInitialized (EventArgs e)
\r
1138 if (null != Initialized)
\r
1139 Initialized (this, e);
\r
1143 partial class DataView {
\r
1144 public virtual bool Equals (DataView dv)
\r
1148 if (!(this.Table == dv.Table && this.Sort == dv.Sort &&
\r
1149 this.RowFilter == dv.RowFilter &&
\r
1150 this.RowStateFilter == dv.RowStateFilter &&
\r
1151 this.AllowEdit == dv.AllowEdit &&
\r
1152 this.AllowNew == dv.AllowNew &&
\r
1153 this.AllowDelete == dv.AllowDelete &&
\r
1154 this.Count == dv.Count))
\r
1157 for (int i = 0; i < Count; ++i)
\r
1158 if (!this [i].Equals (dv [i]))
\r
1162 public DataTable ToTable ()
\r
1164 return this.ToTable (Table.TableName, false, new string[] {});
\r
1167 public DataTable ToTable (string tableName)
\r
1169 return this.ToTable (tableName, false, new string[] {});
\r
1172 public DataTable ToTable (bool isDistinct, params string[] columnNames)
\r
1174 return this.ToTable (Table.TableName, isDistinct, columnNames);
\r
1177 public DataTable ToTable (string tablename, bool isDistinct, params string[] columnNames)
\r
1179 if (columnNames == null)
\r
1180 throw new ArgumentNullException ("columnNames", "'columnNames' argument cannot be null.");
\r
1182 DataTable newTable = new DataTable (tablename);
\r
1184 DataColumn[] columns;
\r
1185 ListSortDirection[] sortDirection = null;
\r
1186 if (columnNames.Length > 0) {
\r
1187 columns = new DataColumn [columnNames.Length];
\r
1188 for (int i=0; i < columnNames.Length; ++i)
\r
1189 columns [i] = Table.Columns [columnNames [i]];
\r
1191 if (sortColumns != null) {
\r
1192 sortDirection = new ListSortDirection [columnNames.Length];
\r
1193 for (int i=0; i < columnNames.Length; ++i) {
\r
1194 sortDirection [i] = ListSortDirection.Ascending;
\r
1195 for (int j = 0; j < sortColumns.Length; ++j) {
\r
1196 if (sortColumns [j] != columns [i])
\r
1198 sortDirection [i] = sortOrder [j];
\r
1203 columns = (DataColumn[]) Table.Columns.ToArray (typeof (DataColumn));
\r
1204 sortDirection = sortOrder;
\r
1207 ArrayList expressionCols = new ArrayList ();
\r
1208 for (int i = 0; i < columns.Length; ++i) {
\r
1209 DataColumn col = columns [i].Clone ();
\r
1210 if (col.Expression != String.Empty) {
\r
1211 col.Expression = string.Empty;
\r
1212 expressionCols.Add (col);
\r
1215 col.ReadOnly = false;
\r
1216 newTable.Columns.Add (col);
\r
1221 // Get the index from index collection of the data table.
\r
1222 Index index = null;
\r
1223 if (sort != string.Empty)
\r
1224 index = Table.GetIndex(sortColumns,sortOrder,RowStateFilter,FilterExpression,true);
\r
1226 index = new Index (new Key(Table, columns, sortDirection, RowStateFilter, rowFilterExpr));
\r
1229 rows = index.GetDistinctRows ();
\r
1231 rows = index.GetAllRows ();
\r
1233 foreach (DataRow row in rows) {
\r
1234 DataRow newRow = newTable.NewNotInitializedRow ();
\r
1235 newTable.Rows.AddInternal (newRow);
\r
1236 newRow.Original = -1;
\r
1237 if (row.HasVersion (DataRowVersion.Current))
\r
1238 newRow.Current = newTable.RecordCache.CopyRecord (Table, row.Current, -1);
\r
1239 else if (row.HasVersion (DataRowVersion.Original))
\r
1240 newRow.Current = newTable.RecordCache.CopyRecord (Table, row.Original, -1);
\r
1242 foreach (DataColumn col in expressionCols)
\r
1243 newRow [col] = row [col.ColumnName];
\r
1244 newRow.Original = -1;
\r