ComponentBase.DesignExt.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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. e.cursor = 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 - Report.Designer.LogicalToDevice(14)), (int)(AbsTop * e.ScaleY));
  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. if (width != 0)
  318. {
  319. Top += Height - (Height * Width / width);
  320. Height = Height * Width / width;
  321. }
  322. }
  323. else
  324. {
  325. Top += e.delta.X;
  326. Height -= e.delta.X;
  327. if (height != 0)
  328. {
  329. Left += Width - (Width * Height / height);
  330. Width = Width * Height / height;
  331. }
  332. }
  333. break;
  334. case SizingPoint.LeftBottom:
  335. if (wider)
  336. {
  337. Left -= e.delta.Y;
  338. Width += e.delta.Y;
  339. if (width != 0)
  340. Height = Height * Width / width;
  341. }
  342. else
  343. {
  344. Height -= e.delta.X;
  345. if (height != 0)
  346. {
  347. Left += Width - (Width * Height / height);
  348. Width = Width * Height / height;
  349. }
  350. }
  351. break;
  352. case SizingPoint.RightTop:
  353. if (wider)
  354. {
  355. Width -= e.delta.Y;
  356. if (width != 0)
  357. {
  358. Top += Height - (Height * Width / width);
  359. Height = Height * Width / width;
  360. }
  361. }
  362. else
  363. {
  364. Height += e.delta.X;
  365. Top -= e.delta.X;
  366. if (height != 0)
  367. Width = Width * Height / height;
  368. }
  369. break;
  370. case SizingPoint.RightBottom:
  371. if (wider)
  372. {
  373. Width += e.delta.Y;
  374. if (width != 0)
  375. Height = Height * Width / width;
  376. }
  377. else
  378. {
  379. Height += e.delta.X;
  380. if (height != 0)
  381. Width = Width * Height / height;
  382. }
  383. break;
  384. case SizingPoint.TopCenter:
  385. Top += e.delta.Y;
  386. Height -= e.delta.Y;
  387. if (height != 0)
  388. Width = Width * Height / height;
  389. break;
  390. case SizingPoint.BottomCenter:
  391. Height += e.delta.Y;
  392. if (height != 0)
  393. Width = Width * Height / height;
  394. break;
  395. case SizingPoint.LeftCenter:
  396. Left += e.delta.X;
  397. Width -= e.delta.X;
  398. if (width != 0)
  399. Height = Height * Width / width;
  400. break;
  401. case SizingPoint.RightCenter:
  402. Width += e.delta.X;
  403. if (width != 0)
  404. Height = Height * Width / width;
  405. break;
  406. }
  407. }
  408. else
  409. {
  410. switch (e.sizingPoint)
  411. {
  412. case SizingPoint.LeftTop:
  413. Left += e.delta.X;
  414. Width -= e.delta.X;
  415. Top += e.delta.Y;
  416. Height -= e.delta.Y;
  417. break;
  418. case SizingPoint.LeftBottom:
  419. Left += e.delta.X;
  420. Width -= e.delta.X;
  421. Height += e.delta.Y;
  422. break;
  423. case SizingPoint.RightTop:
  424. Width += e.delta.X;
  425. Top += e.delta.Y;
  426. Height -= e.delta.Y;
  427. break;
  428. case SizingPoint.RightBottom:
  429. Width += e.delta.X;
  430. Height += e.delta.Y;
  431. break;
  432. case SizingPoint.TopCenter:
  433. Top += e.delta.Y;
  434. Height -= e.delta.Y;
  435. break;
  436. case SizingPoint.BottomCenter:
  437. Height += e.delta.Y;
  438. break;
  439. case SizingPoint.LeftCenter:
  440. Left += e.delta.X;
  441. Width -= e.delta.X;
  442. break;
  443. case SizingPoint.RightCenter:
  444. Width += e.delta.X;
  445. break;
  446. }
  447. }
  448. CheckNegativeSize(e);
  449. }
  450. CheckParent(false);
  451. }
  452. }
  453. /// <summary>
  454. /// Handles MouseUp event that occurs when the user releases the mouse button in the designer.
  455. /// </summary>
  456. /// <remarks>
  457. /// <para>This method is called when the user releases the mouse button in the
  458. /// designer. The standard implementation does the following:</para>
  459. /// <list type="bullet">
  460. /// <item>if <b>e.Mode</b> is <b>WorkspaceMode2.SelectionRect</b>, checks if object
  461. /// is inside the selection rectangle and sets <b>e.Handled</b> flag if so;</item>
  462. /// <item>
  463. /// checks that object is inside its parent (calls the
  464. /// <see cref="CheckParent"/> method).
  465. /// </item>
  466. /// </list>
  467. /// </remarks>
  468. /// <param name="e">Current mouse state.</param>
  469. public virtual void HandleMouseUp(FRMouseEventArgs e)
  470. {
  471. if (e.mode == WorkspaceMode2.SelectionRect)
  472. {
  473. if (e.selectionRect.IntersectsWith(new RectangleF(AbsLeft, AbsTop, Width, Height)))
  474. e.handled = true;
  475. }
  476. else if (e.mode == WorkspaceMode2.Move || e.mode == WorkspaceMode2.Size)
  477. {
  478. if (IsSelected)
  479. CheckParent(true);
  480. }
  481. }
  482. /// <summary>
  483. /// Handles mouse wheel event.
  484. /// </summary>
  485. /// <param name="e">Current mouse state.</param>
  486. public virtual void HandleMouseWheel(FRMouseEventArgs e)
  487. {
  488. }
  489. /// <summary>
  490. /// Checks if given point is inside the object's bounds.
  491. /// </summary>
  492. /// <param name="point">point to check.</param>
  493. /// <returns><b>true</b> if <b>point</b> is inside the object's bounds.</returns>
  494. /// <remarks>
  495. /// You can override this method if your objectis not of rectangular form.
  496. /// </remarks>
  497. public virtual bool PointInObject(PointF point)
  498. {
  499. return AbsBounds.Contains(point);
  500. }
  501. #endregion Public Methods
  502. #region Protected Methods
  503. /// <summary>
  504. /// Draws the selection point.
  505. /// </summary>
  506. /// <param name="e">Paint event args.</param>
  507. /// <param name="p"><see cref="Pen"/> object.</param>
  508. /// <param name="b"><see cref="Brush"/> object.</param>
  509. /// <param name="x">Left coordinate.</param>
  510. /// <param name="y">Top coordinate.</param>
  511. protected virtual void DrawSelectionPoint(FRPaintEventArgs e, Pen p, Brush b, float x, float y)
  512. {
  513. IGraphics g = e.Graphics;
  514. x = (float)Math.Round(x * e.ScaleX);
  515. y = (float)Math.Round(y * e.ScaleY);
  516. float m = Report?.Designer?.DpiMultiplier() ?? 1;
  517. Rectangle rect = Rectangle.Round(new RectangleF(x - 2 * m, y - 2 * m, 4 * m, 4 * m));
  518. g.FillRectangle(b, rect);
  519. g.DrawRectangle(p, rect);
  520. }
  521. /// <summary>
  522. /// Gets the object's selection points.
  523. /// </summary>
  524. /// <returns>Array of <see cref="SelectionPoint"/> objects.</returns>
  525. /// <remarks>
  526. /// <para>Selection point is a small square displayed at the object's sides when object is selected
  527. /// in the designer. You can drag this square by the mouse to change the object's size. For example,
  528. /// the <b>TextObject</b> has eight selection points to change its width and height by the mouse.</para>
  529. /// <para>If you are developing a new component for FastReport, you may override this method
  530. /// if your object has non-standard set of selection points. For example, if an object has something like
  531. /// "AutoSize" property, it would be good to disable all selection points if that property is <b>true</b>,
  532. /// to disable resizing of the object by the mouse.</para>
  533. /// </remarks>
  534. protected virtual SelectionPoint[] GetSelectionPoints()
  535. {
  536. return new SelectionPoint[] {
  537. new SelectionPoint(AbsLeft, AbsTop, SizingPoint.LeftTop),
  538. new SelectionPoint(AbsLeft + Width, AbsTop, SizingPoint.RightTop),
  539. new SelectionPoint(AbsLeft, AbsTop + Height, SizingPoint.LeftBottom),
  540. new SelectionPoint(AbsLeft + Width, AbsTop + Height, SizingPoint.RightBottom),
  541. new SelectionPoint(AbsLeft + Width / 2, AbsTop, SizingPoint.TopCenter),
  542. new SelectionPoint(AbsLeft + Width / 2, AbsTop + Height, SizingPoint.BottomCenter),
  543. new SelectionPoint(AbsLeft, AbsTop + Height / 2, SizingPoint.LeftCenter),
  544. new SelectionPoint(AbsLeft + Width, AbsTop + Height / 2, SizingPoint.RightCenter) };
  545. }
  546. /// <summary>
  547. /// Gets a value indicating that given point is inside selection point.
  548. /// </summary>
  549. /// <param name="x">point's x coordinate.</param>
  550. /// <param name="y">point's y coordinate.</param>
  551. /// <param name="point">selection point.</param>
  552. /// <returns><b>true</b> if <b>(x,y)</b> is inside the <b>point</b></returns>
  553. protected bool PointInSelectionPoint(float x, float y, PointF point)
  554. {
  555. return x >= point.X - 3 && x <= point.X + 3 && y >= point.Y - 3 && y <= point.Y + 3;
  556. }
  557. #endregion Protected Methods
  558. }
  559. }