e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
327 lines
13 KiB
C#
327 lines
13 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="DataRowView.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">[....]</owner>
|
|
// <owner current="true" primary="false">[....]</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Data {
|
|
using System.Diagnostics;
|
|
using System.ComponentModel;
|
|
|
|
public class DataRowView : ICustomTypeDescriptor, System.ComponentModel.IEditableObject, System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyPropertyChanged {
|
|
|
|
private readonly DataView dataView;
|
|
private readonly DataRow _row;
|
|
private bool delayBeginEdit;
|
|
|
|
private static PropertyDescriptorCollection zeroPropertyDescriptorCollection = new PropertyDescriptorCollection(null);
|
|
|
|
/// <summary>
|
|
/// When the PropertyChanged event happens, it must happen on the same DataRowView reference.
|
|
/// This is so a generic event handler like Windows Presentation Foundation can redirect as appropriate.
|
|
/// Having DataView.Equals is not sufficient for WPF, because two different instances may be equal but not equivalent.
|
|
/// For DataRowView, if two instances are equal then they are equivalent.
|
|
/// </summary>
|
|
private System.ComponentModel.PropertyChangedEventHandler onPropertyChanged;
|
|
|
|
internal DataRowView(DataView dataView, DataRow row)
|
|
{
|
|
this.dataView = dataView;
|
|
this._row = row;
|
|
}
|
|
|
|
/// <remarks>
|
|
/// Checks for same reference instead of equivalent <see cref="DataView"/> or <see cref="Row"/>.
|
|
///
|
|
/// Necessary for ListChanged event handlers to use data structures that use the default to
|
|
/// <see cref="Object.Equals(Object)"/> instead of <see cref="Object.ReferenceEquals"/>
|
|
/// to understand if they need to add a <see cref="PropertyChanged"/> event handler.
|
|
/// </remarks>
|
|
/// <returns><see cref="Object.ReferenceEquals"/></returns>
|
|
public override bool Equals(object other) {
|
|
return Object.ReferenceEquals(this, other);
|
|
}
|
|
|
|
/// <returns>Hashcode of <see cref="Row"/></returns>
|
|
public override Int32 GetHashCode() {
|
|
// Everett compatability, must return hashcode for DataRow
|
|
// this does prevent using this object in collections like Hashtable
|
|
// which use the hashcode as an immutable value to identify this object
|
|
// user could/should have used the DataRow property instead of the hashcode
|
|
return Row.GetHashCode();
|
|
}
|
|
|
|
public DataView DataView {
|
|
get {
|
|
return dataView;
|
|
}
|
|
}
|
|
|
|
internal int ObjectID {
|
|
get { return _row.ObjectID; }
|
|
}
|
|
|
|
/// <summary>Gets or sets a value in specified column.</summary>
|
|
/// <param name="ndx">Specified column index.</param>
|
|
/// <remarks>Uses either <see cref="DataRowVersion.Default"/> or <see cref="DataRowVersion.Original"/> to access <see cref="Row"/></remarks>
|
|
/// <exception cref="DataException"><see cref="System.Data.DataView.get_AllowEdit"/> when setting a value.</exception>
|
|
/// <exception cref="IndexOutOfRangeException"><see cref="DataColumnCollection.get_Item(int)"/></exception>
|
|
public object this[int ndx] {
|
|
get {
|
|
return Row[ndx, RowVersionDefault];
|
|
}
|
|
set {
|
|
if (!dataView.AllowEdit && !IsNew) {
|
|
throw ExceptionBuilder.CanNotEdit();
|
|
}
|
|
SetColumnValue(dataView.Table.Columns[ndx], value);
|
|
}
|
|
}
|
|
|
|
/// <summary>Gets the specified column value or related child view or sets a value in specified column.</summary>
|
|
/// <param name="property">Specified column or relation name when getting. Specified column name when setting.</param>
|
|
/// <exception cref="ArgumentException"><see cref="DataColumnCollection.get_Item(string)"/> when <paramref name="property"/> is ambigous.</exception>
|
|
/// <exception cref="ArgumentException">Unmatched <paramref name="property"/> when getting a value.</exception>
|
|
/// <exception cref="DataException">Unmatched <paramref name="property"/> when setting a value.</exception>
|
|
/// <exception cref="DataException"><see cref="System.Data.DataView.get_AllowEdit"/> when setting a value.</exception>
|
|
public object this[string property] {
|
|
get {
|
|
DataColumn column = dataView.Table.Columns[property];
|
|
if (null != column) {
|
|
return Row[column, RowVersionDefault];
|
|
}
|
|
else if (dataView.Table.DataSet != null && dataView.Table.DataSet.Relations.Contains(property)) {
|
|
return CreateChildView(property);
|
|
}
|
|
throw ExceptionBuilder.PropertyNotFound(property, dataView.Table.TableName);
|
|
}
|
|
set {
|
|
DataColumn column = dataView.Table.Columns[property];
|
|
if (null == column) {
|
|
throw ExceptionBuilder.SetFailed(property);
|
|
}
|
|
if (!dataView.AllowEdit && !IsNew) {
|
|
throw ExceptionBuilder.CanNotEdit();
|
|
}
|
|
SetColumnValue(column, value);
|
|
}
|
|
}
|
|
|
|
// IDataErrorInfo stuff
|
|
string System.ComponentModel.IDataErrorInfo.this[string colName] {
|
|
get {
|
|
return Row.GetColumnError(colName);
|
|
}
|
|
}
|
|
|
|
string System.ComponentModel.IDataErrorInfo.Error {
|
|
get {
|
|
return Row.RowError;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current version description of the <see cref="DataRow"/>
|
|
/// in relation to <see cref="System.Data.DataView.get_RowStateFilter"/>
|
|
/// </summary>
|
|
/// <returns>Either <see cref="DataRowVersion.Current"/> or <see cref="DataRowVersion.Original"/></returns>
|
|
public DataRowVersion RowVersion {
|
|
get {
|
|
return (RowVersionDefault & ~DataRowVersion.Proposed);
|
|
}
|
|
}
|
|
|
|
/// <returns>Either <see cref="DataRowVersion.Default"/> or <see cref="DataRowVersion.Original"/></returns>
|
|
private DataRowVersion RowVersionDefault {
|
|
get {
|
|
return Row.GetDefaultRowVersion(dataView.RowStateFilter);
|
|
}
|
|
}
|
|
|
|
internal int GetRecord() {
|
|
return Row.GetRecordFromVersion(RowVersionDefault);
|
|
}
|
|
|
|
internal bool HasRecord() {
|
|
return Row.HasVersion(RowVersionDefault);
|
|
}
|
|
|
|
internal object GetColumnValue(DataColumn column) {
|
|
return Row[column, RowVersionDefault];
|
|
}
|
|
|
|
internal void SetColumnValue(DataColumn column, object value) {
|
|
if (delayBeginEdit) {
|
|
delayBeginEdit = false;
|
|
Row.BeginEdit();
|
|
}
|
|
if (DataRowVersion.Original == RowVersionDefault) {
|
|
throw ExceptionBuilder.SetFailed(column.ColumnName);
|
|
}
|
|
Row[column] = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a <see cref="System.Data.DataView"/>
|
|
/// for the child <see cref="System.Data.DataTable"/>
|
|
/// with the specified <see cref="System.Data.DataRelation"/>.
|
|
/// </summary>
|
|
/// <param name="relation">Specified <see cref="System.Data.DataRelation"/>.</param>
|
|
/// <exception cref="ArgumentException">null or mismatch between <paramref name="relation"/> and <see cref="System.Data.DataView.get_Table"/>.</exception>
|
|
public DataView CreateChildView(DataRelation relation, bool followParent) {
|
|
if (relation == null || relation.ParentKey.Table != DataView.Table) {
|
|
throw ExceptionBuilder.CreateChildView();
|
|
}
|
|
RelatedView childView;
|
|
if (!followParent) {
|
|
int record = GetRecord();
|
|
object[] values = relation.ParentKey.GetKeyValues(record);
|
|
childView = new RelatedView(relation.ChildColumnsReference, values);
|
|
}
|
|
else {
|
|
childView = new RelatedView(this, relation.ParentKey, relation.ChildColumnsReference);
|
|
}
|
|
childView.SetIndex("", DataViewRowState.CurrentRows, null); // finish construction via RelatedView.SetIndex
|
|
childView.SetDataViewManager(DataView.DataViewManager);
|
|
return childView;
|
|
}
|
|
|
|
|
|
public DataView CreateChildView(DataRelation relation) {
|
|
return CreateChildView(relation, followParent: false);
|
|
}
|
|
|
|
|
|
/// <summary><see cref="CreateChildView(DataRelation)"/></summary>
|
|
/// <param name="relationName">Specified <see cref="System.Data.DataRelation"/> name.</param>
|
|
/// <exception cref="ArgumentException">Unmatched <paramref name="relationName"/>.</exception>
|
|
public DataView CreateChildView(string relationName, bool followParent) {
|
|
return CreateChildView(DataView.Table.ChildRelations[relationName], followParent);
|
|
}
|
|
|
|
public DataView CreateChildView(string relationName) {
|
|
return CreateChildView(relationName, followParent: false);
|
|
}
|
|
|
|
public DataRow Row {
|
|
get {
|
|
return _row;
|
|
}
|
|
}
|
|
|
|
public void BeginEdit() {
|
|
delayBeginEdit = true;
|
|
}
|
|
|
|
public void CancelEdit() {
|
|
DataRow tmpRow = Row;
|
|
if (IsNew) {
|
|
dataView.FinishAddNew(false);
|
|
}
|
|
else {
|
|
tmpRow.CancelEdit();
|
|
}
|
|
delayBeginEdit = false;
|
|
}
|
|
|
|
public void EndEdit() {
|
|
if (IsNew) {
|
|
dataView.FinishAddNew(true);
|
|
}
|
|
else {
|
|
Row.EndEdit();
|
|
}
|
|
delayBeginEdit = false;
|
|
}
|
|
|
|
public bool IsNew {
|
|
get {
|
|
return (_row == dataView.addNewRow);
|
|
}
|
|
}
|
|
|
|
public bool IsEdit {
|
|
get {
|
|
return (
|
|
Row.HasVersion(DataRowVersion.Proposed) || // It was edited or
|
|
delayBeginEdit); // DataRowView.BegingEdit() was called, but not edited yet.
|
|
}
|
|
}
|
|
|
|
public void Delete() {
|
|
dataView.Delete(Row);
|
|
}
|
|
|
|
#region ICustomTypeDescriptor
|
|
|
|
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
|
|
return new AttributeCollection((Attribute[])null);
|
|
}
|
|
|
|
string ICustomTypeDescriptor.GetClassName() {
|
|
return null;
|
|
}
|
|
|
|
string ICustomTypeDescriptor.GetComponentName() {
|
|
return null;
|
|
}
|
|
|
|
TypeConverter ICustomTypeDescriptor.GetConverter() {
|
|
return null;
|
|
}
|
|
|
|
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
|
|
return null;
|
|
}
|
|
|
|
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
|
|
return null;
|
|
}
|
|
|
|
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
|
|
return null;
|
|
}
|
|
|
|
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
|
|
return new EventDescriptorCollection(null);
|
|
}
|
|
|
|
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
|
|
return new EventDescriptorCollection(null);
|
|
}
|
|
|
|
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
|
|
return((ICustomTypeDescriptor)this).GetProperties(null);
|
|
}
|
|
|
|
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
|
|
return (dataView.Table != null ? dataView.Table.GetPropertyDescriptorCollection(attributes) : zeroPropertyDescriptorCollection);
|
|
}
|
|
|
|
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
|
|
return this;
|
|
}
|
|
|
|
#endregion
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged {
|
|
add {
|
|
onPropertyChanged += value;
|
|
}
|
|
remove {
|
|
onPropertyChanged -= value;
|
|
}
|
|
|
|
}
|
|
|
|
internal void RaisePropertyChangedEvent (string propName){
|
|
// Do not try catch, we would mask users bugs. if they throw we would catch
|
|
if (onPropertyChanged != null) {
|
|
onPropertyChanged (this, new PropertyChangedEventArgs(propName));
|
|
}
|
|
}
|
|
}
|
|
}
|