DataRowView.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DataRowView.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data {
  9. using System.Diagnostics;
  10. using System.ComponentModel;
  11. public class DataRowView : ICustomTypeDescriptor, System.ComponentModel.IEditableObject, System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyPropertyChanged {
  12. private readonly DataView dataView;
  13. private readonly DataRow _row;
  14. private bool delayBeginEdit;
  15. private static PropertyDescriptorCollection zeroPropertyDescriptorCollection = new PropertyDescriptorCollection(null);
  16. /// <summary>
  17. /// When the PropertyChanged event happens, it must happen on the same DataRowView reference.
  18. /// This is so a generic event handler like Windows Presentation Foundation can redirect as appropriate.
  19. /// Having DataView.Equals is not sufficient for WPF, because two different instances may be equal but not equivalent.
  20. /// For DataRowView, if two instances are equal then they are equivalent.
  21. /// </summary>
  22. private System.ComponentModel.PropertyChangedEventHandler onPropertyChanged;
  23. internal DataRowView(DataView dataView, DataRow row)
  24. {
  25. this.dataView = dataView;
  26. this._row = row;
  27. }
  28. /// <remarks>
  29. /// Checks for same reference instead of equivalent <see cref="DataView"/> or <see cref="Row"/>.
  30. ///
  31. /// Necessary for ListChanged event handlers to use data structures that use the default to
  32. /// <see cref="Object.Equals(Object)"/> instead of <see cref="Object.ReferenceEquals"/>
  33. /// to understand if they need to add a <see cref="PropertyChanged"/> event handler.
  34. /// </remarks>
  35. /// <returns><see cref="Object.ReferenceEquals"/></returns>
  36. public override bool Equals(object other) {
  37. return Object.ReferenceEquals(this, other);
  38. }
  39. /// <returns>Hashcode of <see cref="Row"/></returns>
  40. public override Int32 GetHashCode() {
  41. // Everett compatability, must return hashcode for DataRow
  42. // this does prevent using this object in collections like Hashtable
  43. // which use the hashcode as an immutable value to identify this object
  44. // user could/should have used the DataRow property instead of the hashcode
  45. return Row.GetHashCode();
  46. }
  47. public DataView DataView {
  48. get {
  49. return dataView;
  50. }
  51. }
  52. internal int ObjectID {
  53. get { return _row.ObjectID; }
  54. }
  55. /// <summary>Gets or sets a value in specified column.</summary>
  56. /// <param name="ndx">Specified column index.</param>
  57. /// <remarks>Uses either <see cref="DataRowVersion.Default"/> or <see cref="DataRowVersion.Original"/> to access <see cref="Row"/></remarks>
  58. /// <exception cref="DataException"><see cref="System.Data.DataView.get_AllowEdit"/> when setting a value.</exception>
  59. /// <exception cref="IndexOutOfRangeException"><see cref="DataColumnCollection.get_Item(int)"/></exception>
  60. public object this[int ndx] {
  61. get {
  62. return Row[ndx, RowVersionDefault];
  63. }
  64. set {
  65. if (!dataView.AllowEdit && !IsNew) {
  66. throw ExceptionBuilder.CanNotEdit();
  67. }
  68. SetColumnValue(dataView.Table.Columns[ndx], value);
  69. }
  70. }
  71. /// <summary>Gets the specified column value or related child view or sets a value in specified column.</summary>
  72. /// <param name="property">Specified column or relation name when getting. Specified column name when setting.</param>
  73. /// <exception cref="ArgumentException"><see cref="DataColumnCollection.get_Item(string)"/> when <paramref name="property"/> is ambigous.</exception>
  74. /// <exception cref="ArgumentException">Unmatched <paramref name="property"/> when getting a value.</exception>
  75. /// <exception cref="DataException">Unmatched <paramref name="property"/> when setting a value.</exception>
  76. /// <exception cref="DataException"><see cref="System.Data.DataView.get_AllowEdit"/> when setting a value.</exception>
  77. public object this[string property] {
  78. get {
  79. DataColumn column = dataView.Table.Columns[property];
  80. if (null != column) {
  81. return Row[column, RowVersionDefault];
  82. }
  83. else if (dataView.Table.DataSet != null && dataView.Table.DataSet.Relations.Contains(property)) {
  84. return CreateChildView(property);
  85. }
  86. throw ExceptionBuilder.PropertyNotFound(property, dataView.Table.TableName);
  87. }
  88. set {
  89. DataColumn column = dataView.Table.Columns[property];
  90. if (null == column) {
  91. throw ExceptionBuilder.SetFailed(property);
  92. }
  93. if (!dataView.AllowEdit && !IsNew) {
  94. throw ExceptionBuilder.CanNotEdit();
  95. }
  96. SetColumnValue(column, value);
  97. }
  98. }
  99. // IDataErrorInfo stuff
  100. string System.ComponentModel.IDataErrorInfo.this[string colName] {
  101. get {
  102. return Row.GetColumnError(colName);
  103. }
  104. }
  105. string System.ComponentModel.IDataErrorInfo.Error {
  106. get {
  107. return Row.RowError;
  108. }
  109. }
  110. /// <summary>
  111. /// Gets the current version description of the <see cref="DataRow"/>
  112. /// in relation to <see cref="System.Data.DataView.get_RowStateFilter"/>
  113. /// </summary>
  114. /// <returns>Either <see cref="DataRowVersion.Current"/> or <see cref="DataRowVersion.Original"/></returns>
  115. public DataRowVersion RowVersion {
  116. get {
  117. return (RowVersionDefault & ~DataRowVersion.Proposed);
  118. }
  119. }
  120. /// <returns>Either <see cref="DataRowVersion.Default"/> or <see cref="DataRowVersion.Original"/></returns>
  121. private DataRowVersion RowVersionDefault {
  122. get {
  123. return Row.GetDefaultRowVersion(dataView.RowStateFilter);
  124. }
  125. }
  126. internal int GetRecord() {
  127. return Row.GetRecordFromVersion(RowVersionDefault);
  128. }
  129. internal bool HasRecord() {
  130. return Row.HasVersion(RowVersionDefault);
  131. }
  132. internal object GetColumnValue(DataColumn column) {
  133. return Row[column, RowVersionDefault];
  134. }
  135. internal void SetColumnValue(DataColumn column, object value) {
  136. if (delayBeginEdit) {
  137. delayBeginEdit = false;
  138. Row.BeginEdit();
  139. }
  140. if (DataRowVersion.Original == RowVersionDefault) {
  141. throw ExceptionBuilder.SetFailed(column.ColumnName);
  142. }
  143. Row[column] = value;
  144. }
  145. /// <summary>
  146. /// Returns a <see cref="System.Data.DataView"/>
  147. /// for the child <see cref="System.Data.DataTable"/>
  148. /// with the specified <see cref="System.Data.DataRelation"/>.
  149. /// </summary>
  150. /// <param name="relation">Specified <see cref="System.Data.DataRelation"/>.</param>
  151. /// <exception cref="ArgumentException">null or mismatch between <paramref name="relation"/> and <see cref="System.Data.DataView.get_Table"/>.</exception>
  152. public DataView CreateChildView(DataRelation relation, bool followParent) {
  153. if (relation == null || relation.ParentKey.Table != DataView.Table) {
  154. throw ExceptionBuilder.CreateChildView();
  155. }
  156. RelatedView childView;
  157. if (!followParent) {
  158. int record = GetRecord();
  159. object[] values = relation.ParentKey.GetKeyValues(record);
  160. childView = new RelatedView(relation.ChildColumnsReference, values);
  161. }
  162. else {
  163. childView = new RelatedView(this, relation.ParentKey, relation.ChildColumnsReference);
  164. }
  165. childView.SetIndex("", DataViewRowState.CurrentRows, null); // finish construction via RelatedView.SetIndex
  166. childView.SetDataViewManager(DataView.DataViewManager);
  167. return childView;
  168. }
  169. public DataView CreateChildView(DataRelation relation) {
  170. return CreateChildView(relation, followParent: false);
  171. }
  172. /// <summary><see cref="CreateChildView(DataRelation)"/></summary>
  173. /// <param name="relationName">Specified <see cref="System.Data.DataRelation"/> name.</param>
  174. /// <exception cref="ArgumentException">Unmatched <paramref name="relationName"/>.</exception>
  175. public DataView CreateChildView(string relationName, bool followParent) {
  176. return CreateChildView(DataView.Table.ChildRelations[relationName], followParent);
  177. }
  178. public DataView CreateChildView(string relationName) {
  179. return CreateChildView(relationName, followParent: false);
  180. }
  181. public DataRow Row {
  182. get {
  183. return _row;
  184. }
  185. }
  186. public void BeginEdit() {
  187. delayBeginEdit = true;
  188. }
  189. public void CancelEdit() {
  190. DataRow tmpRow = Row;
  191. if (IsNew) {
  192. dataView.FinishAddNew(false);
  193. }
  194. else {
  195. tmpRow.CancelEdit();
  196. }
  197. delayBeginEdit = false;
  198. }
  199. public void EndEdit() {
  200. if (IsNew) {
  201. dataView.FinishAddNew(true);
  202. }
  203. else {
  204. Row.EndEdit();
  205. }
  206. delayBeginEdit = false;
  207. }
  208. public bool IsNew {
  209. get {
  210. return (_row == dataView.addNewRow);
  211. }
  212. }
  213. public bool IsEdit {
  214. get {
  215. return (
  216. Row.HasVersion(DataRowVersion.Proposed) || // It was edited or
  217. delayBeginEdit); // DataRowView.BegingEdit() was called, but not edited yet.
  218. }
  219. }
  220. public void Delete() {
  221. dataView.Delete(Row);
  222. }
  223. #region ICustomTypeDescriptor
  224. AttributeCollection ICustomTypeDescriptor.GetAttributes() {
  225. return new AttributeCollection((Attribute[])null);
  226. }
  227. string ICustomTypeDescriptor.GetClassName() {
  228. return null;
  229. }
  230. string ICustomTypeDescriptor.GetComponentName() {
  231. return null;
  232. }
  233. TypeConverter ICustomTypeDescriptor.GetConverter() {
  234. return null;
  235. }
  236. EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
  237. return null;
  238. }
  239. PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
  240. return null;
  241. }
  242. object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
  243. return null;
  244. }
  245. EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
  246. return new EventDescriptorCollection(null);
  247. }
  248. EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
  249. return new EventDescriptorCollection(null);
  250. }
  251. PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
  252. return((ICustomTypeDescriptor)this).GetProperties(null);
  253. }
  254. PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
  255. return (dataView.Table != null ? dataView.Table.GetPropertyDescriptorCollection(attributes) : zeroPropertyDescriptorCollection);
  256. }
  257. object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
  258. return this;
  259. }
  260. #endregion
  261. public event PropertyChangedEventHandler PropertyChanged {
  262. add {
  263. onPropertyChanged += value;
  264. }
  265. remove {
  266. onPropertyChanged -= value;
  267. }
  268. }
  269. internal void RaisePropertyChangedEvent (string propName){
  270. // Do not try catch, we would mask users bugs. if they throw we would catch
  271. if (onPropertyChanged != null) {
  272. onPropertyChanged (this, new PropertyChangedEventArgs(propName));
  273. }
  274. }
  275. }
  276. }