From 37d39401c42f7ea6d84ac65a7a2f060aae7271f2 Mon Sep 17 00:00:00 2001 From: stu98832 Date: Thu, 25 Jul 2019 17:23:07 +0800 Subject: [PATCH 1/4] fix: MetroSetComboBox throw ArgumentOutOfRangeException issue --- MetroSet UI/Controls/MetroSetComboBox.cs | 25 +++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/MetroSet UI/Controls/MetroSetComboBox.cs b/MetroSet UI/Controls/MetroSetComboBox.cs index 3aacb98..38243de 100644 --- a/MetroSet UI/Controls/MetroSetComboBox.cs +++ b/MetroSet UI/Controls/MetroSetComboBox.cs @@ -245,23 +245,20 @@ protected override void OnDrawItem(DrawItemEventArgs e) var G = e.Graphics; G.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; - try + if (e.Index == -1) { - var itemState = (e.State & DrawItemState.Selected) == DrawItemState.Selected; - using (var bg = new SolidBrush(itemState ? SelectedItemBackColor : BackgroundColor)) - using (var tc = new SolidBrush(itemState ? SelectedItemForeColor : ForeColor)) - { - using (var f = new Font(Font.Name, 9)) - { - G.FillRectangle(bg, e.Bounds); - G.DrawString(GetItemText(Items[e.Index]), f, tc, e.Bounds, _mth.SetPosition(StringAlignment.Near)); - } - } - + return; } - catch + + var itemState = (e.State & DrawItemState.Selected) == DrawItemState.Selected; + using (var bg = new SolidBrush(itemState ? SelectedItemBackColor : BackgroundColor)) + using (var tc = new SolidBrush(itemState ? SelectedItemForeColor : ForeColor)) { - // + using (var f = new Font(Font.Name, 9)) + { + G.FillRectangle(bg, e.Bounds); + G.DrawString(GetItemText(Items[e.Index]), f, tc, e.Bounds, _mth.SetPosition(StringAlignment.Near)); + } } } From deaf20dfa01e880678e0a0bcfe91f4c0976c41e8 Mon Sep 17 00:00:00 2001 From: stu98832 Date: Fri, 26 Jul 2019 20:39:02 +0800 Subject: [PATCH 2/4] Improve animate of control Add: 1. Easing animate Improve: 1. Sometime speed of MetroSetTabControl animate is slow 2. Slow animate of MetroSetCheckBox and MetroSetRadioBox --- MetroSet UI/Animates/Animate.cs | 201 ++++++++++++++++++++ MetroSet UI/Animates/ColorAnimate.cs | 15 ++ MetroSet UI/Animates/DoubleAnimat.cs | 8 + MetroSet UI/Animates/IntAnimate.cs | 8 + MetroSet UI/Animates/Interpolation.cs | 112 +++++++++++ MetroSet UI/Animates/PointFAnimate.cs | 13 ++ MetroSet UI/Animates/SizeFAnimate.cs | 13 ++ MetroSet UI/Controls/MetroSetCheckBox.cs | 40 ++-- MetroSet UI/Controls/MetroSetRadioButton.cs | 40 ++-- MetroSet UI/Controls/MetroSetSwitch.cs | 40 +--- MetroSet UI/Controls/MetroSetTabControl.cs | 90 ++++++--- MetroSet UI/Enums/EasingType.cs | 24 +++ MetroSet UI/MetroSet UI.csproj | 8 + 13 files changed, 493 insertions(+), 119 deletions(-) create mode 100644 MetroSet UI/Animates/Animate.cs create mode 100644 MetroSet UI/Animates/ColorAnimate.cs create mode 100644 MetroSet UI/Animates/DoubleAnimat.cs create mode 100644 MetroSet UI/Animates/IntAnimate.cs create mode 100644 MetroSet UI/Animates/Interpolation.cs create mode 100644 MetroSet UI/Animates/PointFAnimate.cs create mode 100644 MetroSet UI/Animates/SizeFAnimate.cs create mode 100644 MetroSet UI/Enums/EasingType.cs diff --git a/MetroSet UI/Animates/Animate.cs b/MetroSet UI/Animates/Animate.cs new file mode 100644 index 0000000..6c55807 --- /dev/null +++ b/MetroSet UI/Animates/Animate.cs @@ -0,0 +1,201 @@ +using MetroSet_UI.Enums; +using System; +using System.Windows.Forms; + +namespace MetroSet_UI.Animates +{ + // interpolation animate + public abstract class Animate : IDisposable + { + // interface for custom animate + public delegate void UpdateEventHandler(T value); + + // update event for user to do anything they want + public event UpdateEventHandler Update; + + #region Internal Vars + // a bad way to record time... + private DateTime _lastUpdateTime; + // I use timer instead of thread, so you can modify control without Control.Invoke + private Timer _animateTimer; + // reverse animate + private bool _reverse; + // ...... + protected EventHandler _complated; + #endregion + + #region Constructors + // choose best interval for yourself + public Animate(int updateInterval = 16) + { + _animateTimer = new Timer() + { + Interval = updateInterval, + Enabled = false, + }; + _animateTimer.Tick += this.OnFrameUpdate; + _reverse = false; + Alpha = 0.0; + } + #endregion + + #region Functions + // just set once, and use start, back or reverse to play animate + public void Setting(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler complated = null) + { + InitialValue = initial; + EndValue = end; + EasingType = easing; + Duration = duration; + _complated = complated; + } + + // start animate with default setting + public void Start() + { + _reverse = false; + Alpha = 0.0; + Play(); + } + + // back animate with default setting + public void Back() + { + _reverse = true; + Alpha = 1.0; + Play(); + } + + // start animate with default setting + public void Start(int duration) + { + _reverse = false; + Alpha = 0.0; + Duration = duration; + Play(); + } + + // back animate with default setting + public void Back(int duration) + { + _reverse = true; + Alpha = 1.0; + Duration = duration; + Play(); + } + + // reverse animate + public void Reverse() + { + Reverse(!_reverse); + } + + // reverse animate + public void Reverse(bool val) + { + _reverse = val; + + if (!Active) + Play(); + } + + // play animate + public void Play() + { + _lastUpdateTime = DateTime.Now; + Active = true; + _animateTimer.Enabled = true; + _animateTimer.Start(); + } + + public void Pause() + { + _animateTimer.Stop(); + _animateTimer.Enabled = false; + Active = false; + } + + public void Stop() + { + Pause(); + Alpha = _reverse ? 1.0 : 0.0; + } + + // start animate with specific setting + public void Start(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler callback = null) + { + Setting(duration, initial, end, easing, callback); + Start(); + } + + // back animate with specific setting + public void Back(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler callback = null) + { + Setting(duration, initial, end, easing, callback); + Back(); + } + #endregion + + #region Events + // process frame + private void OnFrameUpdate(object sender, EventArgs e) + { + DateTime updateTime = DateTime.Now; + double elapsed; + + if (Duration == 0) + elapsed = 1.0; + else + elapsed = (updateTime - _lastUpdateTime).TotalMilliseconds / Duration; + + _lastUpdateTime = updateTime; + Alpha = Math.Max(0.0, Math.Min(Alpha + (_reverse ? -elapsed : elapsed), 1.0)); + + if (Update != null) + Update.Invoke(Value); + + if (Alpha == 0.0 || Alpha == 1.0) + { + Pause(); + if (_complated != null) + _complated.Invoke(this, null); + } + } + #endregion + + #region Properties + // progress. value between 0 and 1 + public double Alpha { get; set; } + + // animate duration + // recorded for calculating elapsed alpha + // if you use reverse animate when animate avtiving + // the real duration will different with the duration you set + public int Duration { get; set; } + + // initial state of value + public T InitialValue { get; private set; } + + public abstract T Value { get; } + + // final state of value + public T EndValue { get; private set; } + + // easing type + public EasingType EasingType { get; private set; } + + // active if the animate is running + public bool Active { get; private set; } + + // store you own variable here + public object Tag { get; set; } + #endregion + + #region Dispose + public void Dispose() + { + _animateTimer.Dispose(); + } + #endregion + } +} diff --git a/MetroSet UI/Animates/ColorAnimate.cs b/MetroSet UI/Animates/ColorAnimate.cs new file mode 100644 index 0000000..2905977 --- /dev/null +++ b/MetroSet UI/Animates/ColorAnimate.cs @@ -0,0 +1,15 @@ +using System.Drawing; + +namespace MetroSet_UI.Animates +{ + public class ColorAnimate : Animate + { + public override Color Value => + Color.FromArgb( + (byte)Interpolation.ValueAt(InitialValue.A, EndValue.A, Alpha, EasingType), + (byte)Interpolation.ValueAt(InitialValue.R, EndValue.R, Alpha, EasingType), + (byte)Interpolation.ValueAt(InitialValue.G, EndValue.G, Alpha, EasingType), + (byte)Interpolation.ValueAt(InitialValue.B, EndValue.B, Alpha, EasingType) + ); + } +} diff --git a/MetroSet UI/Animates/DoubleAnimat.cs b/MetroSet UI/Animates/DoubleAnimat.cs new file mode 100644 index 0000000..d7a80ca --- /dev/null +++ b/MetroSet UI/Animates/DoubleAnimat.cs @@ -0,0 +1,8 @@ +namespace MetroSet_UI.Animates +{ + public class DoubleAnimate : Animate + { + public override double Value => + Interpolation.ValueAt(InitialValue, EndValue, Alpha, EasingType); + } +} diff --git a/MetroSet UI/Animates/IntAnimate.cs b/MetroSet UI/Animates/IntAnimate.cs new file mode 100644 index 0000000..52c9af3 --- /dev/null +++ b/MetroSet UI/Animates/IntAnimate.cs @@ -0,0 +1,8 @@ +namespace MetroSet_UI.Animates +{ + public class IntAnimate : Animate + { + public override int Value => + (int)Interpolation.ValueAt(InitialValue, EndValue, Alpha, EasingType); + } +} diff --git a/MetroSet UI/Animates/Interpolation.cs b/MetroSet UI/Animates/Interpolation.cs new file mode 100644 index 0000000..083fea4 --- /dev/null +++ b/MetroSet UI/Animates/Interpolation.cs @@ -0,0 +1,112 @@ +using System; +using MetroSet_UI.Enums; + +namespace MetroSet_UI.Animates +{ + // for animate + public class Interpolation + { + public static double ValueAt(double initial, double end, double alpha, EasingType easing) + { + switch (easing) + { + default: + case EasingType.None: + case EasingType.Linear: + return (end * alpha) + (initial * (1 - alpha)); + case EasingType.QuadIn: + { + double factor = alpha * alpha; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuadOut: + { + double factor = (2 - alpha) * alpha; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuadInOut: + { + double mid = initial + (end - initial) / 2.0; + if (alpha <= 0.5) + return ValueAt(initial, mid, alpha * 2, EasingType.QuadIn); + else + return ValueAt(mid, end, (alpha - 0.5) * 2, EasingType.QuadOut); + } + case EasingType.CubeIn: + { + double factor = alpha * alpha * alpha; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.CubeOut: + { + double factor = -(alpha - 1); + factor = -(factor * factor * factor) + 1; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.CubeInOut: + { + double mid = initial + (end - initial) / 2.0; + if (alpha <= 0.5) + return ValueAt(initial, mid, alpha * 2, EasingType.CubeIn); + else + return ValueAt(mid, end, (alpha - 0.5) * 2, EasingType.CubeOut); + } + case EasingType.QuartIn: + { + double factor = alpha * alpha * alpha * alpha; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuartOut: + { + double factor = -(alpha - 1); + factor = 1-(factor * factor * factor * factor); + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuartInOut: + { + double mid = initial + (end - initial) / 2.0; + if (alpha <= 0.5) + return ValueAt(initial, mid, alpha * 2, EasingType.QuartIn); + else + return ValueAt(mid, end, (alpha - 0.5) * 2, EasingType.QuartOut); + } + case EasingType.QuintIn: + { + double factor = alpha * alpha * alpha * alpha * alpha; + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuintOut: + { + double factor = -(alpha - 1); + factor = 1-(factor * factor * factor * factor * factor); + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.QuintInOut: + { + double mid = initial + (end - initial) / 2.0; + if (alpha <= 0.5) + return ValueAt(initial, mid, alpha / 0.5, EasingType.QuintIn); + else + return ValueAt(mid, end, (alpha - 0.5) / 0.5, EasingType.QuintOut); + } + case EasingType.SineIn: + { + double factor = 1 - Math.Cos(Math.PI / 2 * alpha); + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.SineOut: + { + double factor = Math.Sin(Math.PI / 2 * alpha); + return (end * factor) + (initial * (1 - factor)); + } + case EasingType.SineInOut: + { + if (alpha <= 0.5) + return ValueAt(initial, (initial + end) / 2.0, alpha * 2, EasingType.SineIn); + else + return ValueAt((initial + end) / 2.0, end, (alpha-0.5) * 2, EasingType.SineOut); + } + } + } + } +} diff --git a/MetroSet UI/Animates/PointFAnimate.cs b/MetroSet UI/Animates/PointFAnimate.cs new file mode 100644 index 0000000..c8f3338 --- /dev/null +++ b/MetroSet UI/Animates/PointFAnimate.cs @@ -0,0 +1,13 @@ +using System.Drawing; + +namespace MetroSet_UI.Animates +{ + public class PointFAnimate : Animate + { + public override PointF Value => + new PointF( + (float)Interpolation.ValueAt(InitialValue.X, EndValue.X, Alpha, EasingType), + (float)Interpolation.ValueAt(InitialValue.Y, EndValue.Y, Alpha, EasingType) + ); + } +} diff --git a/MetroSet UI/Animates/SizeFAnimate.cs b/MetroSet UI/Animates/SizeFAnimate.cs new file mode 100644 index 0000000..bd193ba --- /dev/null +++ b/MetroSet UI/Animates/SizeFAnimate.cs @@ -0,0 +1,13 @@ +using System.Drawing; + +namespace MetroSet_UI.Animates +{ + public class SizeFAnimate : Animate + { + public override SizeF Value => + new SizeF( + (float)Interpolation.ValueAt(InitialValue.Width, EndValue.Width, Alpha, EasingType), + (float)Interpolation.ValueAt(InitialValue.Height, EndValue.Height, Alpha, EasingType) + ); + } +} diff --git a/MetroSet UI/Controls/MetroSetCheckBox.cs b/MetroSet UI/Controls/MetroSetCheckBox.cs index c9a5e40..7968f30 100644 --- a/MetroSet UI/Controls/MetroSetCheckBox.cs +++ b/MetroSet UI/Controls/MetroSetCheckBox.cs @@ -22,6 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using MetroSet_UI.Animates; using MetroSet_UI.Design; using MetroSet_UI.Enums; using MetroSet_UI.Extensions; @@ -117,8 +118,7 @@ public StyleManager StyleManager private Style _style; private StyleManager _styleManager; private bool _checked; - private readonly System.Timers.Timer _timer; - private int _alpha; + private IntAnimate _animator; #endregion Internal Vars @@ -131,18 +131,13 @@ public MetroSetCheckBox() ControlStyles.OptimizedDoubleBuffer | ControlStyles.SupportsTransparentBackColor, true); UpdateStyles(); - _alpha = 0; Font = MetroSetFonts.SemiBold(10); Cursor = Cursors.Hand; BackColor = Color.Transparent; _utl = new Utilites(); - _timer = new System.Timers.Timer() - { - Interval = 10, - AutoReset = true, - Enabled = true - }; - _timer.Elapsed += SetCheckedChanged; + _animator = new IntAnimate(); + _animator.Setting(100, 0, 255, EasingType.Linear); + _animator.Update += SetCheckedChanged; ApplyTheme(); } @@ -243,12 +238,13 @@ protected override void OnPaint(PaintEventArgs e) G.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; var rect = new Rectangle(0, 0, 16, 15); + var alpha = _animator.Value; using (var backBrush = new SolidBrush(Enabled ? BackgroundColor : Color.FromArgb(238, 238, 238))) { - using (var checkMarkPen = new Pen(Enabled ? Checked ? Color.FromArgb(_alpha, CheckSignColor) : BackgroundColor : Color.FromArgb(_alpha, DisabledBorderColor), 2)) + using (var checkMarkPen = new Pen(Enabled ? Checked || _animator.Active ? Color.FromArgb(alpha, CheckSignColor) : BackgroundColor : Color.FromArgb(alpha, DisabledBorderColor), 2)) { - using (var checkMarkBrush = new SolidBrush(Enabled ? Checked ? Color.FromArgb(_alpha, CheckSignColor) : BackgroundColor : DisabledBorderColor)) + using (var checkMarkBrush = new SolidBrush(Enabled ? Checked || _animator.Active ? Color.FromArgb(alpha, CheckSignColor) : BackgroundColor : DisabledBorderColor)) { using (var p = new Pen(Enabled ? BorderColor : DisabledBorderColor)) { @@ -301,19 +297,9 @@ private void DrawSymbol(Graphics g, Pen pen, SolidBrush solidBrush) /// /// object /// EventArgs - private void SetCheckedChanged(object o, EventArgs args) + private void SetCheckedChanged(int value) { - if (Checked) - { - if (_alpha >= 255) return; - _alpha += 1; - Invalidate(); - } - else if (_alpha > 0) - { - _alpha -= 1; - Invalidate(); - } + Invalidate(); } /// @@ -369,7 +355,7 @@ public bool Checked { _checked = value; CheckedChanged?.Invoke(this); - SetCheckedChanged(this, null); + _animator.Reverse(!value); CheckState = value ? Enums.CheckState.Checked : Enums.CheckState.Unchecked; Invalidate(); } @@ -440,10 +426,6 @@ public bool Checked protected override void Dispose(bool disposing) { - if (disposing) - { - _timer.Dispose(); - } base.Dispose(disposing); } diff --git a/MetroSet UI/Controls/MetroSetRadioButton.cs b/MetroSet UI/Controls/MetroSetRadioButton.cs index a1c1dd3..1ba8aa3 100644 --- a/MetroSet UI/Controls/MetroSetRadioButton.cs +++ b/MetroSet UI/Controls/MetroSetRadioButton.cs @@ -22,7 +22,9 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using MetroSet_UI.Animates; using MetroSet_UI.Design; +using MetroSet_UI.Enums; using MetroSet_UI.Extensions; using MetroSet_UI.Interfaces; using MetroSet_UI.Native; @@ -116,8 +118,7 @@ public StyleManager StyleManager private Style _style; private StyleManager _styleManager; private bool _checked; - private readonly System.Timers.Timer _timer; - private int _alpha; + private IntAnimate _animator; #endregion Internal Vars @@ -133,15 +134,9 @@ public MetroSetRadioButton() Font = MetroSetFonts.SemiBold(10); Font = new Font("Segoe UI", 10); _utl = new Utilites(); - _alpha = 0; - Cursor = Cursors.Hand; - _timer = new System.Timers.Timer() - { - Interval = 10, - AutoReset = true, - Enabled = true - }; - _timer.Elapsed += SetCheckedChanged; + _animator = new IntAnimate(); + _animator.Setting(100, 0, 255, EasingType.Linear); + _animator.Update += SetCheckedChanged; ApplyTheme(); } @@ -240,10 +235,11 @@ protected override void OnPaint(PaintEventArgs e) G.SmoothingMode = SmoothingMode.AntiAlias; var rect = new Rectangle(0, 0, 17, 16); + var alpha = _animator.Value; using (var backBrush = new SolidBrush(Enabled ? BackgroundColor : Color.FromArgb(238, 238, 238))) { - using (var checkMarkBrush = new SolidBrush(Enabled ? Checked ? Color.FromArgb(_alpha, CheckSignColor) : BackgroundColor : Checked ? Color.FromArgb(_alpha, DisabledBorderColor) : Color.FromArgb(238, 238, 238))) + using (var checkMarkBrush = new SolidBrush(Enabled ? Checked || _animator.Active ? Color.FromArgb(alpha, CheckSignColor) : BackgroundColor : Checked || _animator.Active ? Color.FromArgb(alpha, DisabledBorderColor) : Color.FromArgb(238, 238, 238))) { using (var p = new Pen(Enabled ? BorderColor : DisabledBorderColor)) { @@ -284,19 +280,9 @@ protected override void OnPaint(PaintEventArgs e) /// /// object /// EventArgs - private void SetCheckedChanged(object o, EventArgs args) + private void SetCheckedChanged(int value) { - if (Checked) - { - if (_alpha >= 255) return; - _alpha += 1; - Invalidate(); - } - else if (_alpha > 0) - { - _alpha -= 1; - Invalidate(); - } + Invalidate(); } /// @@ -369,7 +355,7 @@ public bool Checked { _checked = value; CheckedChanged?.Invoke(this); - SetCheckedChanged(this, null); + _animator.Reverse(!value); UpdateState(); CheckState = value ? Enums.CheckState.Checked : Enums.CheckState.Unchecked; Invalidate(); @@ -437,10 +423,6 @@ public bool Checked protected override void Dispose(bool disposing) { - if (disposing) - { - _timer.Dispose(); - } base.Dispose(disposing); } diff --git a/MetroSet UI/Controls/MetroSetSwitch.cs b/MetroSet UI/Controls/MetroSetSwitch.cs index be80de6..dd50817 100644 --- a/MetroSet UI/Controls/MetroSetSwitch.cs +++ b/MetroSet UI/Controls/MetroSetSwitch.cs @@ -22,7 +22,9 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using MetroSet_UI.Animates; using MetroSet_UI.Design; +using MetroSet_UI.Enums; using MetroSet_UI.Extensions; using MetroSet_UI.Interfaces; using MetroSet_UI.Native; @@ -114,7 +116,7 @@ public StyleManager StyleManager private bool _switched; private Style _style; private int _switchlocation = 0; - private readonly Timer _timer; + private IntAnimate _animator; #endregion Internal Vars @@ -129,12 +131,9 @@ public MetroSetSwitch() UpdateStyles(); Cursor = Cursors.Hand; _utl = new Utilites(); - _timer = new Timer - { - Interval = 1, - Enabled = false - }; - _timer.Tick += SetCheckedChanged; + _animator = new IntAnimate(); + _animator.Setting(100, 0, 132, EasingType.Linear); + _animator.Update += SetCheckedChanged; ApplyTheme(); } @@ -282,24 +281,10 @@ protected override void OnPaint(PaintEventArgs e) /// /// object /// EventArgs - private void SetCheckedChanged(object o, EventArgs args) + private void SetCheckedChanged(int value) { - if (Switched) - { - if (_switchlocation >= 131) return; - _switchlocation += 5; - Invalidate(false); - if (_switchlocation == 132) - _timer.Enabled = false; - } - else - { - if (_switchlocation <= 0) return; - _switchlocation -= 5; - Invalidate(false); - if (_switchlocation == 0) - _timer.Enabled = false; - } + _switchlocation = value; + Invalidate(false); } /// @@ -361,8 +346,7 @@ public bool Switched { _switched = value; SwitchedChanged?.Invoke(this); - SetCheckedChanged(this, null); - _timer.Enabled = true; + _animator.Reverse(!value); CheckState = value != true ? Enums.CheckState.Unchecked : Enums.CheckState.Checked; Invalidate(); } @@ -444,10 +428,6 @@ public bool Switched protected override void Dispose(bool disposing) { - if (disposing) - { - _timer.Dispose(); - } base.Dispose(disposing); } diff --git a/MetroSet UI/Controls/MetroSetTabControl.cs b/MetroSet UI/Controls/MetroSetTabControl.cs index 4f5a0ae..64e4499 100644 --- a/MetroSet UI/Controls/MetroSetTabControl.cs +++ b/MetroSet UI/Controls/MetroSetTabControl.cs @@ -22,6 +22,7 @@ * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +using MetroSet_UI.Animates; using MetroSet_UI.Child; using MetroSet_UI.Design; using MetroSet_UI.Enums; @@ -113,6 +114,9 @@ public StyleManager StyleManager private Style _style; private StyleManager _styleManager; + private PointFAnimate _slideAnimator; + private Graphics _slideGraphics; + private Bitmap _slideBitmap; #endregion Internal Vars @@ -131,6 +135,8 @@ public MetroSetTabControl() Font = MetroSetFonts.UIRegular(8); _mth = new Methods(); _utl = new Utilites(); + _slideAnimator = new PointFAnimate(); + _slideAnimator.Update += OnSlideAnimateUpdate; ApplyTheme(); } @@ -216,6 +222,25 @@ private void UpdateProperties() #region Properties + /// + /// Get or set slide animate time(ms). + /// + [Category("MetroSet Framework"), Description("Get or set slide animate time(ms).")] + public int AnimateTime + { + get; + set; + } = 200; + /// + /// Get or set slide animate easing type + /// + [Category("MetroSet Framework"), Description("Get or set slide animate easing type")] + public EasingType AnimateEasingType + { + get; + set; + } = EasingType.CubeOut; + /// /// Gets the collection of tab pages in this tab control. /// @@ -432,56 +457,59 @@ protected override void WndProc(ref Message m) // Credits : Mavamaarten private int _oldIndex; + + private void OnSlideAnimateUpdate(PointF alpha) + { + _slideGraphics.DrawImage(_slideBitmap, alpha); + } - private void DoAnimationScrollLeft(Control control1, Control control2) + private void DoSlideAnimate(TabPage control1, TabPage control2, bool moveback) { - var G = control1.CreateGraphics(); - var p1 = new Bitmap(control1.Width, control1.Height); - var p2 = new Bitmap(control2.Width, control2.Height); - control1.DrawToBitmap(p1, new Rectangle(0, 0, control1.Width, control1.Height)); - control2.DrawToBitmap(p2, new Rectangle(0, 0, control2.Width, control2.Height)); + _slideGraphics = Graphics.FromHwnd(control2.Handle); + _slideBitmap = new Bitmap(control1.Width + control2.Width, control1.Height + control2.Height); - foreach (Control c in control1.Controls) + if (moveback) { - c.Hide(); + control2.DrawToBitmap(_slideBitmap, new Rectangle(0, 0, control2.Width, control2.Height)); + control1.DrawToBitmap(_slideBitmap, new Rectangle(control2.Width, 0, control1.Width, control1.Height)); } - - var slide = control1.Width - (control1.Width % Speed); - - int a; - for (a = 0; a <= slide; a += Speed) + else { - G.DrawImage(p1, new Rectangle(a, 0, control1.Width, control1.Height)); - G.DrawImage(p2, new Rectangle(a - control2.Width, 0, control2.Width, control2.Height)); + control1.DrawToBitmap(_slideBitmap, new Rectangle(0, 0, control1.Width, control1.Height)); + control2.DrawToBitmap(_slideBitmap, new Rectangle(control1.Width, 0, control2.Width, control2.Height)); } - a = control1.Width; - G.DrawImage(p1, new Rectangle(a, 0, control1.Width, control1.Height)); - G.DrawImage(p2, new Rectangle(a - control2.Width, 0, control2.Width, control2.Height)); - - SelectedTab = (TabPage)control2; foreach (Control c in control2.Controls) { - c.Show(); + c.Hide(); } - foreach (Control c in control1.Controls) - { - c.Show(); - } + _slideAnimator.Start( + AnimateTime, + new Point(moveback ? -control2.Width : 0, 0), + new Point(moveback ? 0 : -control1.Width, 0), + AnimateEasingType, + // Complate action + (o, e) => + { + SelectedTab = control2; + foreach (Control c in control2.Controls) + { + c.Show(); + } + } + ); } protected override void OnSelecting(TabControlCancelEventArgs e) { if (!UseAnimation) return; - if (_oldIndex < e.TabPageIndex) + if (_slideAnimator.Active) { - DoAnimationScrollRight(TabPages[_oldIndex], TabPages[e.TabPageIndex]); - } - else - { - DoAnimationScrollLeft(TabPages[_oldIndex], TabPages[e.TabPageIndex]); + e.Cancel = true; + return; } + DoSlideAnimate(TabPages[_oldIndex], TabPages[e.TabPageIndex], _oldIndex > e.TabPageIndex); } protected override void OnDeselecting(TabControlCancelEventArgs e) diff --git a/MetroSet UI/Enums/EasingType.cs b/MetroSet UI/Enums/EasingType.cs new file mode 100644 index 0000000..6e1b55f --- /dev/null +++ b/MetroSet UI/Enums/EasingType.cs @@ -0,0 +1,24 @@ +namespace MetroSet_UI.Enums +{ + // for animate + public enum EasingType + { + None, + Linear, + QuadIn, + QuadOut, + QuadInOut, + CubeIn, + CubeOut, + CubeInOut, + QuartIn, + QuartOut, + QuartInOut, + QuintIn, + QuintOut, + QuintInOut, + SineIn, + SineOut, + SineInOut + } +} diff --git a/MetroSet UI/MetroSet UI.csproj b/MetroSet UI/MetroSet UI.csproj index 410e292..0ac4f31 100644 --- a/MetroSet UI/MetroSet UI.csproj +++ b/MetroSet UI/MetroSet UI.csproj @@ -76,6 +76,13 @@ + + + + + + + Component @@ -153,6 +160,7 @@ + Form From fd8ec1cc64055ce1ea397034d71b5ec69333ded6 Mon Sep 17 00:00:00 2001 From: stu98832 Date: Thu, 1 Aug 2019 12:27:28 +0800 Subject: [PATCH 3/4] Rewrite Animate Update event and Complete event to callback * Update files Animate MetroSetSwitch MetroSetCheckBox MetroSetRadioButton MetroSetTabControl --- MetroSet UI/Animates/Animate.cs | 33 ++++++++++----------- MetroSet UI/Controls/MetroSetCheckBox.cs | 12 +------- MetroSet UI/Controls/MetroSetRadioButton.cs | 12 +------- MetroSet UI/Controls/MetroSetSwitch.cs | 17 ++++------- MetroSet UI/Controls/MetroSetTabControl.cs | 31 +++++++++---------- 5 files changed, 37 insertions(+), 68 deletions(-) diff --git a/MetroSet UI/Animates/Animate.cs b/MetroSet UI/Animates/Animate.cs index 6c55807..b7ca466 100644 --- a/MetroSet UI/Animates/Animate.cs +++ b/MetroSet UI/Animates/Animate.cs @@ -7,11 +7,15 @@ namespace MetroSet_UI.Animates // interpolation animate public abstract class Animate : IDisposable { - // interface for custom animate - public delegate void UpdateEventHandler(T value); + /// + /// Call when animation update + /// + public Action Update { get; set; } - // update event for user to do anything they want - public event UpdateEventHandler Update; + /// + /// Call when animation complate + /// + public MethodInvoker Complete { get; set; } #region Internal Vars // a bad way to record time... @@ -20,8 +24,6 @@ public abstract class Animate : IDisposable private Timer _animateTimer; // reverse animate private bool _reverse; - // ...... - protected EventHandler _complated; #endregion #region Constructors @@ -41,13 +43,12 @@ public Animate(int updateInterval = 16) #region Functions // just set once, and use start, back or reverse to play animate - public void Setting(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler complated = null) + public void Setting(int duration, T initial, T end, EasingType easing = EasingType.Linear) { InitialValue = initial; EndValue = end; EasingType = easing; Duration = duration; - _complated = complated; } // start animate with default setting @@ -122,16 +123,16 @@ public void Stop() } // start animate with specific setting - public void Start(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler callback = null) + public void Start(int duration, T initial, T end, EasingType easing = EasingType.Linear) { - Setting(duration, initial, end, easing, callback); + Setting(duration, initial, end, easing); Start(); } // back animate with specific setting - public void Back(int duration, T initial, T end, EasingType easing = EasingType.Linear, EventHandler callback = null) + public void Back(int duration, T initial, T end, EasingType easing = EasingType.Linear) { - Setting(duration, initial, end, easing, callback); + Setting(duration, initial, end, easing); Back(); } #endregion @@ -150,15 +151,13 @@ private void OnFrameUpdate(object sender, EventArgs e) _lastUpdateTime = updateTime; Alpha = Math.Max(0.0, Math.Min(Alpha + (_reverse ? -elapsed : elapsed), 1.0)); - - if (Update != null) - Update.Invoke(Value); + + Update?.Invoke(Value); if (Alpha == 0.0 || Alpha == 1.0) { Pause(); - if (_complated != null) - _complated.Invoke(this, null); + Complete?.Invoke(); } } #endregion diff --git a/MetroSet UI/Controls/MetroSetCheckBox.cs b/MetroSet UI/Controls/MetroSetCheckBox.cs index 7968f30..7fa734a 100644 --- a/MetroSet UI/Controls/MetroSetCheckBox.cs +++ b/MetroSet UI/Controls/MetroSetCheckBox.cs @@ -137,7 +137,7 @@ public MetroSetCheckBox() _utl = new Utilites(); _animator = new IntAnimate(); _animator.Setting(100, 0, 255, EasingType.Linear); - _animator.Update += SetCheckedChanged; + _animator.Update = (alpha) => Invalidate(); ApplyTheme(); } @@ -292,16 +292,6 @@ private void DrawSymbol(Graphics g, Pen pen, SolidBrush solidBrush) public delegate void CheckedChangedEventHandler(object sender); - /// - /// The Method that increases and decreases the alpha of check symbol which it make the control animate. - /// - /// object - /// EventArgs - private void SetCheckedChanged(int value) - { - Invalidate(); - } - /// /// Here we will handle the checking state in runtime. /// diff --git a/MetroSet UI/Controls/MetroSetRadioButton.cs b/MetroSet UI/Controls/MetroSetRadioButton.cs index 1ba8aa3..4b8f380 100644 --- a/MetroSet UI/Controls/MetroSetRadioButton.cs +++ b/MetroSet UI/Controls/MetroSetRadioButton.cs @@ -136,7 +136,7 @@ public MetroSetRadioButton() _utl = new Utilites(); _animator = new IntAnimate(); _animator.Setting(100, 0, 255, EasingType.Linear); - _animator.Update += SetCheckedChanged; + _animator.Update = (alpha) => Invalidate(); ApplyTheme(); } @@ -275,16 +275,6 @@ protected override void OnPaint(PaintEventArgs e) public event CheckedChangedEventHandler CheckedChanged; public delegate void CheckedChangedEventHandler(object sender); - /// - /// The Method that increases and decreases the alpha of radio symbol which it make the control animate. - /// - /// object - /// EventArgs - private void SetCheckedChanged(int value) - { - Invalidate(); - } - /// /// Here we will handle the checking state in runtime. /// diff --git a/MetroSet UI/Controls/MetroSetSwitch.cs b/MetroSet UI/Controls/MetroSetSwitch.cs index dd50817..2fdb56c 100644 --- a/MetroSet UI/Controls/MetroSetSwitch.cs +++ b/MetroSet UI/Controls/MetroSetSwitch.cs @@ -133,7 +133,11 @@ public MetroSetSwitch() _utl = new Utilites(); _animator = new IntAnimate(); _animator.Setting(100, 0, 132, EasingType.Linear); - _animator.Update += SetCheckedChanged; + _animator.Update = (alpha) => + { + _switchlocation = alpha; + Invalidate(false); + }; ApplyTheme(); } @@ -276,17 +280,6 @@ protected override void OnPaint(PaintEventArgs e) public event SwitchedChangedEventHandler SwitchedChanged; - /// - /// The Method that increases and decreases the location symbol which it make the control animate. - /// - /// object - /// EventArgs - private void SetCheckedChanged(int value) - { - _switchlocation = value; - Invalidate(false); - } - /// /// Here we will handle the checking state in runtime. /// diff --git a/MetroSet UI/Controls/MetroSetTabControl.cs b/MetroSet UI/Controls/MetroSetTabControl.cs index 64e4499..d6e814d 100644 --- a/MetroSet UI/Controls/MetroSetTabControl.cs +++ b/MetroSet UI/Controls/MetroSetTabControl.cs @@ -136,7 +136,6 @@ public MetroSetTabControl() _mth = new Methods(); _utl = new Utilites(); _slideAnimator = new PointFAnimate(); - _slideAnimator.Update += OnSlideAnimateUpdate; ApplyTheme(); } @@ -457,11 +456,6 @@ protected override void WndProc(ref Message m) // Credits : Mavamaarten private int _oldIndex; - - private void OnSlideAnimateUpdate(PointF alpha) - { - _slideGraphics.DrawImage(_slideBitmap, alpha); - } private void DoSlideAnimate(TabPage control1, TabPage control2, bool moveback) { @@ -483,21 +477,24 @@ private void DoSlideAnimate(TabPage control1, TabPage control2, bool moveback) { c.Hide(); } - + + _slideAnimator.Update = (alpha) => + { + _slideGraphics.DrawImage(_slideBitmap, alpha); + }; + _slideAnimator.Complete = () => + { + SelectedTab = control2; + foreach (Control c in control2.Controls) + { + c.Show(); + } + }; _slideAnimator.Start( AnimateTime, new Point(moveback ? -control2.Width : 0, 0), new Point(moveback ? 0 : -control1.Width, 0), - AnimateEasingType, - // Complate action - (o, e) => - { - SelectedTab = control2; - foreach (Control c in control2.Controls) - { - c.Show(); - } - } + AnimateEasingType ); } From 97cf06b6efefb4d812d04abdc0bc0c8f983ccbd2 Mon Sep 17 00:00:00 2001 From: stu98832 Date: Thu, 1 Aug 2019 12:39:16 +0800 Subject: [PATCH 4/4] Fix TabPage was not drawn at first animation --- MetroSet UI/Controls/MetroSetTabControl.cs | 3 +++ MetroSet UI/Extensions/Utilites.cs | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/MetroSet UI/Controls/MetroSetTabControl.cs b/MetroSet UI/Controls/MetroSetTabControl.cs index d6e814d..db26b16 100644 --- a/MetroSet UI/Controls/MetroSetTabControl.cs +++ b/MetroSet UI/Controls/MetroSetTabControl.cs @@ -459,6 +459,9 @@ protected override void WndProc(ref Message m) private void DoSlideAnimate(TabPage control1, TabPage control2, bool moveback) { + // initialize control and child controls when control first painted + _utl.InitControlHandle(control1); + _utl.InitControlHandle(control2); _slideGraphics = Graphics.FromHwnd(control2.Handle); _slideBitmap = new Bitmap(control1.Width + control2.Width, control1.Height + control2.Height); diff --git a/MetroSet UI/Extensions/Utilites.cs b/MetroSet UI/Extensions/Utilites.cs index 6bf3268..2da9e21 100644 --- a/MetroSet UI/Extensions/Utilites.cs +++ b/MetroSet UI/Extensions/Utilites.cs @@ -24,6 +24,7 @@ using System.Drawing; using System.Drawing.Drawing2D; +using System.Windows.Forms; namespace MetroSet_UI.Extensions { @@ -116,5 +117,26 @@ public Color GetAlphaHexColor(int alpha, string hexColor) { return Color.FromArgb(alpha, ColorTranslator.FromHtml(hexColor)); } + + // Check and create handle of control + // Credits : + // control invalidate does not trigger the paint event of hidden or invisible control + // see https://stackoverflow.com/questions/38137654 + // force create handle + // see https://stackoverflow.com/questions/1807921/ + /// + /// Initialize the Handle of Control and child controls if their handle were not created + /// + public void InitControlHandle(Control ctrl) + { + if (!ctrl.IsHandleCreated) + { + var unused = ctrl.Handle; + foreach (Control child in ctrl.Controls) + { + InitControlHandle(child); + } + } + } } } \ No newline at end of file