Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Data / System / Data / DataView.cs / 6 / DataView.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //[....] //[....] //----------------------------------------------------------------------------- namespace System.Data { using System; using System.Diagnostics; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Text; ////// [ Designer("Microsoft.VSDesigner.Data.VS.DataViewDesigner, " + AssemblyRef.MicrosoftVSDesigner), Editor("Microsoft.VSDesigner.Data.Design.DataSourceEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing), DefaultProperty("Table"), DefaultEvent("PositionChanged") ] #if WINFSInternalOnly internal #else public #endif class DataView : MarshalByValueComponent, IBindingListView , System.ComponentModel.ITypedList, ISupportInitializeNotification { private DataViewManager dataViewManager; private DataTable table; private bool locked = false; private Index index; private Dictionary/// Represents a databindable, customized view of a ////// for sorting, filtering, searching, editing, and navigation. /// findIndexes; private string sort = ""; /// Allow a user implemented comparision of two DataRow ///User must use correct DataRowVersion in comparison or index corruption will happen private System.Comparison_comparison; /// /// IFilter will allow LinqDataView to wrap private IFilter rowFilter = null; private DataViewRowState recordStates = DataViewRowState.CurrentRows; private bool shouldOpen = true; private bool open = false; private bool allowNew = true; private bool allowEdit = true; private bool allowDelete = true; private bool applyDefaultSort = false; internal DataRow addNewRow; private ListChangedEventArgs addNewMoved; private System.ComponentModel.ListChangedEventHandler onListChanged; private System.EventHandler onInitialized; internal static ListChangedEventArgs ResetEventArgs = new ListChangedEventArgs(ListChangedType.Reset, -1); private DataTable delayedTable = null; private string delayedRowFilter = null; private string delayedSort = null; private DataViewRowState delayedRecordStates = (DataViewRowState)(-1); private bool fInitInProgress = false; private bool fEndInitInProgress = false; ///instead of using a DataExpression /// /// You can't delay create the DataRowView instances since multiple thread read access is valid /// and each thread must obtain the same DataRowView instance and we want to avoid (inter)locking. /// ////// In V1.1, the DataRowView[] was recreated after every change. Each DataRowView was bound to a DataRow. /// In V2.0 Whidbey, the DataRowView retained but bound to an index instead of DataRow, allowing the DataRow to vary. /// In V2.0 Orcas, the DataRowView retained and bound to a DataRow, allowing the index to vary. /// private DictionaryrowViewCache = new Dictionary (DataRowReferenceComparer.Default); /// /// This collection allows expression maintaince to (add / remove) from the index when it really should be a (change / move). /// private readonly DictionaryrowViewBuffer = new Dictionary (DataRowReferenceComparer.Default); private sealed class DataRowReferenceComparer : IEqualityComparer { internal static readonly DataRowReferenceComparer Default = new DataRowReferenceComparer(); private DataRowReferenceComparer() { } public bool Equals(DataRow x, DataRow y) { return ((object)x == (object)y); } public int GetHashCode(DataRow obj) { return obj.ObjectID; } } DataViewListener dvListener = null; private static int _objectTypeCount; // Bid counter private readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); internal DataView(DataTable table, bool locked) { GC.SuppressFinalize(this); Bid.Trace(" %d#, table=%d, locked=%d{bool}\n", ObjectID, (table != null) ? table.ObjectID : 0, locked); this.dvListener = new DataViewListener(this); this.locked = locked; this.table = table; dvListener.RegisterMetaDataEvents(this.table); } /// /// public DataView() : this(null) { SetIndex2("", DataViewRowState.CurrentRows, null, true); } ///Initializes a new instance of the ///class. /// public DataView(DataTable table) : this(table, false) { SetIndex2("", DataViewRowState.CurrentRows, null, true); } ///Initializes a new instance of the ///class with the /// specified . /// public DataView(DataTable table, String RowFilter, string Sort, DataViewRowState RowState) { GC.SuppressFinalize(this); Bid.Trace("Initializes a new instance of the ///class with the /// specified . %d#, table=%d, RowFilter='%ls', Sort='%ls', RowState=%d{ds.DataViewRowState}\n", ObjectID, (table != null) ? table.ObjectID : 0, RowFilter, Sort, (int)RowState); if (table == null) throw ExceptionBuilder.CanNotUse(); this.dvListener = new DataViewListener(this); this.locked = false; this.table = table; dvListener.RegisterMetaDataEvents(this.table); if ((((int)RowState) & ((int)~(DataViewRowState.CurrentRows | DataViewRowState.OriginalRows))) != 0) { throw ExceptionBuilder.RecordStateRange(); } else if (( ((int)RowState) & ((int)DataViewRowState.ModifiedOriginal) ) != 0 && ( ((int)RowState) & ((int)DataViewRowState.ModifiedCurrent) ) != 0 ) { throw ExceptionBuilder.SetRowStateFilter(); } if (Sort == null) Sort = ""; if (RowFilter == null) RowFilter = ""; DataExpression newFilter = new DataExpression(table, RowFilter); SetIndex(Sort, RowState, newFilter); } /// /// Allow construction of DataView with ///and /// This is a copy of the other DataView ctor and needs to be kept in sync internal DataView(DataTable table, System.Predicatepredicate, System.Comparison comparison, DataViewRowState RowState) { GC.SuppressFinalize(this); Bid.Trace(" %d#, table=%d, RowState=%d{ds.DataViewRowState}\n", ObjectID, (table != null) ? table.ObjectID : 0, (int)RowState); if (table == null) throw ExceptionBuilder.CanNotUse(); this.dvListener = new DataViewListener(this); this.locked = false; this.table = table; dvListener.RegisterMetaDataEvents(this.table); if ((((int)RowState) & ((int)~(DataViewRowState.CurrentRows | DataViewRowState.OriginalRows))) != 0) { throw ExceptionBuilder.RecordStateRange(); } else if (( ((int)RowState) & ((int)DataViewRowState.ModifiedOriginal) ) != 0 && ( ((int)RowState) & ((int)DataViewRowState.ModifiedCurrent) ) != 0 ) { throw ExceptionBuilder.SetRowStateFilter(); } _comparison = comparison; SetIndex2("", RowState, ((null != predicate) ? new RowPredicateFilter(predicate) : null), true); } /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(true), ResDescriptionAttribute(Res.DataViewAllowDeleteDescr) ] public bool AllowDelete { get { return allowDelete; } set { if (allowDelete != value) { allowDelete = value; OnListChanged(ResetEventArgs); } } } ////// Sets or gets a value indicating whether deletes are /// allowed. /// ////// [ RefreshProperties(RefreshProperties.All), ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(false), ResDescriptionAttribute(Res.DataViewApplyDefaultSortDescr) ] public bool ApplyDefaultSort { get { return applyDefaultSort; } set { Bid.Trace("Gets or sets a value indicating whether to use the default sort. ///%d#, %d{bool}\n", ObjectID, value); if (applyDefaultSort != value) { _comparison = null; // clear the delegate to allow the Sort string to be effective applyDefaultSort = value; UpdateIndex(true); OnListChanged(ResetEventArgs); } } } /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(true), ResDescriptionAttribute(Res.DataViewAllowEditDescr) ] public bool AllowEdit { get { return allowEdit; } set { if (allowEdit != value) { allowEdit = value; OnListChanged(ResetEventArgs); } } } ////// Gets or sets a value indicating whether edits are allowed. /// ////// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(true), ResDescriptionAttribute(Res.DataViewAllowNewDescr) ] public bool AllowNew { get { return allowNew; } set { if (allowNew != value) { allowNew = value; OnListChanged(ResetEventArgs); } } } ////// Gets or sets a value indicating whether the new rows can /// be added using the ////// method. /// /// Gets the number of records in the [Browsable(false), ResDescriptionAttribute(Res.DataViewCountDescr)] public int Count { get { Debug.Assert(rowViewCache.Count == CountFromIndex, "DataView.Count mismatch"); return rowViewCache.Count; } } private int CountFromIndex { get { return (((null != index) ? index.RecordCount : 0) + ((null != addNewRow) ? 1 : 0)); } } ///. /// /// [Browsable(false), ResDescriptionAttribute(Res.DataViewDataViewManagerDescr)] public DataViewManager DataViewManager { get { return dataViewManager; } } [Browsable(false)] public bool IsInitialized { get { return !fInitInProgress; } } ////// Gets the ///associated with this . /// /// [Browsable(false), ResDescriptionAttribute(Res.DataViewIsOpenDescr)] protected bool IsOpen { get { return open; } } bool ICollection.IsSynchronized { get { return false; } } ////// Gets a value indicating whether the data source is currently open and /// projecting views of data on the ///. /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(""), ResDescriptionAttribute(Res.DataViewRowFilterDescr) ] public virtual string RowFilter { get { // ACCESSOR: virtual was missing from this get DataExpression expression = (rowFilter as DataExpression); return(expression == null ? "" : expression.Expression); // } set { if (value == null) value = ""; Bid.Trace("/// Gets or sets the expression used to filter which rows are viewed in the /// ///. /// %d#, '%ls'\n", ObjectID, value); if (fInitInProgress) { delayedRowFilter = value; return; } CultureInfo locale = (table != null ? table.Locale : CultureInfo.CurrentCulture); if (null == rowFilter || (String.Compare(RowFilter,value,false,locale) != 0)) { DataExpression newFilter = new DataExpression(table, value); SetIndex(sort, recordStates, newFilter); } } } #region RowPredicateFilter /// /// The predicate delegate that will determine if a DataRow should be contained within the view. /// This RowPredicate property is mutually exclusive with the RowFilter property. /// internal System.PredicateRowPredicate { get { RowPredicateFilter filter = (GetFilter() as RowPredicateFilter); return ((null != filter) ? filter.PredicateFilter : null); } set { if (!Object.ReferenceEquals(RowPredicate, value)) { SetIndex(Sort, RowStateFilter, ((null != value) ? new RowPredicateFilter(value) : null)); } } } /// private sealed class RowPredicateFilter : System.Data.IFilter { internal readonly System.Predicate PredicateFilter; /// internal RowPredicateFilter(System.Predicate predicate) { Debug.Assert(null != predicate, "null predicate"); PredicateFilter = predicate; } /// bool IFilter.Invoke(DataRow row, DataRowVersion version) { Debug.Assert(DataRowVersion.Default != version, "not expecting Default"); Debug.Assert(DataRowVersion.Proposed != version, "not expecting Proposed"); return PredicateFilter(row); } } #endregion /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(DataViewRowState.CurrentRows), ResDescriptionAttribute(Res.DataViewRowStateFilterDescr) ] public DataViewRowState RowStateFilter { get { return recordStates; } set { Bid.Trace("Gets or sets the row state filter used in the ///. %d#, %d{ds.DataViewRowState}\n", ObjectID, (int)value); if (fInitInProgress) { delayedRecordStates = value; return; } if ((((int)value) & ((int)~(DataViewRowState.CurrentRows | DataViewRowState.OriginalRows))) != 0) throw ExceptionBuilder.RecordStateRange(); else if (( ((int)value) & ((int)DataViewRowState.ModifiedOriginal) ) != 0 && ( ((int)value) & ((int)DataViewRowState.ModifiedCurrent) ) != 0 ) throw ExceptionBuilder.SetRowStateFilter(); if (recordStates != value) { SetIndex(sort, value, rowFilter); } } } /// /// [ ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(""), ResDescriptionAttribute(Res.DataViewSortDescr) ] public string Sort { get { if (sort.Length == 0 && applyDefaultSort && table != null && table._primaryIndex.Length > 0) { return table.FormatSortString(table._primaryIndex); } else { return sort; } } set { if (value == null) { value = ""; } Bid.Trace("/// Gets /// or sets the sort column or columns, and sort order for the table. /// ///%d#, '%ls'\n", ObjectID, value); if (fInitInProgress) { delayedSort = value; return; } CultureInfo locale = (table != null ? table.Locale : CultureInfo.CurrentCulture); if (String.Compare(sort, value, false, locale) != 0 || (null != _comparison)) { CheckSort(value); _comparison = null; // clear the delegate to allow the Sort string to be effective SetIndex(value, recordStates, rowFilter); } } } /// Allow a user implemented comparision of two DataRow ///User must use correct DataRowVersion in comparison or index corruption will happen internal System.ComparisonSortComparison { get { return _comparison; } set { Bid.Trace(" %d#\n", ObjectID); if (!Object.ReferenceEquals(_comparison, value)) { _comparison = value; SetIndex("", recordStates, rowFilter); } } } /// /// private void ResetSort() { // this is dead code, no one is calling it sort = ""; SetIndex(sort, recordStates, rowFilter); } ////// Resets the ///property to its default state. /// /// private bool ShouldSerializeSort() { return(sort != null); } object ICollection.SyncRoot { get { return this; } } ////// Indicates whether the ///property should be persisted. /// /// [ TypeConverterAttribute(typeof(DataTableTypeConverter)), ResCategoryAttribute(Res.DataCategory_Data), DefaultValue(null), RefreshProperties(RefreshProperties.All), ResDescriptionAttribute(Res.DataViewTableDescr) ] public DataTable Table { get { return table; } set { Bid.Trace("/// Gets or sets the source ///. /// %d#, %d\n", ObjectID, (value != null) ? value.ObjectID : 0); if (fInitInProgress && value != null) { delayedTable = value; return; } if (locked) throw ExceptionBuilder.SetTable(); if (dataViewManager != null) throw ExceptionBuilder.CanNotSetTable(); if (value != null && value.TableName.Length == 0) throw ExceptionBuilder.CanNotBindTable(); if (table != value) { dvListener.UnregisterMetaDataEvents(); table = value; if (table != null) { dvListener.RegisterMetaDataEvents(this.table); } // SetIndex2("", DataViewRowState.CurrentRows, null, false); if (table != null) { OnListChanged(new ListChangedEventArgs(ListChangedType.PropertyDescriptorChanged, new DataTablePropertyDescriptor(table))); } // index was updated without firing the reset, fire it now OnListChanged(ResetEventArgs); } } } object IList.this[int recordIndex] { get { return this[recordIndex]; } set { throw ExceptionBuilder.SetIListObject(); } } /// /// ////// Gets a row of data from a specified table. /// ///public DataRowView this[int recordIndex] { get { return GetRowView(GetRow(recordIndex)); } } /// /// Adds a new row of data to view. /// ////// Only one new row of data allowed at a time, so previous new row will be added to row collection. /// Unsupported pattern: dataTable.Rows.Add(dataView.AddNew().Row) /// public virtual DataRowView AddNew() { IntPtr hscp; Bid.ScopeEnter(out hscp, "%d#\n", ObjectID); try { CheckOpen(); if (!AllowNew) throw ExceptionBuilder.AddNewNotAllowNull(); if (addNewRow != null) { rowViewCache[addNewRow].EndEdit(); } Debug.Assert(null == addNewRow, "AddNew addNewRow is not null"); addNewRow = table.NewRow(); DataRowView drv = new DataRowView(this, addNewRow); rowViewCache.Add(addNewRow, drv); OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOf(drv))); return drv; } finally{ Bid.ScopeLeave(ref hscp); } } public void BeginInit() { fInitInProgress = true; } public void EndInit() { if (delayedTable != null && this.delayedTable.fInitInProgress) { this.delayedTable.delayedViews.Add(this); return; } fInitInProgress = false; fEndInitInProgress = true; if (delayedTable != null) { Table = delayedTable; delayedTable = null; } if (delayedSort != null) { Sort = delayedSort; delayedSort = null; } if (delayedRowFilter != null) { RowFilter = delayedRowFilter; delayedRowFilter = null; } if (delayedRecordStates != (DataViewRowState)(-1)) { RowStateFilter = delayedRecordStates; delayedRecordStates = (DataViewRowState)(-1); } fEndInitInProgress = false; SetIndex(Sort, RowStateFilter, rowFilter); OnInitialized(); } private void CheckOpen() { if (!IsOpen) throw ExceptionBuilder.NotOpen(); } private void CheckSort(string sort) { if (table == null) throw ExceptionBuilder.CanNotUse(); if (sort.Length == 0) return; table.ParseSortString(sort); } /// /// protected void Close() { shouldOpen = false; UpdateIndex(); dvListener.UnregisterMetaDataEvents(); } public void CopyTo(Array array, int index) { if (null != this.index) { RBTree/// Closes the ////// . /// .RBTreeEnumerator iterator = this.index.GetEnumerator(0); while (iterator.MoveNext()) { array.SetValue(GetRowView(iterator.Current), index); checked { index++; } } } if (null != addNewRow) { array.SetValue(rowViewCache[addNewRow], index); } } private void CopyTo(DataRowView[] array, int index) { if (null != this.index) { RBTree .RBTreeEnumerator iterator = this.index.GetEnumerator(0); while (iterator.MoveNext()) { array[index] = GetRowView(iterator.Current); checked { index++; } } } if (null != addNewRow) { array[index] = rowViewCache[addNewRow]; } } /// /// public void Delete(int index) { Delete(GetRow(index)); } internal void Delete(DataRow row) { if (null != row) { IntPtr hscp; Bid.ScopeEnter(out hscp, "Deletes a row at the specified index. ///%d#, row=%d#", ObjectID, row.ObjectID); try { CheckOpen(); if (row == addNewRow) { FinishAddNew(false); return; } if (!AllowDelete) { throw ExceptionBuilder.CanNotDelete(); } row.Delete(); } finally { Bid.ScopeLeave(ref hscp); } } } protected override void Dispose(bool disposing) { if (disposing) { Close(); } base.Dispose(disposing); } /// /// public int Find(object key) { return FindByKey(key); } ////// Finds a row in the ///by the specified primary key /// value. /// Find index of a DataRowView instance that matches the specified primary key value. internal virtual int FindByKey(object key) { return index.FindRecordByKey(key); } ////// public int Find(object[] key) { return FindByKey(key); } ////// Finds a row in the ///by the specified primary key values. /// Find index of a DataRowView instance that matches the specified primary key values. internal virtual int FindByKey(object[] key) { return index.FindRecordByKey(key); } ////// public DataRowView[] FindRows(object key) { return FindRowsByKey(new object[] {key}); } ////// Finds a row in the ///by the specified primary key /// value. /// /// public DataRowView[] FindRows(object[] key) { return FindRowsByKey(key); } ////// Finds a row in the ///by the specified primary key values. /// Find DataRowView instances that match the specified primary key values. internal virtual DataRowView[] FindRowsByKey(object[] key) { IntPtr hscp; Bid.ScopeEnter(out hscp, "%d#\n", ObjectID); try { Range range = index.FindRecords(key); return GetDataRowViewFromRange(range); } finally{ Bid.ScopeLeave(ref hscp); } } /// This method exists for LinqDataView to keep a level of abstraction away from the RBTree internal Range FindRecords(Index.ComparisonBySelector comparison, TKey key) where TRow:DataRow { return this.index.FindRecords(comparison, key); } /// Convert a Range into a DataRowView[]. internal DataRowView[] GetDataRowViewFromRange(Range range) { if (range.IsNull) { return new DataRowView[0]; } DataRowView[] rows = new DataRowView[range.Count]; for (int i=0; i%d#, success=%d{bool}\n", ObjectID, success); DataRow newRow = addNewRow; if (success) { if (DataRowState.Detached == newRow.RowState) { // MaintainDataView will translate the ItemAdded from the RowCollection into // into either an ItemMoved or no event, since it didn't change position. // also possible it's added to the RowCollection but filtered out of the view. table.Rows.Add(newRow); } else { // this means that the record was added to the table by different means and not part of view newRow.EndEdit(); } } if (newRow == addNewRow) { // this means that the record did not get to the view bool flag = rowViewCache.Remove(addNewRow); Debug.Assert(flag, "didn't remove addNewRow"); addNewRow = null; if (!success) { newRow.CancelEdit(); } OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, Count)); } } /// /// public IEnumerator GetEnumerator() { // V1.1 compatability: returning List/// Gets an enumerator for this ///. /// .GetEnumerator() from RowViewCache // prevents users from changing data without invalidating the enumerator // aka don't 'return this.RowViewCache.GetEnumerator()' DataRowView[] temp = new DataRowView[this.Count]; this.CopyTo(temp, 0); return temp.GetEnumerator(); } #region IList bool IList.IsReadOnly { get { return false; } } bool IList.IsFixedSize { get { return false; } } int IList.Add(object value) { if (value == null) { // null is default value, so we AddNew. AddNew(); return Count - 1; } throw ExceptionBuilder.AddExternalObject(); } void IList.Clear() { throw ExceptionBuilder.CanNotClear(); } bool IList.Contains(object value) { return (0 <= IndexOf(value as DataRowView)); } int IList.IndexOf(object value) { return IndexOf(value as DataRowView); } /// Return positional index of a ///in this DataView Behavioral change: will now return -1 once a DataRowView becomes detached. internal int IndexOf(DataRowView rowview) { if (null != rowview) { if (Object.ReferenceEquals(addNewRow, rowview.Row)) { return Count - 1; } if ((null != index) && (DataRowState.Detached != rowview.Row.RowState)) { DataRowView cached; // verify the DataRowView is one we currently track - not something previously detached if (rowViewCache.TryGetValue(rowview.Row, out cached) && ((object)cached == (object)rowview)) { return IndexOfDataRowView(rowview); } } } return -1; } private int IndexOfDataRowView(DataRowView rowview) { return index.GetIndex(rowview.GetRecord()); } void IList.Insert(int index, object value) { throw ExceptionBuilder.InsertExternalObject(); } void IList.Remove(object value) { int index = IndexOf(value as DataRowView); if (0 <= index) { // must delegate to IList.RemoveAt ((IList)this).RemoveAt(index); } else { throw ExceptionBuilder.RemoveExternalObject(); } } void IList.RemoveAt(int index) { Delete(index); } internal Index GetFindIndex(string column, bool keepIndex) { if (findIndexes == null) { findIndexes = new Dictionary(); } Index findIndex; if (findIndexes.TryGetValue(column, out findIndex)) { if (!keepIndex) { findIndexes.Remove(column); findIndex.RemoveRef(); if (findIndex.RefCount == 1) { // if we have created it and we are removing it, refCount is (1) findIndex.RemoveRef(); // if we are reusing the index created by others, refcount is (2) } } } else { if (keepIndex) { findIndex = table.GetIndex(column, recordStates, GetFilter()); findIndexes[column] = findIndex; findIndex.AddRef(); } } return findIndex; } #endregion #region IBindingList implementation bool IBindingList.AllowNew { get { return AllowNew; } } object IBindingList.AddNew() { return AddNew(); } bool IBindingList.AllowEdit { get { return AllowEdit; } } bool IBindingList.AllowRemove { get { return AllowDelete; } } bool IBindingList.SupportsChangeNotification { get { return true; } } bool IBindingList.SupportsSearching { get { return true; } } bool IBindingList.SupportsSorting { get { return true; } } bool IBindingList.IsSorted { get { return this.Sort.Length != 0; } } PropertyDescriptor IBindingList.SortProperty { get { return GetSortProperty(); } } internal PropertyDescriptor GetSortProperty() { if (table != null && index != null && index.IndexFields.Length == 1) { return new DataColumnPropertyDescriptor(index.IndexFields[0].Column); } return null; } ListSortDirection IBindingList.SortDirection { get { if (index.IndexFields.Length == 1 && index.IndexFields[0].IsDescending) { return ListSortDirection.Descending; } return ListSortDirection.Ascending; } } #endregion #region ListChanged & Initialized events /// /// [ResCategoryAttribute(Res.DataCategory_Data), ResDescriptionAttribute(Res.DataViewListChangedDescr)] public event System.ComponentModel.ListChangedEventHandler ListChanged { add { Bid.Trace("/// Occurs when the list managed by the ///changes. /// %d#\n", ObjectID); onListChanged += value; } remove { Bid.Trace(" %d#\n", ObjectID); onListChanged -= value; } } [ ResCategoryAttribute(Res.DataCategory_Action), ResDescriptionAttribute(Res.DataSetInitializedDescr) ] public event System.EventHandler Initialized { add { onInitialized += value; } remove { onInitialized -= value; } } #endregion #region IBindingList implementation void IBindingList.AddIndex(PropertyDescriptor property) { GetFindIndex(property.Name, /*keepIndex:*/true); } void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) { this.Sort = CreateSortString(property, direction); } int IBindingList.Find(PropertyDescriptor property, object key) { // NOTE: this function had keepIndex previosely if (property != null) { bool created = false; Index findIndex = null; try { if ((null == findIndexes) || !findIndexes.TryGetValue(property.Name, out findIndex)) { created = true; findIndex = table.GetIndex(property.Name, recordStates, GetFilter()); findIndex.AddRef(); } Range recordRange = findIndex.FindRecords(key); if (!recordRange.IsNull) { // check to see if key is equal return index.GetIndex(findIndex.GetRecord(recordRange.Min)); } } finally { if (created && (null != findIndex)) { findIndex.RemoveRef(); if (findIndex.RefCount == 1) { // if we have created it and we are removing it, refCount is (1) findIndex.RemoveRef(); // if we are reusing the index created by others, refcount is (2) } } } } return -1; } void IBindingList.RemoveIndex(PropertyDescriptor property) { // Ups: If we don't have index yet we will create it before destroing; Fix this later GetFindIndex(property.Name, /*keepIndex:*/false); } void IBindingList.RemoveSort() { Bid.Trace(" %d#\n", ObjectID); this.Sort = string.Empty; } #endregion #region Additional method and properties for new interface IBindingListView void IBindingListView.ApplySort(ListSortDescriptionCollection sorts) { if (sorts == null) throw ExceptionBuilder.ArgumentNull("sorts"); StringBuilder sortString = new StringBuilder(); bool addCommaToString = false; foreach(ListSortDescription sort in sorts) { if (sort == null) throw ExceptionBuilder.ArgumentContainsNull("sorts"); PropertyDescriptor property = sort.PropertyDescriptor; if (property == null) throw ExceptionBuilder.ArgumentNull("PropertyDescriptor"); if (!this.table.Columns.Contains(property.Name)) { // just check if column does not exist, we will handle duplicate column in Sort throw ExceptionBuilder.ColumnToSortIsOutOfRange(property.Name); } ListSortDirection direction = sort.SortDirection; if (addCommaToString) // (sortStr.Length != 0) sortString.Append(','); sortString.Append(CreateSortString(property, direction)); if (!addCommaToString) addCommaToString = true; } this.Sort = sortString.ToString(); // what if we dont have any valid sort criteira? we would reset the sort } private string CreateSortString(PropertyDescriptor property, ListSortDirection direction) { Debug.Assert (property != null, "property is null"); StringBuilder resultString = new StringBuilder(); resultString.Append('['); resultString.Append(property.Name); resultString.Append(']'); if (ListSortDirection.Descending == direction) { resultString.Append(" DESC"); } return resultString.ToString(); } void IBindingListView.RemoveFilter() { Bid.Trace(" %d#\n", ObjectID); this.RowFilter = ""; } string IBindingListView.Filter { get { return this.RowFilter; } set { this.RowFilter = value; } } ListSortDescriptionCollection IBindingListView.SortDescriptions { get { return GetSortDescriptions(); } } internal ListSortDescriptionCollection GetSortDescriptions() { ListSortDescription[] sortDescArray = new ListSortDescription[0]; if (table != null && index != null && index.IndexFields.Length > 0) { sortDescArray = new ListSortDescription[index.IndexFields.Length]; for(int i = 0; i < index.IndexFields.Length; i++ ) { DataColumnPropertyDescriptor columnProperty = new DataColumnPropertyDescriptor(index.IndexFields[i].Column); if (index.IndexFields[i].IsDescending) { sortDescArray[i] = new ListSortDescription(columnProperty, ListSortDirection.Descending); } else { sortDescArray[i] = new ListSortDescription(columnProperty, ListSortDirection.Ascending); } } } return new ListSortDescriptionCollection(sortDescArray); } bool IBindingListView.SupportsAdvancedSorting { get { return true; } } bool IBindingListView.SupportsFiltering { get { return true; } } #endregion #region ITypedList string System.ComponentModel.ITypedList.GetListName(PropertyDescriptor[] listAccessors) { if(table != null) { if (listAccessors == null || listAccessors.Length == 0) { return table.TableName; } else { DataSet dataSet = table.DataSet; if (dataSet != null) { DataTable foundTable = dataSet.FindTable(table, listAccessors, 0); if (foundTable != null) { return foundTable.TableName; } } } } return String.Empty; } PropertyDescriptorCollection System.ComponentModel.ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) { if (table != null) { if (listAccessors == null || listAccessors.Length == 0) { return table.GetPropertyDescriptorCollection(null); } else { DataSet dataSet = table.DataSet; if (dataSet == null) return new PropertyDescriptorCollection(null); DataTable foundTable = dataSet.FindTable(table, listAccessors, 0); if (foundTable != null) { return foundTable.GetPropertyDescriptorCollection(null); } } } return new PropertyDescriptorCollection(null); } #endregion /// /// internal virtual IFilter GetFilter() { return rowFilter; } private int GetRecord(int recordIndex) { if (unchecked((uint)Count <= (uint)recordIndex)) throw ExceptionBuilder.RowOutOfRange(recordIndex); if (recordIndex == index.RecordCount) return addNewRow.GetDefaultRecord(); return index.GetRecord(recordIndex); } ////// Gets the filter for the ///. /// internal DataRow GetRow(int index) { int count = Count; if (unchecked((uint)count <= (uint)index)) { throw ExceptionBuilder.GetElementIndex(index); } if ((index == (count - 1)) && (addNewRow != null)) { // if we could rely on tempRecord being registered with recordManager // then this special case code would go away return addNewRow; } return table.recordManager[GetRecord(index)]; } private DataRowView GetRowView(int record) { return GetRowView(table.recordManager[record]); } private DataRowView GetRowView(DataRow dr) { return rowViewCache[dr]; } protected virtual void IndexListChanged(object sender, ListChangedEventArgs e) { if (ListChangedType.Reset != e.ListChangedType) { OnListChanged(e); } if (addNewRow != null && index.RecordCount == 0) { // [....] : 83032 Clear the newly added row as the underlying index is reset. FinishAddNew(false); } if (ListChangedType.Reset == e.ListChangedType) { OnListChanged(e); } } internal void IndexListChangedInternal(ListChangedEventArgs e) { rowViewBuffer.Clear(); if ((ListChangedType.ItemAdded == e.ListChangedType) && (null != addNewMoved)) { if (addNewMoved.NewIndex == addNewMoved.OldIndex) { // ItemAdded for addNewRow which didn't change position // RowStateChange only triggers RowChanged, not ListChanged } else { // translate the ItemAdded into ItemMoved for addNewRow adding into sorted collection ListChangedEventArgs f = addNewMoved; addNewMoved = null; IndexListChanged(this, f); } } // the ItemAdded has to fire twice for AddNewRow (public IBindingList API documentation) IndexListChanged(this, e); } internal void MaintainDataView(ListChangedType changedType, DataRow row, bool trackAddRemove) { DataRowView buffer = null; switch (changedType) { case ListChangedType.ItemAdded: Debug.Assert(null != row, "MaintainDataView.ItemAdded with null DataRow"); if (trackAddRemove) { if (rowViewBuffer.TryGetValue(row, out buffer)) { // help turn expression add/remove into a changed/move bool flag = rowViewBuffer.Remove(row); Debug.Assert(flag, "row actually removed"); } } if (row == addNewRow) { // DataView.AddNew().Row was added to DataRowCollection int index = IndexOfDataRowView(rowViewCache[addNewRow]); Debug.Assert(0 <= index, "ItemAdded was actually deleted"); addNewRow = null; addNewMoved = new ListChangedEventArgs(ListChangedType.ItemMoved, index, Count - 1); } else if (!rowViewCache.ContainsKey(row)) { rowViewCache.Add(row, buffer ?? new DataRowView(this, row)); } else { Debug.Assert(false, "ItemAdded DataRow already in view"); } break; case ListChangedType.ItemDeleted: Debug.Assert(null != row, "MaintainDataView.ItemDeleted with null DataRow"); Debug.Assert(row != addNewRow, "addNewRow being deleted"); if (trackAddRemove) { // help turn expression add/remove into a changed/move rowViewCache.TryGetValue(row, out buffer); if (null != buffer) { rowViewBuffer.Add(row, buffer); } else { Debug.Assert(false, "ItemDeleted DataRow not in view tracking"); } } if (!rowViewCache.Remove(row)) { Debug.Assert(false, "ItemDeleted DataRow not in view"); } break; case ListChangedType.Reset: Debug.Assert(null == row, "MaintainDataView.Reset with non-null DataRow"); ResetRowViewCache(); break; case ListChangedType.ItemChanged: case ListChangedType.ItemMoved: break; case ListChangedType.PropertyDescriptorAdded: case ListChangedType.PropertyDescriptorChanged: case ListChangedType.PropertyDescriptorDeleted: Debug.Assert(false, "unexpected"); break; } } /// /// protected virtual void OnListChanged(ListChangedEventArgs e) { Bid.Trace("/// Raises the ///event. /// %d#, ListChangedType=%d{ListChangedType}\n", ObjectID, (int)e.ListChangedType); try { DataColumn col = null; string propertyName = null; switch (e.ListChangedType) { case ListChangedType.ItemChanged: // ItemChanged - a column value changed (0 <= e.OldIndex) // ItemChanged - a DataRow.RowError changed (-1 == e.OldIndex) case ListChangedType.ItemMoved: // ItemMoved - a column value affecting sort order changed // ItemMoved - a state change in equivalent fields Debug.Assert(((ListChangedType.ItemChanged == e.ListChangedType) && ((e.NewIndex == e.OldIndex) || (-1 == e.OldIndex))) || (ListChangedType.ItemMoved == e.ListChangedType && (e.NewIndex != e.OldIndex) && (0 <= e.OldIndex)), "unexpected ItemChanged|ItemMoved"); Debug.Assert(0 <= e.NewIndex, "negative NewIndex"); if (0 <= e.NewIndex) { DataRow dr = GetRow(e.NewIndex); if (dr.HasPropertyChanged) { col = dr.LastChangedColumn; propertyName = (null != col) ? col.ColumnName : String.Empty; } dr.ResetLastChangedColumn(); } break; case ListChangedType.ItemAdded: case ListChangedType.ItemDeleted: case ListChangedType.PropertyDescriptorAdded: case ListChangedType.PropertyDescriptorChanged: case ListChangedType.PropertyDescriptorDeleted: case ListChangedType.Reset: break; } if (onListChanged != null) { if ((col != null) && (e.NewIndex == e.OldIndex)) { ListChangedEventArgs newEventArg = new ListChangedEventArgs(e.ListChangedType, e.NewIndex, new DataColumnPropertyDescriptor(col)); onListChanged(this, newEventArg); } else { onListChanged(this, e); } } if (null != propertyName) { // empty string if more than 1 column changed this[e.NewIndex].RaisePropertyChangedEvent(propertyName); } } catch (Exception f) { // if (!Common.ADP.IsCatchableExceptionType(f)) { throw; } ExceptionBuilder.TraceExceptionWithoutRethrow(f); // ignore the exception } } private void OnInitialized() { if (onInitialized != null) { onInitialized(this, EventArgs.Empty); } } /// /// protected void Open() { shouldOpen = true; UpdateIndex(); dvListener.RegisterMetaDataEvents(this.table); } ////// Opens a ///. /// /// protected void Reset() { if (IsOpen) { index.Reset(); } } internal void ResetRowViewCache() { Dictionary[To be supplied.] ///rvc = new Dictionary (CountFromIndex, DataRowReferenceComparer.Default); DataRowView drv; if (null != index) { // RBTree .RBTreeEnumerator iterator = index.GetEnumerator(0); while (iterator.MoveNext()) { DataRow row = table.recordManager[iterator.Current]; if (!rowViewCache.TryGetValue(row, out drv)) { drv = new DataRowView(this, row); } rvc.Add(row, drv); } } if (null != addNewRow) { rowViewCache.TryGetValue(addNewRow, out drv); Debug.Assert(null != drv, "didn't contain addNewRow"); rvc.Add(addNewRow, drv); } Debug.Assert(rvc.Count == CountFromIndex, "didn't add expected count"); this.rowViewCache = rvc; } internal void SetDataViewManager(DataViewManager dataViewManager) { if (this.table == null) throw ExceptionBuilder.CanNotUse(); if (this.dataViewManager != dataViewManager) { if (dataViewManager != null) dataViewManager.nViews--; this.dataViewManager = dataViewManager; if (dataViewManager != null) { dataViewManager.nViews++; DataViewSetting dataViewSetting = dataViewManager.DataViewSettings[table]; try { // [....]: check that we will not do unnesasary operation here if dataViewSetting.Sort == this.Sort ... applyDefaultSort = dataViewSetting.ApplyDefaultSort; DataExpression newFilter = new DataExpression(table, dataViewSetting.RowFilter); SetIndex(dataViewSetting.Sort, dataViewSetting.RowStateFilter, newFilter); } catch (Exception e) { // if (!Common.ADP.IsCatchableExceptionType(e)) { throw; } ExceptionBuilder.TraceExceptionWithoutRethrow(e); // ignore the exception } locked = true; } else { SetIndex("", DataViewRowState.CurrentRows, null); } } } internal virtual void SetIndex(string newSort, DataViewRowState newRowStates, IFilter newRowFilter) { SetIndex2(newSort, newRowStates, newRowFilter, true); } internal void SetIndex2(string newSort, DataViewRowState newRowStates, IFilter newRowFilter, bool fireEvent) { Bid.Trace(" %d#, newSort='%ls', newRowStates=%d{ds.DataViewRowState}\n", ObjectID, newSort, (int)newRowStates); this.sort = newSort; this.recordStates = newRowStates; this.rowFilter = newRowFilter; Debug.Assert((0 == (DataViewRowState.ModifiedCurrent & newRowStates)) || (0 == (DataViewRowState.ModifiedOriginal & newRowStates)), "asking DataViewRowState for both Original & Current records"); if (fEndInitInProgress) return; if (fireEvent) { // old code path for virtual UpdateIndex UpdateIndex(true); } else { // new code path for RelatedView Debug.Assert(null == _comparison, "RelatedView should not have a comparison function"); UpdateIndex(true, false); } if (null != findIndexes) { Dictionary indexes = findIndexes; findIndexes = null; foreach(KeyValuePair entry in indexes) { entry.Value.RemoveRef(); } } } protected void UpdateIndex() { UpdateIndex(false); } protected virtual void UpdateIndex(bool force) { UpdateIndex(force, true); } internal void UpdateIndex(bool force, bool fireEvent) { IntPtr hscp; Bid.ScopeEnter(out hscp, " %d#, force=%d{bool}\n", ObjectID, force); try { if (open != shouldOpen || force) { this.open = shouldOpen; Index newIndex = null; if (open) { if (table != null) { if (null != SortComparison) { // because an Index with with a Comparison %d#, TableName='%ls', distinct=%d{bool}\n", ObjectID, tableName, distinct); if (columnNames == null){ throw ExceptionBuilder.ArgumentNull("columnNames"); } DataTable dt = new DataTable(); dt.Locale = this.table.Locale; dt.CaseSensitive = this.table.CaseSensitive; dt.TableName = ((null != tableName) ? tableName : this.table.TableName); dt.Namespace = this.table.Namespace; dt.Prefix = this.table.Prefix; if (columnNames.Length == 0) { columnNames = new string[Table.Columns.Count]; for (int i = 0; i < columnNames.Length; i++) { columnNames[i] = Table.Columns[i].ColumnName; } } int [] columnIndexes = new int[columnNames.Length]; List
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ObjectConverter.cs
- sqlser.cs
- AesManaged.cs
- AnnotationObservableCollection.cs
- dataobject.cs
- RelationshipSet.cs
- ValueChangedEventManager.cs
- ErrorFormatter.cs
- ISAPIApplicationHost.cs
- DateTimeFormatInfo.cs
- _PooledStream.cs
- SqlClientWrapperSmiStream.cs
- FontNameEditor.cs
- BindingWorker.cs
- CredentialSelector.cs
- WebPartManager.cs
- CollectionViewGroup.cs
- DeferredSelectedIndexReference.cs
- GridViewUpdatedEventArgs.cs
- Mutex.cs
- MailHeaderInfo.cs
- XmlIlVisitor.cs
- IncomingWebRequestContext.cs
- MemberInfoSerializationHolder.cs
- ClientEventManager.cs
- SQLResource.cs
- HijriCalendar.cs
- AQNBuilder.cs
- ThreadInterruptedException.cs
- SortQuery.cs
- HttpCachePolicyWrapper.cs
- ExceptionUtil.cs
- DispatcherSynchronizationContext.cs
- RawStylusActions.cs
- IntSecurity.cs
- DockEditor.cs
- DataGridViewComboBoxColumn.cs
- Stroke2.cs
- FileLevelControlBuilderAttribute.cs
- ResolveInfo.cs
- SoapDocumentMethodAttribute.cs
- Margins.cs
- HybridObjectCache.cs
- EntityContainerAssociationSet.cs
- DocumentGridContextMenu.cs
- HashCoreRequest.cs
- ProcessModule.cs
- GenericUI.cs
- SecurityPermission.cs
- CubicEase.cs
- InplaceBitmapMetadataWriter.cs
- OpenFileDialog.cs
- DataGridState.cs
- ScriptReferenceEventArgs.cs
- TreeIterator.cs
- AdornerDecorator.cs
- OdbcConnection.cs
- DBNull.cs
- Wizard.cs
- CharacterBuffer.cs
- SizeConverter.cs
- MenuEventArgs.cs
- ConnectionInterfaceCollection.cs
- NextPreviousPagerField.cs
- StoreContentChangedEventArgs.cs
- TreeViewItem.cs
- mactripleDES.cs
- GridItem.cs
- WebResponse.cs
- SqlInfoMessageEvent.cs
- ConfigurationValidatorAttribute.cs
- TemplateFactory.cs
- LineInfo.cs
- ClientBuildManagerCallback.cs
- SafeRightsManagementPubHandle.cs
- UnmanagedMemoryStreamWrapper.cs
- Domain.cs
- GroupBox.cs
- webbrowsersite.cs
- DetailsViewUpdatedEventArgs.cs
- LayoutSettings.cs
- SessionEndingCancelEventArgs.cs
- TextDataBindingHandler.cs
- PresentationTraceSources.cs
- StorageAssociationSetMapping.cs
- TextReader.cs
- SectionUpdates.cs
- ClientRequest.cs
- SharedStatics.cs
- RNGCryptoServiceProvider.cs
- DependencyObject.cs
- ParamArrayAttribute.cs
- UseLicense.cs
- AutoGeneratedField.cs
- DataColumnChangeEvent.cs
- MethodSignatureGenerator.cs
- TreeView.cs
- WhitespaceSignificantCollectionAttribute.cs
- DeleteWorkflowOwnerCommand.cs
- XmlSchemas.cs