MatrixButtons.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. using FastReport.Table;
  2. using FastReport.Utils;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.ComponentModel;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. namespace FastReport.AdvMatrix
  9. {
  10. /// <summary>
  11. /// Represents a base class for matrix buttons such as expand or sort button.
  12. /// </summary>
  13. public class MatrixButton : ReportComponentBase
  14. {
  15. /// <summary>
  16. /// Determines the symbol size, in pixels. 0 indicates the auto size.
  17. /// </summary>
  18. [Category("Appearance")]
  19. public int SymbolSize { get; set; }
  20. /// <summary>
  21. /// Determines whether this buttons belongs to column header. For internal use only.
  22. /// </summary>
  23. [Browsable(false)]
  24. public bool IsColumn { get; set; }
  25. /// <summary>
  26. /// Gets or set the index of this button. For internal use only.
  27. /// </summary>
  28. [Browsable(false)]
  29. public int Index { get; set; }
  30. internal AdvMatrixObject Matrix
  31. {
  32. get
  33. {
  34. Base obj = this;
  35. while (obj != null && !(obj is AdvMatrixObject))
  36. obj = obj.Parent;
  37. if (obj is AdvMatrixObject)
  38. return Report.FindObject(obj.Name) as AdvMatrixObject;
  39. return null;
  40. }
  41. }
  42. internal TableCell Cell
  43. {
  44. get
  45. {
  46. Base obj = this;
  47. while (obj != null && !(obj is TableCell))
  48. obj = obj.Parent;
  49. if (obj is TableCell)
  50. return obj as TableCell;
  51. return null;
  52. }
  53. }
  54. /// <inheritdoc/>
  55. public override void Assign(Base source)
  56. {
  57. base.Assign(source);
  58. MatrixButton src = source as MatrixButton;
  59. if (src != null)
  60. {
  61. SymbolSize = src.SymbolSize;
  62. IsColumn = src.IsColumn;
  63. Index = src.Index;
  64. }
  65. }
  66. /// <inheritdoc/>
  67. public override void Serialize(FRWriter writer)
  68. {
  69. base.Serialize(writer);
  70. MatrixButton c = writer.DiffObject as MatrixButton;
  71. if (SymbolSize != c.SymbolSize)
  72. writer.WriteInt("SymbolSize", SymbolSize);
  73. if (IsColumn != c.IsColumn)
  74. writer.WriteBool("IsColumn", IsColumn);
  75. if (Index != c.Index)
  76. writer.WriteInt("Index", Index);
  77. }
  78. /// <summary>
  79. /// Initializes a new instance of the <see cref="MatrixButton"/> class.
  80. /// </summary>
  81. public MatrixButton()
  82. {
  83. Cursor = System.Windows.Forms.Cursors.Hand;
  84. Printable = false;
  85. Exportable = false;
  86. SymbolSize = 0;
  87. }
  88. }
  89. /// <summary>
  90. /// Represents the symbol used to display the matrix expand/collapse state.
  91. /// </summary>
  92. public enum CollapseSymbol
  93. {
  94. /// <summary>
  95. /// Plus/minus.
  96. /// </summary>
  97. PlusMinus,
  98. /// <summary>
  99. /// The pointer.
  100. /// </summary>
  101. Pointer,
  102. /// <summary>
  103. /// The arrow.
  104. /// </summary>
  105. Arrow
  106. }
  107. /// <summary>
  108. /// Represents the matrix button used to toggle expand/collapse state of matrix headers.
  109. /// </summary>
  110. public partial class MatrixCollapseButton : MatrixButton
  111. {
  112. /// <summary>
  113. /// Determines whether this button has a collapsed state. For internal use only.
  114. /// </summary>
  115. [Browsable(false)]
  116. public bool Collapsed { get; set; }
  117. /// <summary>
  118. /// Determines whether to show collapse/expand menu on right click.
  119. /// </summary>
  120. public bool ShowCollapseExpandMenu { get; set; }
  121. /// <summary>
  122. /// Determines the symbol used to display the button's state.
  123. /// </summary>
  124. [Category("Appearance")]
  125. public CollapseSymbol Symbol { get; set; }
  126. /// <summary>
  127. /// Determines if only one button in the group can be expanded.
  128. /// </summary>
  129. [Category("Behavior")]
  130. public bool Exclusive { get; set; }
  131. /// <inheritdoc/>
  132. public override void Assign(Base source)
  133. {
  134. base.Assign(source);
  135. MatrixCollapseButton src = source as MatrixCollapseButton;
  136. if (src != null)
  137. {
  138. Collapsed = src.Collapsed;
  139. Symbol = src.Symbol;
  140. Exclusive = src.Exclusive;
  141. ShowCollapseExpandMenu = src.ShowCollapseExpandMenu;
  142. }
  143. }
  144. /// <inheritdoc/>
  145. public override void Draw(FRPaintEventArgs e)
  146. {
  147. if (Parent == null || (Parent as ReportComponentBase).Width == 0 || (Parent as ReportComponentBase).Height == 0)
  148. return;
  149. IGraphics g = e.Graphics;
  150. IGraphicsState state = g.Save();
  151. #if SKIA
  152. g.SmoothingMode = SmoothingMode.None;
  153. #else
  154. g.SmoothingMode = SmoothingMode.AntiAlias;
  155. #endif
  156. PointF[] points = null;
  157. Pen pen = e.Cache.GetPen(Border.Color, Border.Width, DashStyle.Solid);
  158. Brush brush = e.Cache.GetBrush(Border.Color);
  159. // trying to be pixel perfect
  160. float dx = SymbolSize > 0 ? SymbolSize : (int)Math.Min(Width, Height) - 6;
  161. if ((int)dx % 2 == 1)
  162. dx -= 1;
  163. float dy = dx;
  164. g.ScaleTransform(e.ScaleX, e.ScaleY);
  165. g.TranslateTransform((int)Math.Round(AbsLeft + Width / 2 - dx / 2), (int)Math.Round(AbsTop + Height / 2 - dy / 2));
  166. if (Fill is SolidFill)
  167. g.FillRectangle(e.Cache.GetBrush((Fill as SolidFill).Color), new RectangleF(-2, -2, dx + 4, dy + 4));
  168. if (Border.Lines != BorderLines.None)
  169. g.DrawRectangle(pen, -2, -2, dx + 4, dy + 4);
  170. if (Collapsed)
  171. {
  172. switch (Symbol)
  173. {
  174. case CollapseSymbol.PlusMinus:
  175. g.DrawLine(pen, 0, dy / 2, dx, dy / 2);
  176. g.DrawLine(pen, dx / 2, 0, dx / 2, dy);
  177. break;
  178. case CollapseSymbol.Arrow:
  179. points = new PointF[] { new PointF(dx / 2, 0), new PointF(dx, dy / 2), new PointF(dx / 2, dy) };
  180. g.FillAndDrawPolygon(pen, brush, points);
  181. break;
  182. case CollapseSymbol.Pointer:
  183. g.DrawLines(pen, new PointF[] { new PointF(dx / 2, 0), new PointF(dx, dy / 2), new PointF(dx / 2, dy) });
  184. break;
  185. }
  186. }
  187. else
  188. {
  189. switch (Symbol)
  190. {
  191. case CollapseSymbol.PlusMinus:
  192. g.DrawLine(pen, 0, dy / 2, dx, dy / 2);
  193. break;
  194. case CollapseSymbol.Arrow:
  195. points = new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, dy), new PointF(dx, dy / 2) };
  196. g.FillAndDrawPolygon(pen, brush, points);
  197. break;
  198. case CollapseSymbol.Pointer:
  199. var d = dy / 4 - 1; // a bit more perfect placement
  200. g.DrawLines(pen, new PointF[] { new PointF(0 + d, dy / 2 - d), new PointF(dx / 2 + d, dy - d), new PointF(dx + d, dy / 2 - d) });
  201. break;
  202. }
  203. }
  204. g.Restore(state);
  205. }
  206. /// <inheritdoc/>
  207. public override void Serialize(FRWriter writer)
  208. {
  209. base.Serialize(writer);
  210. MatrixCollapseButton c = writer.DiffObject as MatrixCollapseButton;
  211. if (Collapsed != c.Collapsed)
  212. writer.WriteBool("Collapsed", Collapsed);
  213. if (Exclusive != c.Exclusive)
  214. writer.WriteBool("Exclusive", Exclusive);
  215. if (Symbol != c.Symbol)
  216. writer.WriteValue("Symbol", Symbol);
  217. if (ShowCollapseExpandMenu != c.ShowCollapseExpandMenu)
  218. writer.WriteBool("ShowCollapseExpandMenu", ShowCollapseExpandMenu);
  219. }
  220. /// <summary>
  221. /// For internal use only,return action click for Advanced Matrix collapse button
  222. /// </summary>
  223. public void MatrixCollapseButtonClick()
  224. {
  225. if (IsColumn)
  226. Matrix.ToggleColumnVisible(Index);
  227. else
  228. Matrix.ToggleRowVisible(Index);
  229. Report.Refresh();
  230. }
  231. internal List<HeaderDataList> GetLinkedItems(HeaderData item)
  232. {
  233. List<HeaderDataList> list = new List<HeaderDataList>();
  234. Action<HeaderData> func = (root) =>
  235. {
  236. if (root == null)
  237. return;
  238. foreach (HeaderDataList dl in root.AllItems)
  239. {
  240. if (dl.Descriptor.VisibleToggledBy == Name)
  241. list.Add(dl);
  242. }
  243. };
  244. func(item);
  245. if (list.Count == 0 && item.Value == null)
  246. func(item.Parent);
  247. return list;
  248. }
  249. internal int GetLinkedItemsCount(HeaderData item)
  250. {
  251. int result = 0;
  252. foreach (HeaderDataList dl in GetLinkedItems(item))
  253. result += dl.Count;
  254. return result;
  255. }
  256. /// <summary>
  257. /// Initializes a new instance of the <see cref="MatrixCollapseButton"/> class.
  258. /// </summary>
  259. public MatrixCollapseButton()
  260. {
  261. BaseName = "CollapseButton";
  262. Symbol = CollapseSymbol.PlusMinus;
  263. Border.Lines = BorderLines.All;
  264. }
  265. }
  266. /// <summary>
  267. /// Represents the symbol used to display the matrix sort order.
  268. /// </summary>
  269. public enum SortSymbol
  270. {
  271. /// <summary>
  272. /// The arrow.
  273. /// </summary>
  274. Arrow,
  275. /// <summary>
  276. /// The pointer.
  277. /// </summary>
  278. Pointer
  279. }
  280. /// <summary>
  281. /// Represents the matrix button used to toggle sort order of matrix headers.
  282. /// </summary>
  283. public partial class MatrixSortButton : MatrixButton
  284. {
  285. /// <summary>
  286. /// Determines the sort state of this button.
  287. /// </summary>
  288. [Category("Behavior")]
  289. public SortOrder Sort { get; set; }
  290. /// <summary>
  291. /// Determines whether "None" sort is allowed when you switch sort states.
  292. /// </summary>
  293. [Category("Behavior")]
  294. public bool AllowInactiveSort { get; set; }
  295. /// <summary>
  296. /// Determines the symbol used to display the state of this button.
  297. /// </summary>
  298. [Category("Appearance")]
  299. public SortSymbol Symbol { get; set; }
  300. /// <summary>
  301. /// Determines the color used to display button with inactive sort state (Sort = None).
  302. /// </summary>
  303. [Category("Appearance")]
  304. public Color InactiveSortColor { get; set; }
  305. /// <inheritdoc/>
  306. public override void Assign(Base source)
  307. {
  308. base.Assign(source);
  309. MatrixSortButton src = source as MatrixSortButton;
  310. if (src != null)
  311. {
  312. Sort = src.Sort;
  313. AllowInactiveSort = src.AllowInactiveSort;
  314. Symbol = src.Symbol;
  315. InactiveSortColor = src.InactiveSortColor;
  316. }
  317. }
  318. /// <summary>
  319. /// For internal use only,return action click for Advanced Matrix sort button
  320. /// </summary>
  321. public void MatrixSortButtonClick()
  322. {
  323. SortOrder sort = (Sort == SortOrder.None ? SortOrder.Ascending : (Sort == SortOrder.Ascending ? SortOrder.Descending : SortOrder.None));
  324. if (Index == 0)
  325. {
  326. // this button toggles sort order of descriptors with SortToggledBy = this
  327. if (!AllowInactiveSort && sort == SortOrder.None)
  328. sort = SortOrder.Ascending;
  329. (Report.FindObject(Name) as MatrixSortButton).Sort = sort;
  330. }
  331. else if (Matrix != null)
  332. {
  333. if (IsColumn)
  334. Matrix.SortRowsByColumn(Index, sort);
  335. else
  336. Matrix.SortColumnsByRow(Index, sort);
  337. }
  338. Report.Refresh();
  339. }
  340. /// <inheritdoc/>
  341. public override void Draw(FRPaintEventArgs e)
  342. {
  343. IGraphics g = e.Graphics;
  344. IGraphicsState state = g.Save();
  345. PointF[] points = null;
  346. Pen pen = e.Cache.GetPen(Border.Color, Border.Width, DashStyle.Solid);
  347. Brush brush = e.Cache.GetBrush(pen.Color);
  348. // trying to be pixel perfect
  349. float dx = SymbolSize > 0 ? SymbolSize : (int)Math.Min(Width, Height) - 6;
  350. if ((int)dx % 2 == 1)
  351. dx -= 1;
  352. float dy = dx;
  353. float add = 0;
  354. #if SKIA
  355. g.SmoothingMode = SmoothingMode.None;
  356. add = e.ScaleX == 1 ? 0.5f : 0;
  357. #else
  358. g.SmoothingMode = SmoothingMode.AntiAlias;
  359. #endif
  360. g.ScaleTransform(e.ScaleX, e.ScaleY);
  361. g.TranslateTransform((int)Math.Round(AbsLeft + Width / 2 - dx / 2), (int)Math.Round(AbsTop + Height / 2 - dy / 2));
  362. if (Fill is SolidFill)
  363. g.FillRectangle(e.Cache.GetBrush((Fill as SolidFill).Color), new RectangleF(-2, -2, dx + 4, dy + 4));
  364. if (Border.Lines != BorderLines.None)
  365. g.DrawRectangle(pen, -2, -2, dx + 4, dy + 4);
  366. if (Sort == SortOrder.Ascending)
  367. {
  368. switch (Symbol)
  369. {
  370. case SortSymbol.Arrow:
  371. points = new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, 0), new PointF(dx, dy / 2) };
  372. g.FillAndDrawPolygon(pen, brush, points);
  373. break;
  374. case SortSymbol.Pointer:
  375. g.DrawLines(pen, new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, 0), new PointF(dx, dy / 2) });
  376. break;
  377. }
  378. }
  379. else if (Sort == SortOrder.Descending)
  380. {
  381. switch (Symbol)
  382. {
  383. case SortSymbol.Arrow:
  384. points = new PointF[] { new PointF(0 - add, dy / 2), new PointF(dx / 2, dy + add), new PointF(dx + add, dy / 2) };
  385. g.FillAndDrawPolygon(pen, brush, points);
  386. break;
  387. case SortSymbol.Pointer:
  388. g.DrawLines(pen, new PointF[] { new PointF(0, dy / 2), new PointF(dx / 2, dy), new PointF(dx, dy / 2) });
  389. break;
  390. }
  391. }
  392. else if (Sort == SortOrder.None)
  393. {
  394. pen = e.Cache.GetPen(InactiveSortColor, pen.Width, DashStyle.Solid);
  395. brush = e.Cache.GetBrush(pen.Color);
  396. switch (Symbol)
  397. {
  398. case SortSymbol.Arrow:
  399. points = new PointF[] { new PointF(1, dy / 2 - 1), new PointF(dx / 2, 0), new PointF(dx - 1, dy / 2 - 1) };
  400. g.FillAndDrawPolygon(pen, brush, points);
  401. points = new PointF[] { new PointF(1 - add, dy / 2 + 1), new PointF(dx / 2, dy + add), new PointF(dx - 1 + add, dy / 2 + 1) };
  402. g.FillAndDrawPolygon(pen, brush, points);
  403. break;
  404. case SortSymbol.Pointer:
  405. g.DrawLines(pen, new PointF[] { new PointF(1, dy / 2 - 1), new PointF(dx / 2, 0), new PointF(dx - 1, dy / 2 - 1) });
  406. g.DrawLines(pen, new PointF[] { new PointF(1, dy / 2 + 1), new PointF(dx / 2, dy), new PointF(dx - 1, dy / 2 + 1) });
  407. break;
  408. }
  409. }
  410. g.Restore(state);
  411. }
  412. /// <inheritdoc/>
  413. public override void Serialize(FRWriter writer)
  414. {
  415. base.Serialize(writer);
  416. MatrixSortButton c = writer.DiffObject as MatrixSortButton;
  417. if (Sort != c.Sort)
  418. writer.WriteValue("Sort", Sort);
  419. if (AllowInactiveSort != c.AllowInactiveSort)
  420. writer.WriteBool("AllowInactiveSort", AllowInactiveSort);
  421. if (Symbol != c.Symbol)
  422. writer.WriteValue("Symbol", Symbol);
  423. if (InactiveSortColor != c.InactiveSortColor)
  424. writer.WriteValue("InactiveSortColor", InactiveSortColor);
  425. }
  426. /// <summary>
  427. /// Initializes a new instance of the <see cref="MatrixSortButton"/> class.
  428. /// </summary>
  429. public MatrixSortButton()
  430. {
  431. BaseName = "SortButton";
  432. Sort = SortOrder.None;
  433. AllowInactiveSort = true;
  434. Symbol = SortSymbol.Arrow;
  435. InactiveSortColor = Color.Gray;
  436. }
  437. }
  438. }