UIUtils.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. using System;
  2. using System.Windows.Forms;
  3. using System.Drawing;
  4. using System.Reflection;
  5. namespace FastReport.Utils
  6. {
  7. /// <summary>
  8. /// A static class that contains methods to auto-convert rtl layout.
  9. /// </summary>
  10. public static class UIUtils
  11. {
  12. #region RTL support methods
  13. /// <summary>
  14. /// Changes control's layout to rtl.
  15. /// </summary>
  16. /// <param name="parent"></param>
  17. public static void CheckRTL(Control parent)
  18. {
  19. if (!Config.RightToLeft)
  20. return;
  21. parent.RightToLeft = RightToLeft.Yes;
  22. parent.SuspendLayout();
  23. foreach (Control item in parent.Controls)
  24. {
  25. try
  26. {
  27. AnchorStyles anchor = AnchorStyles.None;
  28. if ((item.Anchor & AnchorStyles.Left) != 0)
  29. anchor |= AnchorStyles.Right;
  30. if ((item.Anchor & AnchorStyles.Right) != 0)
  31. anchor |= AnchorStyles.Left;
  32. if ((item.Anchor & AnchorStyles.Top) != 0)
  33. anchor |= AnchorStyles.Top;
  34. if ((item.Anchor & AnchorStyles.Bottom) != 0)
  35. anchor |= AnchorStyles.Bottom;
  36. DockStyle dock = item.Dock;
  37. if (item.Dock == DockStyle.Left)
  38. dock = DockStyle.Right;
  39. if (item.Dock == DockStyle.Right)
  40. dock = DockStyle.Left;
  41. int width = parent is Form form ? form.ClientSize.Width : parent.Width;
  42. if (dock == DockStyle.None)
  43. item.Location = new Point(width - item.Size.Width - item.Location.X, item.Location.Y);
  44. item.Anchor = anchor;
  45. item.Dock = dock;
  46. if (item is TreeView tree)
  47. {
  48. tree.RightToLeftLayout = true;
  49. }
  50. CheckRTL(item);
  51. }
  52. catch
  53. {
  54. }
  55. }
  56. parent.ResumeLayout();
  57. }
  58. #endregion
  59. #region Draw extension methods
  60. /// <summary>
  61. /// Draws an image and a text.
  62. /// </summary>
  63. /// <param name="control">The control which is used to determine RTL and DPI settings.</param>
  64. /// <param name="e">The draw event args.</param>
  65. /// <param name="img">The image.</param>
  66. /// <param name="text">The text.</param>
  67. /// <remarks>This method is used to draw items in an owner-drawn listboxes and comboboxes. It respects RTL and DPI settings of a control.</remarks>
  68. public static void DrawImageAndText(this Control control, DrawItemEventArgs e, Image img, string text)
  69. {
  70. Graphics g = e.Graphics;
  71. int offsX = 2;
  72. if (img != null)
  73. {
  74. Rectangle imgRect = new Rectangle(
  75. control.RightToLeft == RightToLeft.Yes ? e.Bounds.Right - img.Width - control.LogicalToDevice(4) : e.Bounds.X + control.LogicalToDevice(4),
  76. e.Bounds.Y + (e.Bounds.Height - img.Height) / 2,
  77. img.Width,
  78. img.Height);
  79. g.DrawImage(img, imgRect);
  80. offsX = img.Width + control.LogicalToDevice(8);
  81. }
  82. Rectangle textRect = new Rectangle(
  83. control.RightToLeft == RightToLeft.Yes ? e.Bounds.X : e.Bounds.X + offsX,
  84. e.Bounds.Y,
  85. e.Bounds.Width - offsX,
  86. e.Bounds.Height);
  87. TextFormatFlags flags = TextFormatFlags.VerticalCenter | TextFormatFlags.EndEllipsis;
  88. if (control.RightToLeft == RightToLeft.Yes)
  89. flags |= TextFormatFlags.RightToLeft | TextFormatFlags.Right;
  90. TextRenderer.DrawText(g, text, e.Font, textRect, e.ForeColor, flags);
  91. }
  92. #if !(WPF || AVALONIA)
  93. /// <summary>
  94. /// Draws control's border.
  95. /// </summary>
  96. /// <param name="control">The control.</param>
  97. /// <param name="graphics">The graphics.</param>
  98. /// <param name="bounds">The bounds.</param>
  99. public static void DrawVisualStyleBorder(this Control control, Graphics graphics, Rectangle bounds)
  100. {
  101. ControlPaint.DrawVisualStyleBorder(graphics, bounds);
  102. }
  103. #endif
  104. #endregion
  105. #region DPI Extension methods
  106. // Per-monitor DPI support extension methods.
  107. // In .Net 4.7 MS introduced new properties and methods such as Control.DeviceDpi and Control.LogicalToDeviceUnits.
  108. // Since we are targeting .Net 4.0, emulate these methods in earlier versions of fw (also on Mono),
  109. // and use reflection to call these methods if they exist.
  110. /// <summary>
  111. /// Gets current dpi value for the control.
  112. /// </summary>
  113. /// <param name="control">The control.</param>
  114. /// <returns>The dpi value.</returns>
  115. public static int Dpi(this Control control)
  116. {
  117. try
  118. {
  119. // the reason why we use a form instead of a control: the control may not be updated yet
  120. // if we call this code from WM_DPICHANGED handler
  121. Form form = control.FindForm();
  122. if (form != null)
  123. control = form;
  124. #if (WPF || AVALONIA)
  125. return control.DeviceDpi;
  126. #else
  127. // introduced in .Net 4.7
  128. PropertyInfo p = control.GetType().GetProperty("DeviceDpi");
  129. if (p != null)
  130. return (int)(p.GetValue(control, null));
  131. #endif
  132. }
  133. catch
  134. { }
  135. return DrawUtils.ScreenDpi;
  136. }
  137. /// <summary>
  138. /// Gets current dpi multiplier for the control (1.0 for 96dpi).
  139. /// </summary>
  140. /// <param name="control">The control.</param>
  141. /// <returns>The dpi multiplier.</returns>
  142. public static float DpiMultiplier(this Control control)
  143. {
  144. return control.Dpi() / 96f;
  145. }
  146. /// <summary>
  147. /// Gets current font dpi multiplier for the control (1.0 for 96dpi).
  148. /// </summary>
  149. /// <remarks>The return value depends on the base resolution of the main screen.</remarks>
  150. /// <param name="control">The control.</param>
  151. /// <returns>The font dpi multiplier.</returns>
  152. public static float FontDpiMultiplier(this Control control)
  153. {
  154. #if AVALONIA
  155. return 1;
  156. #else
  157. return (float)control.Dpi() / DrawUtils.ScreenDpi;
  158. #endif
  159. }
  160. /// <summary>
  161. /// Converts logical units to device units (pixels).
  162. /// </summary>
  163. /// <param name="control">The control.</param>
  164. /// <param name="value">Logical units.</param>
  165. /// <returns>Device units.</returns>
  166. public static int LogicalToDevice(this Control control, int value)
  167. {
  168. return (int)(value * control.DpiMultiplier());
  169. }
  170. /// <summary>
  171. /// Converts logical units to device units (pixels).
  172. /// </summary>
  173. /// <param name="control">The control.</param>
  174. /// <param name="value">Logical units.</param>
  175. /// <returns>Device units.</returns>
  176. public static float LogicalToDevice(this Control control, float value)
  177. {
  178. return value * control.DpiMultiplier();
  179. }
  180. /// <summary>
  181. /// Converts logical units to device units (pixels).
  182. /// </summary>
  183. /// <param name="control">The control.</param>
  184. /// <param name="value">Logical units.</param>
  185. /// <returns>Device units.</returns>
  186. public static Rectangle LogicalToDevice(this Control control, Rectangle value)
  187. {
  188. return new Rectangle(
  189. control.LogicalToDevice(value.Left),
  190. control.LogicalToDevice(value.Top),
  191. control.LogicalToDevice(value.Width),
  192. control.LogicalToDevice(value.Height));
  193. }
  194. /// <summary>
  195. /// Converts logical units to device units (pixels).
  196. /// </summary>
  197. /// <param name="control">The control.</param>
  198. /// <param name="value">Logical units.</param>
  199. /// <returns>Device units.</returns>
  200. public static Point LogicalToDevice(this Control control, Point value)
  201. {
  202. return new Point(
  203. control.LogicalToDevice(value.X),
  204. control.LogicalToDevice(value.Y));
  205. }
  206. /// <summary>
  207. /// Converts logical units to device units (pixels).
  208. /// </summary>
  209. /// <param name="control">The control.</param>
  210. /// <param name="value">Logical units.</param>
  211. /// <returns>Device units.</returns>
  212. public static Size LogicalToDevice(this Control control, Size value)
  213. {
  214. return new Size(
  215. control.LogicalToDevice(value.Width),
  216. control.LogicalToDevice(value.Height));
  217. }
  218. /// <summary>
  219. /// Converts logical font to device font.
  220. /// </summary>
  221. /// <param name="control">The control.</param>
  222. /// <param name="value">Logical font.</param>
  223. /// <param name="disposeOriginal">Determines whether to dispose the original font or not.</param>
  224. /// <returns>Device font.</returns>
  225. public static Font LogicalToDevice(this Control control, Font value, bool disposeOriginal = false)
  226. {
  227. float mult = control.FontDpiMultiplier();
  228. Font result = new Font(value.Name, value.Size * mult, value.Style);
  229. if (disposeOriginal)
  230. value.Dispose();
  231. return result;
  232. }
  233. #endregion
  234. #region Image helper extension methods
  235. /// <summary>
  236. /// Returns an image from resources using control's dpi value.
  237. /// </summary>
  238. /// <param name="control">The control.</param>
  239. /// <param name="index">Image index.</param>
  240. /// <returns>An image with specified index from "buttons.png" resource.</returns>
  241. public static Image GetImage(this Control control, int index)
  242. {
  243. return Res.GetImage(index, control.Dpi());
  244. }
  245. /// <summary>
  246. /// Returns an image from resources using control's dpi value.
  247. /// </summary>
  248. /// <param name="control">The control.</param>
  249. /// <param name="name">Image name.</param>
  250. /// <returns>An image with specified index from "buttons.png" resource.</returns>
  251. public static Image GetImage(this Control control, string name)
  252. {
  253. return Res.GetImage(name, control.Dpi());
  254. }
  255. /// <summary>
  256. /// Returns an imagelist from resources using control's dpi value.
  257. /// </summary>
  258. /// <param name="control">The control.</param>
  259. /// <returns>An imagelist from "buttons.png" resource.</returns>
  260. public static ImageList GetImages(this Control control)
  261. {
  262. return Res.GetImages(control.Dpi());
  263. }
  264. #endregion
  265. }
  266. }