PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/source/library/Interlace.UserInterface/Utilities/TypedGridSelection.cs

https://bitbucket.org/VahidN/interlace
C# | 347 lines | 199 code | 53 blank | 95 comment | 30 complexity | 1bd3bc8dc0ef670b4485edd5e695d4aa MD5 | raw file
  1. #region Using Directives and Copyright Notice
  2. // Copyright (c) 2007-2010, Computer Consultancy Pty Ltd
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. // * Redistributions of source code must retain the above copyright
  8. // notice, this list of conditions and the following disclaimer.
  9. // * Redistributions in binary form must reproduce the above copyright
  10. // notice, this list of conditions and the following disclaimer in the
  11. // documentation and/or other materials provided with the distribution.
  12. // * Neither the name of the Computer Consultancy Pty Ltd nor the
  13. // names of its contributors may be used to endorse or promote products
  14. // derived from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. // ARE DISCLAIMED. IN NO EVENT SHALL COMPUTER CONSULTANCY PTY LTD BE LIABLE
  20. // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  26. // DAMAGE.
  27. using System;
  28. using System.Collections.Generic;
  29. using System.ComponentModel;
  30. using System.Configuration;
  31. using System.Data;
  32. using DevExpress.Data;
  33. using DevExpress.XtraGrid;
  34. using DevExpress.XtraGrid.Views.Base;
  35. using DevExpress.XtraGrid.Views.Grid;
  36. using DevExpress.XtraGrid.Views.Grid.ViewInfo;
  37. #endregion
  38. namespace Interlace.Utilities
  39. {
  40. /// <summary>
  41. /// Provides properties that give type safe access to the current
  42. /// selection of a grid view.
  43. /// </summary>
  44. /// <remarks>
  45. /// The properties of this object can be bound to, and will raise events
  46. /// when they change. For example, a button that acts on the selection
  47. /// of a grid can have its Enabled property bound to HasAnySelection or
  48. /// HasSingleSelection on this object.
  49. /// </remarks>
  50. /// <typeparam name="TRow">The type of object contained in the grid.</typeparam>
  51. public class TypedGridSelection<TRow> : INotifyPropertyChanged where TRow : class
  52. {
  53. ColumnView _view;
  54. int _previousSelectionCount;
  55. bool _forbidGroupRowSelection;
  56. bool _lockRowFocusing;
  57. // TODO: Remove the hack, onnnneeeee day.
  58. bool _enablePostNewRowHack = false;
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="TypedGridSelection&lt;TRow&gt;"/> class.
  61. /// </summary>
  62. /// <param name="view">The grid view that the selection of is to be tracked.</param>
  63. public TypedGridSelection(ColumnView view)
  64. {
  65. _view = view;
  66. _view.SelectionChanged += new SelectionChangedEventHandler(_view_SelectionChanged);
  67. // When a grid is not in multi-select mode, the selection changed events aren't fired.
  68. // (See Developer Express Knowledge Base Article A1882)
  69. // As a work-around, we watch the focus events too:
  70. _view.FocusedRowChanged += new FocusedRowChangedEventHandler(_view_FocusedRowChanged);
  71. _view.GridControl.DataSourceChanged += new EventHandler(_view_DataSourceChanged);
  72. _view.ColumnFilterChanged += new EventHandler(_view_ColumnFilterChanged);
  73. _previousSelectionCount = _view.SelectedRowsCount;
  74. _forbidGroupRowSelection = false;
  75. _lockRowFocusing = false;
  76. }
  77. void _view_ColumnFilterChanged(object sender, EventArgs e)
  78. {
  79. HandleRowFocusOrSelectionChange();
  80. }
  81. public bool EnablePostNewRowHack
  82. {
  83. get { return _enablePostNewRowHack; }
  84. set { _enablePostNewRowHack = value; }
  85. }
  86. void _view_DataSourceChanged(object sender, EventArgs e)
  87. {
  88. if (PropertyChanged != null)
  89. {
  90. PropertyChanged(this, new PropertyChangedEventArgs("HasDataSource"));
  91. }
  92. HandleRowFocusOrSelectionChange();
  93. }
  94. void HandleRowFocusOrSelectionChange()
  95. {
  96. if (PropertyChanged != null)
  97. {
  98. PropertyChanged(this, new PropertyChangedEventArgs("SingleSelection"));
  99. if (Math.Sign(_previousSelectionCount) != Math.Sign(_view.SelectedRowsCount))
  100. {
  101. PropertyChanged(this, new PropertyChangedEventArgs("HasAnySelection"));
  102. }
  103. if ((_previousSelectionCount == 1) != (_view.SelectedRowsCount == 1))
  104. {
  105. PropertyChanged(this, new PropertyChangedEventArgs("HasSingleSelection"));
  106. }
  107. }
  108. _previousSelectionCount = _view.SelectedRowsCount;
  109. }
  110. void _view_FocusedRowChanged(object sender, DevExpress.XtraGrid.Views.Base.FocusedRowChangedEventArgs e)
  111. {
  112. if (_forbidGroupRowSelection)
  113. CancelSelectionOfGroupRows(_view, e);
  114. if (!_lockRowFocusing)
  115. HandleRowFocusOrSelectionChange();
  116. }
  117. void _view_SelectionChanged(object sender, DevExpress.Data.SelectionChangedEventArgs e)
  118. {
  119. HandleRowFocusOrSelectionChange();
  120. }
  121. /// <summary>
  122. /// Occurs when one of the properties in this class change.
  123. /// </summary>
  124. public event PropertyChangedEventHandler PropertyChanged;
  125. /// <summary>
  126. /// Gets a value indicating whether the grid view has any selection.
  127. /// </summary>
  128. /// <value>
  129. /// <c>true</c> if the grid view has any selection; otherwise, <c>false</c>.
  130. /// </value>
  131. public bool HasAnySelection
  132. {
  133. get
  134. {
  135. return _view.SelectedRowsCount > 0;
  136. }
  137. }
  138. /// <summary>
  139. /// Gets a value indicating whether the grid view has a single selection.
  140. /// </summary>
  141. /// <value>
  142. /// <c>true</c> if the grid view has single selection; otherwise, <c>false</c>.
  143. /// </value>
  144. public bool HasSingleSelection
  145. {
  146. get
  147. {
  148. return _view.SelectedRowsCount == 1;
  149. }
  150. }
  151. /// <summary>
  152. /// Indicates if the grid is attached to a non-null datasource. Useful for determining
  153. /// the difference between a null datasource and an empty one.
  154. /// </summary>
  155. /// <value>True if there is a valid datasource, else false if datasource is null.</value>
  156. public bool HasDataSource
  157. {
  158. get { return _view.GridControl.DataSource != null; }
  159. }
  160. /// <summary>
  161. /// Gets the single selection.
  162. /// </summary>
  163. /// <value>The single selection if there is only one object
  164. /// selected, otherwise <c>null</c>.</value>
  165. public TRow SingleSelection
  166. {
  167. get
  168. {
  169. PostNewRows();
  170. int[] selectedRowHandles = _view.GetSelectedRows();
  171. if (selectedRowHandles.Length != 1) return null;
  172. return _view.GetRow(selectedRowHandles[0]) as TRow;
  173. }
  174. }
  175. /// <summary>
  176. /// When DevExpress grids create a row, it's not a 'real' row until there's either data
  177. /// in it, or it gets specifically asked to create the row. If you call GetRow with the
  178. /// virtual row's handle (-999998), then it throws up because the row doesn't really exist.
  179. /// This call forces the grid to create the row for real.
  180. /// </summary>
  181. private void PostNewRows()
  182. {
  183. if (_enablePostNewRowHack) _view.UpdateCurrentRow();
  184. }
  185. /// <summary>
  186. /// Determines if the single selected row is visible.
  187. /// </summary>
  188. /// <value>True if the single selected row is visible. False if
  189. /// there are multiple selected rows, or the single selected row
  190. /// is not visible. (In other words if it's scrolled out of view.)</value>
  191. public bool SingleSelectionVisible
  192. {
  193. get
  194. {
  195. int[] selectedRowHandles = _view.GetSelectedRows();
  196. if (selectedRowHandles.Length != 1) return false;
  197. return _view.GetVisibleIndex(selectedRowHandles[0]) >= 0;
  198. }
  199. }
  200. /// <summary>
  201. /// Gets a list of selected items. If there is no selection, an empty
  202. /// list is returned.
  203. /// </summary>
  204. /// <value>A list of selected items. If there is no selection, an empty
  205. /// list is returned.</value>
  206. public List<TRow> MultipleSelection
  207. {
  208. get
  209. {
  210. List<TRow> returnValue = new List<TRow>();
  211. PostNewRows();
  212. int[] selectedRowHandles = _view.GetSelectedRows();
  213. foreach (int selectedRowHandle in selectedRowHandles)
  214. {
  215. returnValue.Add(_view.GetRow(selectedRowHandle) as TRow);
  216. }
  217. return returnValue;
  218. }
  219. }
  220. /// <summary>
  221. /// Returns the number of selected items in the grid. Mainly here for convenience.
  222. /// </summary>
  223. /// <value>An int representing the number of currently selected items in the grid.</value>
  224. public int SelectionCount
  225. {
  226. get { return _view.GetSelectedRows().Length; }
  227. }
  228. /// <summary>
  229. /// When set to true, ensures that the user can't select group rows in the grid.
  230. /// </summary>
  231. public bool ForbidGroupRowSelection
  232. {
  233. get { return _forbidGroupRowSelection; }
  234. set
  235. {
  236. _forbidGroupRowSelection = value;
  237. if (value)
  238. {
  239. // Make sure we aren't already on a group row.
  240. if (_view is GridView)
  241. {
  242. GridView gridView = _view as GridView;
  243. if (gridView.IsGroupRow(gridView.FocusedRowHandle))
  244. gridView.MoveNext();
  245. }
  246. }
  247. }
  248. }
  249. private void CancelSelectionOfGroupRows(ColumnView columnView, FocusedRowChangedEventArgs e)
  250. {
  251. if (_lockRowFocusing) return;
  252. GridView gridView;
  253. if (columnView is GridView)
  254. gridView = columnView as GridView;
  255. else
  256. return;
  257. try
  258. {
  259. _lockRowFocusing = true;
  260. if (gridView.IsGroupRow(e.FocusedRowHandle) & !gridView.IsGroupRow(e.PrevFocusedRowHandle))
  261. {
  262. if (gridView.GetParentRowHandle(e.PrevFocusedRowHandle) == e.FocusedRowHandle)
  263. {
  264. if (gridView.GetVisibleIndex(e.FocusedRowHandle) > 0)
  265. {
  266. int rowHandle = gridView.GetVisibleRowHandle(
  267. gridView.GetVisibleIndex(e.FocusedRowHandle) - 1);
  268. if (gridView.IsGroupRow(rowHandle))
  269. {
  270. rowHandle = gridView.GetChildRowHandle(rowHandle,
  271. gridView.GetChildRowCount(rowHandle) - 1);
  272. }
  273. gridView.FocusedRowHandle = rowHandle;
  274. }
  275. else
  276. {
  277. gridView.FocusedRowHandle = e.PrevFocusedRowHandle;
  278. }
  279. }
  280. else
  281. {
  282. if (gridView.IsGroupRow(e.FocusedRowHandle))
  283. gridView.ExpandGroupRow(e.FocusedRowHandle, false);
  284. gridView.MoveNext();
  285. }
  286. }
  287. }
  288. finally
  289. {
  290. _lockRowFocusing = false;
  291. }
  292. }
  293. }
  294. }