yt_Button.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing.Drawing2D;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;
  10. using System.Windows.Forms;
  11. using ZedGraph;
  12. namespace ImpulseVision
  13. {
  14. public class yt_Button : Button
  15. {
  16. #region -- Свойства --
  17. [Description("Цвет обводки (границы) кнопки")]
  18. public Color BorderColor { get; set; } = Color.Tomato;
  19. [Description("Указывает, включено ли использование отдельного цвета обводки (границы) кнопки")]
  20. public bool BorderColorEnabled { get; set; } = false;
  21. [Description("Цвет обводки (границы) кнопки при наведении курсора")]
  22. public Color BorderColorOnHover { get; set; } = Color.Tomato;
  23. [Description("Указывает, включено ли использование отдельного цвета обводки (границы) кнопки при наведении курсора")]
  24. public bool BorderColorOnHoverEnabled { get; set; } = false;
  25. [Description("Дополнительный фоновый цвет кнопки используемый для создания градиента (При BackColorGradientEnabled = true)")]
  26. public Color BackColorAdditional { get; set; } = Color.Gray;
  27. [Description("Указывает, включен ли градинт кнопки")]
  28. public bool BackColorGradientEnabled { get; set; } = false;
  29. [Description("Определяет направление линейного градиента шапки")]
  30. public LinearGradientMode BackColorGradientMode { get; set; } = LinearGradientMode.Horizontal;
  31. [Description("Текст, отображаемый при наведении курсора")]
  32. public string TextHover { get; set; }
  33. private bool roundingEnable = false;
  34. [Description("Вкл/Выкл закругление объекта")]
  35. public bool RoundingEnable
  36. {
  37. get => roundingEnable;
  38. set
  39. {
  40. roundingEnable = value;
  41. Refresh();
  42. }
  43. }
  44. private int roundingPercent = 100;
  45. [DisplayName("Rounding [%]")]
  46. [DefaultValue(100)]
  47. [Description("Указывает радиус закругления объекта в процентном соотношении")]
  48. public int Rounding
  49. {
  50. get => roundingPercent;
  51. set
  52. {
  53. if (value >= 0 && value <= 100)
  54. {
  55. roundingPercent = value;
  56. Refresh();
  57. }
  58. }
  59. }
  60. [Description("Вкл/Выкл эффект волны по нажатию кнопки курсором.")]
  61. public bool UseRippleEffect { get; set; } = true;
  62. [Description("Цвет эффекта волны по нажатию кнопки курсором")]
  63. public Color RippleColor { get; set; } = Color.Black;
  64. [Description("Вкл/Выкл эффект нажатия кнопки.")]
  65. public bool UseDownPressEffectOnClick { get; set; }
  66. public bool UseZoomEffectOnHover { get; set; }
  67. public override string Text
  68. {
  69. get { return base.Text; }
  70. set
  71. {
  72. base.Text = value;
  73. Invalidate();
  74. }
  75. }
  76. #endregion
  77. #region -- Переменные --
  78. private StringFormat SF = new StringFormat();
  79. private bool MouseEntered = false;
  80. private bool MousePressed = false;
  81. Animation CurtainButtonAnim = new Animation();
  82. //Animation RippleButtonAnim = new Animation();
  83. Animation TextSlideAnim = new Animation();
  84. Dictionary<Animation, Rectangle> RippleButtonAnimDic = new Dictionary<Animation, Rectangle>();
  85. Point ClickLocation = new Point();
  86. #endregion
  87. public yt_Button()
  88. {
  89. SetStyle(
  90. ControlStyles.AllPaintingInWmPaint |
  91. ControlStyles.OptimizedDoubleBuffer |
  92. ControlStyles.ResizeRedraw |
  93. ControlStyles.SupportsTransparentBackColor |
  94. ControlStyles.UserPaint |
  95. ControlStyles.Opaque |
  96. ControlStyles.Selectable |
  97. ControlStyles.UserMouse |
  98. ControlStyles.EnableNotifyMessage,
  99. true);
  100. DoubleBuffered = true;
  101. Size = new Size(100, 30);
  102. Font = new Font("Verdana", 8.25F, FontStyle.Regular);
  103. Cursor = Cursors.Hand;
  104. BackColor = Color.Tomato;
  105. BorderColor = BackColor;
  106. ForeColor = Color.White;
  107. SF.Alignment = StringAlignment.Center;
  108. SF.LineAlignment = StringAlignment.Center;
  109. }
  110. protected override void OnPaint(PaintEventArgs e)
  111. {
  112. base.OnPaint(e);
  113. Graphics graph = e.Graphics;
  114. graph.SmoothingMode = SmoothingMode.HighQuality;
  115. graph.InterpolationMode = InterpolationMode.HighQualityBicubic;
  116. graph.PixelOffsetMode = PixelOffsetMode.HighQuality;
  117. graph.SmoothingMode = SmoothingMode.AntiAlias;
  118. graph.Clear(Parent.BackColor);
  119. Rectangle rect = new Rectangle(0, 0, Width - 1, Height - 1);
  120. Rectangle rectCurtain = new Rectangle(0, 0, (int)CurtainButtonAnim.Value, Height - 1);
  121. //Rectangle rectRipple = new Rectangle(
  122. // ClickLocation.X - (int)RippleButtonAnim.Value / 2,
  123. // ClickLocation.Y - (int)RippleButtonAnim.Value / 2,
  124. // (int)RippleButtonAnim.Value,
  125. // (int)RippleButtonAnim.Value
  126. // );
  127. Rectangle rectText = new Rectangle((int)TextSlideAnim.Value, rect.Y, rect.Width, rect.Height);
  128. Rectangle rectTextHover = new Rectangle((int)TextSlideAnim.Value - rect.Width, rect.Y, rect.Width, rect.Height);
  129. // Закругление
  130. float roundingValue = 0.1F;
  131. if (RoundingEnable && roundingPercent > 0)
  132. {
  133. roundingValue = Height / 100F * roundingPercent;
  134. }
  135. GraphicsPath rectPath = Drawer.RoundedRectangle(rect, roundingValue);
  136. //Rectangle regionRect = rect;
  137. //regionRect.Inflate(1, 1);
  138. //GraphicsPath regionPath = Drawer.RoundedRectangle(regionRect, roundingValue);
  139. //Region = new Region(regionPath);
  140. Region = new Region(rectPath);
  141. graph.Clear(Parent.BackColor);
  142. Brush headerBrush = new SolidBrush(BackColor);
  143. if (BackColorGradientEnabled)
  144. {
  145. if (rect.Width > 0 && rect.Height > 0)
  146. headerBrush = new LinearGradientBrush(rect, BackColor, BackColorAdditional, BackColorGradientMode);
  147. }
  148. Brush borderBrush = headerBrush;
  149. if (BorderColorEnabled)
  150. {
  151. borderBrush = new SolidBrush(BorderColor);
  152. if (MouseEntered && BorderColorOnHoverEnabled)
  153. borderBrush = new SolidBrush(BorderColorOnHover);
  154. }
  155. // Основной прямоугольник (Фон)
  156. graph.DrawPath(new Pen(borderBrush), rectPath);
  157. graph.FillPath(headerBrush, rectPath);
  158. graph.SetClip(rectPath);
  159. // Рисуем доп. прямоугольник (Наша шторка)
  160. graph.DrawRectangle(new Pen(Color.FromArgb(60, Color.White)), rectCurtain);
  161. graph.FillRectangle(new SolidBrush(Color.FromArgb(60, Color.White)), rectCurtain);
  162. if (UseRippleEffect == false)
  163. {
  164. // Стандартное рисование праямоугольника при клике
  165. if (MousePressed)
  166. {
  167. graph.DrawRectangle(new Pen(Color.FromArgb(30, Color.Black)), rect);
  168. graph.FillRectangle(new SolidBrush(Color.FromArgb(30, Color.Black)), rect);
  169. }
  170. }
  171. else
  172. {
  173. // Ripple Effect - Волна
  174. for (int i = 0; i < RippleButtonAnimDic.Count; i++)
  175. {
  176. KeyValuePair<Animation, Rectangle> animRect = RippleButtonAnimDic.ToList()[i];
  177. Animation MultiRippleButtonAnim = animRect.Key;
  178. Rectangle rectMultiRipple = animRect.Value;
  179. rectMultiRipple = new Rectangle(
  180. ClickLocation.X - (int)MultiRippleButtonAnim.Value / 2,
  181. ClickLocation.Y - (int)MultiRippleButtonAnim.Value / 2,
  182. (int)MultiRippleButtonAnim.Value,
  183. (int)MultiRippleButtonAnim.Value
  184. );
  185. if (MultiRippleButtonAnim.Value > 0 && MultiRippleButtonAnim.Value < MultiRippleButtonAnim.TargetValue)
  186. {
  187. graph.DrawEllipse(new Pen(Color.FromArgb(30, RippleColor)), rectMultiRipple);
  188. graph.FillEllipse(new SolidBrush(Color.FromArgb(30, RippleColor)), rectMultiRipple);
  189. }
  190. else if (MultiRippleButtonAnim.Value == MultiRippleButtonAnim.TargetValue)
  191. {
  192. if (MousePressed == false)
  193. {
  194. MultiRippleButtonAnim.Value = 0;
  195. MultiRippleButtonAnim.Status = Animation.AnimationStatus.Completed;
  196. }
  197. else
  198. {
  199. if (i == RippleButtonAnimDic.Count - 1)
  200. {
  201. graph.DrawEllipse(new Pen(Color.FromArgb(30, RippleColor)), rectMultiRipple);
  202. graph.FillEllipse(new SolidBrush(Color.FromArgb(30, RippleColor)), rectMultiRipple);
  203. }
  204. }
  205. }
  206. }
  207. // Удаляем из очереди выполненные анимации волны
  208. List<Animation> completedRippleAnimations = RippleButtonAnimDic.Keys.ToList().FindAll(x => x.Status == Animation.AnimationStatus.Completed);
  209. for (int i = 0; i < completedRippleAnimations.Count; i++)
  210. RippleButtonAnimDic.Remove(completedRippleAnimations[i]);
  211. }
  212. // Ripple Effect - Волна
  213. //////if (RippleButtonAnim.Value > 0 && RippleButtonAnim.Value < RippleButtonAnim.TargetValue)
  214. //////{
  215. ////// graph.DrawEllipse(new Pen(Color.FromArgb(30, Color.Black)), rectRipple);
  216. ////// graph.FillEllipse(new SolidBrush(Color.FromArgb(30, Color.Black)), rectRipple);
  217. //////}
  218. //////else if (RippleButtonAnim.Value == RippleButtonAnim.TargetValue)
  219. //////{
  220. ////// // Тут можно добавить проверку MousePressed, если false тогда обнуляем
  221. ////// if (MousePressed == false)
  222. ////// {
  223. ////// RippleButtonAnim.Value = 0;
  224. ////// }
  225. ////// else
  226. ////// {
  227. ////// graph.DrawEllipse(new Pen(Color.FromArgb(30, Color.Black)), rectRipple);
  228. ////// graph.FillEllipse(new SolidBrush(Color.FromArgb(30, Color.Black)), rectRipple);
  229. ////// }
  230. //////}
  231. // Рисуем текст
  232. if (string.IsNullOrEmpty(TextHover))
  233. {
  234. graph.DrawString(Text, Font, new SolidBrush(ForeColor), rect, SF);
  235. }
  236. else
  237. {
  238. graph.DrawString(Text, Font, new SolidBrush(ForeColor), rectText, SF);
  239. graph.DrawString(TextHover, Font, new SolidBrush(ForeColor), rectTextHover, SF);
  240. }
  241. }
  242. private void TextSlideAction()
  243. {
  244. if (MouseEntered)
  245. {
  246. TextSlideAnim = new Animation("TextSlide_" + Handle, Invalidate, TextSlideAnim.Value, Width - 1);
  247. }
  248. else
  249. {
  250. TextSlideAnim = new Animation("TextSlide_" + Handle, Invalidate, TextSlideAnim.Value, 0);
  251. }
  252. TextSlideAnim.StepDivider = 8;
  253. Animator.Request(TextSlideAnim, true);
  254. }
  255. //private void ButtonRippleAction()
  256. //{
  257. // RippleButtonAnim = new Animation("ButtonRipple_" + Handle, Invalidate, 0, Width * 2);
  258. // RippleButtonAnim.StepDivider = 14;
  259. // Animator.Request(RippleButtonAnim, true);
  260. //}
  261. private void ButtonMultiRippleAction()
  262. {
  263. Animation MultiRippleButtonAnim = new Animation("ButtonMultiRipple_" + Handle + DateTime.Now.Millisecond, Invalidate, 0, Width * 3);
  264. MultiRippleButtonAnim.StepDivider = 20;
  265. Animator.Request(MultiRippleButtonAnim);
  266. RippleButtonAnimDic.Add(MultiRippleButtonAnim, new Rectangle());
  267. }
  268. private void ButtonCurtainAction()
  269. {
  270. if (MouseEntered)
  271. {
  272. CurtainButtonAnim = new Animation("ButtonCurtain_" + Handle, Invalidate, CurtainButtonAnim.Value, Width - 1);
  273. }
  274. else
  275. {
  276. CurtainButtonAnim = new Animation("ButtonCurtain_" + Handle, Invalidate, CurtainButtonAnim.Value, 0);
  277. }
  278. CurtainButtonAnim.StepDivider = 8;
  279. Animator.Request(CurtainButtonAnim, true);
  280. }
  281. protected override void OnMouseEnter(EventArgs e)
  282. {
  283. base.OnMouseEnter(e);
  284. MouseEntered = true;
  285. if (UseZoomEffectOnHover)
  286. {
  287. Rectangle buttonRect = new Rectangle(Location, Size);
  288. buttonRect.Inflate(1, 1);
  289. Location = buttonRect.Location;
  290. Size = buttonRect.Size;
  291. }
  292. ButtonCurtainAction();
  293. TextSlideAction();
  294. }
  295. protected override void OnMouseLeave(EventArgs e)
  296. {
  297. base.OnMouseLeave(e);
  298. MouseEntered = false;
  299. if (UseZoomEffectOnHover)
  300. {
  301. Rectangle buttonRect = new Rectangle(Location, Size);
  302. buttonRect.Inflate(-1, -1);
  303. Location = buttonRect.Location;
  304. Size = buttonRect.Size;
  305. }
  306. ButtonCurtainAction();
  307. TextSlideAction();
  308. }
  309. protected override void OnMouseDown(MouseEventArgs e)
  310. {
  311. base.OnMouseDown(e);
  312. MousePressed = true;
  313. CurtainButtonAnim.Value = CurtainButtonAnim.TargetValue;
  314. ClickLocation = e.Location;
  315. //ButtonRippleAction();
  316. ButtonMultiRippleAction();
  317. if (UseDownPressEffectOnClick) Location = new Point(Location.X, Location.Y + 2);
  318. Focus();
  319. }
  320. protected override void OnMouseUp(MouseEventArgs e)
  321. {
  322. base.OnMouseUp(e);
  323. MousePressed = false;
  324. Invalidate();
  325. if (UseDownPressEffectOnClick) Location = new Point(Location.X, Location.Y - 2);
  326. }
  327. protected override void OnTextChanged(EventArgs e)
  328. {
  329. base.OnTextChanged(e);
  330. //Invalidate();
  331. }
  332. protected override void OnParentBackColorChanged(EventArgs e)
  333. {
  334. Invalidate();
  335. base.OnParentBackColorChanged(e);
  336. }
  337. }
  338. }