ComponentBase.DesignExt.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. using FastReport.Utils;
  2. using System;
  3. using System.Drawing;
  4. using System.Drawing.Drawing2D;
  5. using System.Windows.Forms;
  6. namespace FastReport
  7. {
  8. partial class ComponentBase
  9. {
  10. #region Public Methods
  11. /// <summary>
  12. /// Corrects the object's size and sizing point if the size becomes negative.
  13. /// </summary>
  14. /// <param name="e">Current mouse state.</param>
  15. /// <para>Typically you don't need to use or override this method.</para>
  16. /// <para>This method is called by the FastReport designer to check if the object's size becomes negative
  17. /// when resizing the object by the mouse. Method must correct the object's size and/or position to
  18. /// make it positive, also change the sizing point if needed.</para>
  19. public virtual void CheckNegativeSize(FRMouseEventArgs e)
  20. {
  21. if (Width < 0 && Height < 0)
  22. {
  23. e.sizingPoint = SizingPointHelper.SwapDiagonally(e.sizingPoint);
  24. Bounds = new RectangleF(Right, Bottom, -Width, -Height);
  25. }
  26. else if (Width < 0)
  27. {
  28. e.sizingPoint = SizingPointHelper.SwapHorizontally(e.sizingPoint);
  29. Bounds = new RectangleF(Right, Top, -Width, Height);
  30. }
  31. else if (Height < 0)
  32. {
  33. e.sizingPoint = SizingPointHelper.SwapVertically(e.sizingPoint);
  34. Bounds = new RectangleF(Left, Bottom, Width, -Height);
  35. }
  36. else
  37. return;
  38. Cursor.Current = SizingPointHelper.ToCursor(e.sizingPoint);
  39. }
  40. /// <summary>
  41. /// Checks if the object is inside its parent.
  42. /// </summary>
  43. /// <param name="immediately">if <b>true</b>, check now independent of any conditions.</param>
  44. /// <remarks>
  45. /// <para>Typically you don't need to use or override this method.</para>
  46. /// <para>When you move an object with the mouse, it may be moved outside its parent. If so, this method
  47. /// must find a new parent for the object and correct it's <b>Left</b>, <b>Top</b> and <b>Parent</b>
  48. /// properties. If <b>immediately</b> parameter is <b>false</b>, you can optimize the method
  49. /// to search for new parent only if the object's bounds are outside parent. If this parameter is
  50. /// <b>true</b>, you must skip any optimizations and search for a parent immediately.</para>
  51. /// </remarks>
  52. public virtual void CheckParent(bool immediately)
  53. {
  54. }
  55. /// <summary>
  56. /// Draws the object.
  57. /// </summary>
  58. /// <param name="e">Paint event args.</param>
  59. /// <remarks>
  60. /// <para>This method is widely used in the FastReport. It is called each time when the object needs to draw
  61. /// or print itself.</para>
  62. /// <para>In order to draw the object correctly, you should multiply the object's bounds by the <b>scale</b>
  63. /// parameter.</para>
  64. /// <para><b>cache</b> parameter is used to optimize the drawing speed. It holds all items such as
  65. /// pens, fonts, brushes, string formats that was used before. If the item with requested parameters
  66. /// exists in the cache, it will be returned (instead of create new item and then dispose it).</para>
  67. /// </remarks>
  68. public virtual void Draw(FRPaintEventArgs e)
  69. {
  70. if (IsDesigning)
  71. {
  72. if (IsAncestor)
  73. e.Graphics.DrawImage(Report.Designer.GetImage(99), (int)(AbsRight * e.ScaleX - 9), (int)(AbsTop * e.ScaleY + 2));
  74. }
  75. }
  76. /// <summary>
  77. /// Draw the frame around the object to indicate that it accepts the drag&amp;drop operation.
  78. /// </summary>
  79. /// <param name="e">Paint event args.</param>
  80. /// <param name="color">The color of frame.</param>
  81. public virtual void DrawDragAcceptFrame(FRPaintEventArgs e, Color color)
  82. {
  83. RectangleF rect = new RectangleF(AbsLeft * e.ScaleX, AbsTop * e.ScaleY, Width * e.ScaleX, Height * e.ScaleY);
  84. Pen p = e.Cache.GetPen(color, 1, DashStyle.Dot);
  85. for (int i = 0; i < 3; i++)
  86. {
  87. e.Graphics.DrawRectangle(p, rect.Left, rect.Top, rect.Width, rect.Height);
  88. rect.Inflate(-1, -1);
  89. }
  90. }
  91. /// <summary>
  92. /// Draw the selection points.
  93. /// </summary>
  94. /// <param name="e">Paint event args.</param>
  95. /// <remarks>
  96. /// This method draws a set of selection points returned by the <see cref="GetSelectionPoints"/> method.
  97. /// </remarks>
  98. public virtual void DrawSelection(FRPaintEventArgs e)
  99. {
  100. if (Page == null)
  101. return;
  102. bool firstSelected = Report.Designer.SelectedObjects.IndexOf(this) == 0;
  103. Pen p = firstSelected ? Pens.Black : Pens.White;
  104. Brush b = firstSelected ? Brushes.White : Brushes.Black;
  105. SelectionPoint[] selectionPoints = GetSelectionPoints();
  106. foreach (SelectionPoint pt in selectionPoints)
  107. {
  108. DrawSelectionPoint(e, p, b, pt.x, pt.y);
  109. }
  110. }
  111. /// <inheritdoc/>
  112. public override ContextMenuBase GetContextMenu()
  113. {
  114. return new ComponentBaseMenu(Report.Designer);
  115. }
  116. /// <summary>
  117. /// Gets the preferred size of an object.
  118. /// </summary>
  119. /// <returns>Preferred size.</returns>
  120. /// <remarks>
  121. /// This method is called by the FastReport designer when you insert a new object.
  122. /// </remarks>
  123. public virtual SizeF GetPreferredSize()
  124. {
  125. return new SizeF(Units.Millimeters * 25, Units.Millimeters * 5);
  126. }
  127. /// <summary>
  128. /// Returns a "smart tag" menu.
  129. /// </summary>
  130. /// <remarks>
  131. /// "Smart tag" is a little button that appears near the object's top-right corner when we are in the
  132. /// designer and move the mouse over the object. When you click that button you will see a popup window
  133. /// where you can set up some properties of the object. FastReport uses smart tags to quickly choose
  134. /// the datasource (for a band) or data column (for objects).
  135. /// </remarks>
  136. public virtual SmartTagBase GetSmartTag()
  137. {
  138. return null;
  139. }
  140. /// <summary>
  141. /// Handles double click event in the designer.
  142. /// </summary>
  143. /// <remarks>
  144. /// This method is called when the user doubleclicks the object in the designer. Typical implementation
  145. /// invokes the object's editor (calls the <b>InvokeEditor</b> method) and sets the designer's
  146. /// <b>Modified</b> flag.
  147. /// </remarks>
  148. public virtual void HandleDoubleClick()
  149. {
  150. if (HasFlag(Flags.CanEdit) && !HasRestriction(Restrictions.DontEdit) &&
  151. this is IHasEditor && (this as IHasEditor).InvokeEditor())
  152. Report.Designer.SetModified(this, "Change");
  153. }
  154. /// <summary>
  155. /// Handles the DragDrop event in the designer.
  156. /// </summary>
  157. /// <param name="e">Current mouse state.</param>
  158. /// <remarks>
  159. /// This method is called when the user drops an item from the Data Tree window into this object.
  160. /// This method should copy the information from the <b>e.DraggedObject</b> object and set the
  161. /// <b>e.Handled</b> flag to <b>true</b> to complete the drag operation.
  162. /// </remarks>
  163. public virtual void HandleDragDrop(FRMouseEventArgs e)
  164. {
  165. }
  166. /// <summary>
  167. /// Handles the DragOver event in the designer.
  168. /// </summary>
  169. /// <param name="e">Current mouse state.</param>
  170. /// <remarks>
  171. /// This method is called when the user drags an item from the Data Tree window. This method should
  172. /// check that the mouse (<b>e.X, e.Y</b>) is inside the object, then set the <b>e.Handled</b> flag
  173. /// to <b>true</b> if an item can be dragged into this object.
  174. /// </remarks>
  175. public virtual void HandleDragOver(FRMouseEventArgs e)
  176. {
  177. }
  178. /// <summary>
  179. /// Handles KeyDown event in the designer.
  180. /// </summary>
  181. /// <param name="sender">The designer's workspace.</param>
  182. /// <param name="e">Keyboard event parameters.</param>
  183. /// <remarks>
  184. /// This method is called when the user presses any key in the designer. Typical implementation
  185. /// does nothing.
  186. /// </remarks>
  187. public virtual void HandleKeyDown(Control sender, KeyEventArgs e)
  188. {
  189. }
  190. /// <summary>
  191. /// Handles MouseDown event that occurs when the user clicks the mouse in the designer.
  192. /// </summary>
  193. /// <remarks>
  194. /// <para>This method is called when the user press the mouse button in the designer.
  195. /// The standard implementation does the following:</para>
  196. /// <list type="bullet">
  197. /// <item>checks if the mouse pointer is inside the object;</item>
  198. /// <item>add an object to the selected objects list of the designer;</item>
  199. /// <item>sets the <b>e.Handled</b> flag to <b>true</b>.</item>
  200. /// </list>
  201. /// </remarks>
  202. /// <param name="e">Current mouse state.</param>
  203. public virtual void HandleMouseDown(FRMouseEventArgs e)
  204. {
  205. if (PointInObject(new PointF(e.x, e.y)))
  206. {
  207. SelectedObjectCollection selection = Report.Designer.SelectedObjects;
  208. if (e.modifierKeys == Keys.Shift)
  209. {
  210. // toggle selection
  211. if (selection.IndexOf(this) != -1)
  212. {
  213. if (selection.Count > 1)
  214. selection.Remove(this);
  215. }
  216. else
  217. selection.Add(this);
  218. }
  219. else
  220. {
  221. // select the object if not selected yet
  222. if (selection.IndexOf(this) == -1)
  223. {
  224. selection.Clear();
  225. selection.Add(this);
  226. }
  227. }
  228. e.handled = true;
  229. e.mode = WorkspaceMode2.Move;
  230. }
  231. }
  232. /// <summary>
  233. /// Handles MouseMove event that occurs when the user moves the mouse in the designer.
  234. /// </summary>
  235. /// <remarks>
  236. /// <para>This method is called when the user moves the mouse in the designer. Typical
  237. /// use of this method is to change the mouse cursor to <b>SizeAll</b> when it is over
  238. /// an object. The standard implementation does the following:</para>
  239. /// <list type="bullet">
  240. /// <item>checks if the mouse pointer is inside the object;</item>
  241. /// <item>changes the cursor shape (<b>e.Cursor</b> property);</item>
  242. /// <item>sets the <b>e.Handled</b> flag to <b>true</b>.</item>
  243. /// </list>
  244. /// </remarks>
  245. /// <param name="e">Current mouse state.</param>
  246. public virtual void HandleMouseHover(FRMouseEventArgs e)
  247. {
  248. if (PointInObject(new PointF(e.x, e.y)))
  249. {
  250. e.handled = true;
  251. e.cursor = Cursors.SizeAll;
  252. }
  253. }
  254. /// <summary>
  255. /// Handles MouseMove event that occurs when the user moves the mouse in the designer.
  256. /// </summary>
  257. /// <remarks>
  258. /// <para>This method is called when the user moves the mouse in the designer. The
  259. /// standard implementation does the following:</para>
  260. /// <list type="bullet">
  261. /// <item>
  262. /// if mouse button is not pressed, check that mouse pointer is inside one of
  263. /// the selection points returned by the <see cref="GetSelectionPoints"/>
  264. /// method and set the <b>e.SizingPoint</b> member to the corresponding sizing
  265. /// point;
  266. /// </item>
  267. /// <item>if mouse button is pressed, and <b>e.SizingPoint</b> member is not
  268. /// <b>SizingPoint.None</b>, resize the object.</item>
  269. /// </list>
  270. /// </remarks>
  271. /// <param name="e">Current mouse state.</param>
  272. public virtual void HandleMouseMove(FRMouseEventArgs e)
  273. {
  274. if (!IsSelected)
  275. return;
  276. if (e.button == MouseButtons.None)
  277. {
  278. PointF point = new PointF(e.x, e.y);
  279. e.sizingPoint = SizingPoint.None;
  280. SelectionPoint[] selectionPoints = GetSelectionPoints();
  281. foreach (SelectionPoint pt in selectionPoints)
  282. {
  283. if (PointInSelectionPoint(pt.x, pt.y, point))
  284. {
  285. e.sizingPoint = pt.sizingPoint;
  286. break;
  287. }
  288. }
  289. if (e.sizingPoint != SizingPoint.None)
  290. {
  291. e.handled = true;
  292. e.mode = WorkspaceMode2.Size;
  293. e.cursor = SizingPointHelper.ToCursor(e.sizingPoint);
  294. }
  295. }
  296. else if (!IsParentSelected)
  297. {
  298. if (e.mode == WorkspaceMode2.Move)
  299. {
  300. Left += e.delta.X;
  301. Top += e.delta.Y;
  302. }
  303. else if (e.mode == WorkspaceMode2.Size)
  304. {
  305. if ((e.modifierKeys & Keys.Shift) > 0)
  306. {
  307. bool wider = Math.Abs(e.delta.X) > Math.Abs(e.delta.Y);
  308. float width = Width;
  309. float height = Height;
  310. switch (e.sizingPoint)
  311. {
  312. case SizingPoint.LeftTop:
  313. if (wider)
  314. {
  315. Left += e.delta.Y;
  316. Width -= e.delta.Y;
  317. Top += Height - (Height * Width / width);
  318. Height = Height * Width / width;
  319. }
  320. else
  321. {
  322. Top += e.delta.X;
  323. Height -= e.delta.X;
  324. Left += Width - (Width * Height / height);
  325. Width = Width * Height / height;
  326. }
  327. break;
  328. case SizingPoint.LeftBottom:
  329. if (wider)
  330. {
  331. Left -= e.delta.Y;
  332. Width += e.delta.Y;
  333. Height = Height * Width / width;
  334. }
  335. else
  336. {
  337. Height -= e.delta.X;
  338. Left += Width - (Width * Height / height);
  339. Width = Width * Height / height;
  340. }
  341. break;
  342. case SizingPoint.RightTop:
  343. if (wider)
  344. {
  345. Width -= e.delta.Y;
  346. Top += Height - (Height * Width / width);
  347. Height = Height * Width / width;
  348. }
  349. else
  350. {
  351. Height += e.delta.X;
  352. Top -= e.delta.X;
  353. Width = Width * Height / height;
  354. }
  355. break;
  356. case SizingPoint.RightBottom:
  357. if (wider)
  358. {
  359. Width += e.delta.Y;
  360. Height = Height * Width / width;
  361. }
  362. else
  363. {
  364. Height += e.delta.X;
  365. Width = Width * Height / height;
  366. }
  367. break;
  368. case SizingPoint.TopCenter:
  369. Top += e.delta.Y;
  370. Height -= e.delta.Y;
  371. Width = Width * Height / height;
  372. break;
  373. case SizingPoint.BottomCenter:
  374. Height += e.delta.Y;
  375. Width = Width * Height / height;
  376. break;
  377. case SizingPoint.LeftCenter:
  378. Left += e.delta.X;
  379. Width -= e.delta.X;
  380. Height = Height * Width / width;
  381. break;
  382. case SizingPoint.RightCenter:
  383. Width += e.delta.X;
  384. Height = Height * Width / width;
  385. break;
  386. }
  387. }
  388. else
  389. {
  390. switch (e.sizingPoint)
  391. {
  392. case SizingPoint.LeftTop:
  393. Left += e.delta.X;
  394. Width -= e.delta.X;
  395. Top += e.delta.Y;
  396. Height -= e.delta.Y;
  397. break;
  398. case SizingPoint.LeftBottom:
  399. Left += e.delta.X;
  400. Width -= e.delta.X;
  401. Height += e.delta.Y;
  402. break;
  403. case SizingPoint.RightTop:
  404. Width += e.delta.X;
  405. Top += e.delta.Y;
  406. Height -= e.delta.Y;
  407. break;
  408. case SizingPoint.RightBottom:
  409. Width += e.delta.X;
  410. Height += e.delta.Y;
  411. break;
  412. case SizingPoint.TopCenter:
  413. Top += e.delta.Y;
  414. Height -= e.delta.Y;
  415. break;
  416. case SizingPoint.BottomCenter:
  417. Height += e.delta.Y;
  418. break;
  419. case SizingPoint.LeftCenter:
  420. Left += e.delta.X;
  421. Width -= e.delta.X;
  422. break;
  423. case SizingPoint.RightCenter:
  424. Width += e.delta.X;
  425. break;
  426. }
  427. }
  428. CheckNegativeSize(e);
  429. }
  430. CheckParent(false);
  431. }
  432. }
  433. /// <summary>
  434. /// Handles MouseUp event that occurs when the user releases the mouse button in the designer.
  435. /// </summary>
  436. /// <remarks>
  437. /// <para>This method is called when the user releases the mouse button in the
  438. /// designer. The standard implementation does the following:</para>
  439. /// <list type="bullet">
  440. /// <item>if <b>e.Mode</b> is <b>WorkspaceMode2.SelectionRect</b>, checks if object
  441. /// is inside the selection rectangle and sets <b>e.Handled</b> flag if so;</item>
  442. /// <item>
  443. /// checks that object is inside its parent (calls the
  444. /// <see cref="CheckParent"/> method).
  445. /// </item>
  446. /// </list>
  447. /// </remarks>
  448. /// <param name="e">Current mouse state.</param>
  449. public virtual void HandleMouseUp(FRMouseEventArgs e)
  450. {
  451. if (e.mode == WorkspaceMode2.SelectionRect)
  452. {
  453. if (e.selectionRect.IntersectsWith(new RectangleF(AbsLeft, AbsTop, Width, Height)))
  454. e.handled = true;
  455. }
  456. else if (e.mode == WorkspaceMode2.Move || e.mode == WorkspaceMode2.Size)
  457. {
  458. if (IsSelected)
  459. CheckParent(true);
  460. }
  461. }
  462. /// <summary>
  463. /// Handles mouse wheel event.
  464. /// </summary>
  465. /// <param name="e">Current mouse state.</param>
  466. public virtual void HandleMouseWheel(FRMouseEventArgs e)
  467. {
  468. }
  469. /// <summary>
  470. /// Checks if given point is inside the object's bounds.
  471. /// </summary>
  472. /// <param name="point">point to check.</param>
  473. /// <returns><b>true</b> if <b>point</b> is inside the object's bounds.</returns>
  474. /// <remarks>
  475. /// You can override this method if your objectis not of rectangular form.
  476. /// </remarks>
  477. public virtual bool PointInObject(PointF point)
  478. {
  479. return AbsBounds.Contains(point);
  480. }
  481. #endregion Public Methods
  482. #region Protected Methods
  483. /// <summary>
  484. /// Draws the selection point.
  485. /// </summary>
  486. /// <param name="e">Paint event args.</param>
  487. /// <param name="p"><see cref="Pen"/> object.</param>
  488. /// <param name="b"><see cref="Brush"/> object.</param>
  489. /// <param name="x">Left coordinate.</param>
  490. /// <param name="y">Top coordinate.</param>
  491. protected virtual void DrawSelectionPoint(FRPaintEventArgs e, Pen p, Brush b, float x, float y)
  492. {
  493. IGraphics g = e.Graphics;
  494. x = (float)Math.Round(x * e.ScaleX);
  495. y = (float)Math.Round(y * e.ScaleY);
  496. float m = Report?.Designer?.DpiMultiplier() ?? 1;
  497. Rectangle rect = Rectangle.Round(new RectangleF(x - 2 * m, y - 2 * m, 4 * m, 4 * m));
  498. g.FillRectangle(b, rect);
  499. g.DrawRectangle(p, rect);
  500. }
  501. /// <summary>
  502. /// Gets the object's selection points.
  503. /// </summary>
  504. /// <returns>Array of <see cref="SelectionPoint"/> objects.</returns>
  505. /// <remarks>
  506. /// <para>Selection point is a small square displayed at the object's sides when object is selected
  507. /// in the designer. You can drag this square by the mouse to change the object's size. For example,
  508. /// the <b>TextObject</b> has eight selection points to change its width and height by the mouse.</para>
  509. /// <para>If you are developing a new component for FastReport, you may override this method
  510. /// if your object has non-standard set of selection points. For example, if an object has something like
  511. /// "AutoSize" property, it would be good to disable all selection points if that property is <b>true</b>,
  512. /// to disable resizing of the object by the mouse.</para>
  513. /// </remarks>
  514. protected virtual SelectionPoint[] GetSelectionPoints()
  515. {
  516. return new SelectionPoint[] {
  517. new SelectionPoint(AbsLeft, AbsTop, SizingPoint.LeftTop),
  518. new SelectionPoint(AbsLeft + Width, AbsTop, SizingPoint.RightTop),
  519. new SelectionPoint(AbsLeft, AbsTop + Height, SizingPoint.LeftBottom),
  520. new SelectionPoint(AbsLeft + Width, AbsTop + Height, SizingPoint.RightBottom),
  521. new SelectionPoint(AbsLeft + Width / 2, AbsTop, SizingPoint.TopCenter),
  522. new SelectionPoint(AbsLeft + Width / 2, AbsTop + Height, SizingPoint.BottomCenter),
  523. new SelectionPoint(AbsLeft, AbsTop + Height / 2, SizingPoint.LeftCenter),
  524. new SelectionPoint(AbsLeft + Width, AbsTop + Height / 2, SizingPoint.RightCenter) };
  525. }
  526. /// <summary>
  527. /// Gets a value indicating that given point is inside selection point.
  528. /// </summary>
  529. /// <param name="x">point's x coordinate.</param>
  530. /// <param name="y">point's y coordinate.</param>
  531. /// <param name="point">selection point.</param>
  532. /// <returns><b>true</b> if <b>(x,y)</b> is inside the <b>point</b></returns>
  533. protected bool PointInSelectionPoint(float x, float y, PointF point)
  534. {
  535. return x >= point.X - 3 && x <= point.X + 3 && y >= point.Y - 3 && y <= point.Y + 3;
  536. }
  537. #endregion Protected Methods
  538. }
  539. }