diff --git a/CUEControls/CUEControls.csproj b/CUEControls/CUEControls.csproj index b7cf434..61ab018 100644 --- a/CUEControls/CUEControls.csproj +++ b/CUEControls/CUEControls.csproj @@ -52,6 +52,18 @@ FileSystemTreeView.cs + + UserControl + + + MediaSlider.cs + + + Component + + + PeakMeterCtrl.cs + True @@ -69,6 +81,7 @@ + diff --git a/CUEControls/MediaSlider.Designer.cs b/CUEControls/MediaSlider.Designer.cs new file mode 100644 index 0000000..6075dd4 --- /dev/null +++ b/CUEControls/MediaSlider.Designer.cs @@ -0,0 +1,46 @@ +namespace MediaSlider +{ + partial class MediaSlider + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // MediaSlider + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.Transparent; + this.Margin = new System.Windows.Forms.Padding(0); + this.Name = "MediaSlider"; + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/CUEControls/MediaSlider.cs b/CUEControls/MediaSlider.cs new file mode 100644 index 0000000..a04f1c5 --- /dev/null +++ b/CUEControls/MediaSlider.cs @@ -0,0 +1,4111 @@ +#region Author/About +/************************************************************************************ + * MediaSlider v1.3 * + * * + * Created: Febuary 7, 2010 * + * Built on: Win7 * + * Purpose: Animated slider control * + * Revision: 1.3 * + * Tested On: Win7 32bit, Vista 64bit, XP Professional * + * IDE: C# 2008 SP1 FW 3.5 * + * Referenced: Control Library VTD * + * Author: John Underhill (Steppenwolfe) * + * * + ************************************************************************************* + + You can not: + -Sell or redistribute this code or the binary for profit. + -Use this in spyware, malware, or any generally acknowledged form of malicious software. + -Remove or alter the above author accreditation, or this disclaimer. + + You can: + -Use this code in your applications in any way you like. + -Use this in a published program, (a credit to vtdev.com would be nice) + + I will not: + -Except any responsibility for this code whatsoever. + -Modify on demand.. you have the source code, read it, learn from it, write it. + -There is no guarantee of fitness, nor should you have any expectation of support. + -I further renounce any and all responsibilities for this code, in every way conceivable, + now, and for the rest of time. + + Updates to 1.1 + -fixed bug in incremental value + -added jump to position when track clicked w/ SmoothScrolling enabled + -fixed bug in ButtonSize property set + -fixed a couple of things in example form + + Updates to 1.2 + -Fixed false error condition on Value init + -Fixed ButtonSize property to update at design time + -Removed native properties that are incompatible with control + -Fixed control so that Minimum and Maximum values can both be negative -(but Maximum has to be more then Minimum) + + Updates to 1.3 + Fixed scrolling ceter pointer in button 'issue' + Fixed button resize bug + Fixed track resize bug + Fixed backwards scroll step bug + + Cheers, + John + steppenwolfe_2000@yahoo.com + */ +#endregion + +#region Directives +using System; +using System.Text; +using System.Timers; +using System.Windows.Forms; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Runtime.InteropServices; +using System.Diagnostics; +#endregion + +namespace MediaSlider +{ + [DefaultEvent("ValueChanged"), ToolboxBitmap(typeof(MediaSlider), "tbimage")] + public partial class MediaSlider : UserControl + { + #region Constants + private const int TrackMinDepth = 2; + private const int TrackMaxDepth = 6; + #endregion + + #region Enums + public enum AnimateSpeed : int + { + Fast = 1, + Normal = 5, + Slow = 20 + } + + public enum ButtonType : uint + { + Round = 0, + RoundedRectInline, + RoundedRectOverlap, + PointerUpRight, + PointerDownLeft, + Hybrid, + GlassInline, + GlassOverlap + } + + public enum FlyOutStyle : int + { + None = 0, + OnFocus, + Persistant + } + + public enum PresetType : uint + { + WmpVolume, + WmpTrackbar, + WmcTrackBar, + Office2007, + Glass + } + + public enum TickMode : int + { + Standard = 0, + Composite, + Precision, + LargeStepped + } + + public enum TrackType : uint + { + Progress = 0, + Value + } + + private enum PointDirection : int + { + Bottom = 0, + Right + } + private enum SliderSelectedState : uint + { + None = 0, + Disabled, + Focused, + MouseLeave, + Pressed, + Depressed, + Hover + } + + private enum ChangeType : uint + { + Large = 0, + Small + } + + private enum HitTest : uint + { + Nowhere = 0, + Button, + Track + } + #endregion + + #region Structs + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public RECT(int x, int y, int right, int bottom) + { + this.Left = x; + this.Top = y; + this.Right = right; + this.Bottom = bottom; + } + public int Left; + public int Top; + public int Right; + public int Bottom; + } + #endregion + + #region API + [DllImport("user32.dll")] + private static extern IntPtr GetDC(IntPtr handle); + + [DllImport("user32.dll")] + private static extern int ReleaseDC(IntPtr handle, IntPtr hdc); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); + + [DllImport("user32.dll")] + private static extern bool ValidateRect(IntPtr hWnd, ref RECT lpRect); + + [DllImport("user32.dll")] + private static extern bool GetClientRect(IntPtr hWnd, ref RECT r); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetCursorPos(ref Point lpPoint); + + [DllImport("user32.dll")] + private static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool PtInRect(ref RECT lprc, Point pt); + #endregion + + #region Fields + private bool _bHasInitialized = false; + private bool _bPulseTimerOn = false; + private bool _bAnimated = false; + private bool _bSmoothScrolling = false; + private bool _bTrackShadow = false; + private bool _bShowButtonOnHover = false; + private bool _bInTarget = false; + private bool _bPropInit = false; + private bool _bFinishedPropRun = false; + private int _iLargeChange = 0; + private int _iMaximum = 10; + private int _iMinimum = 0; + private int _iSmallChange = 0; + private int _iValue = 0; + private int _iTrackDepth = 6; + private int _iButtonCornerRadius = 4; + private int _iTrackPadding = 2; + private int _iTickMaxLength = 4; + private int _iTickMinPadding = 2; + private int _iFlyOutMaxDepth = 16; + private int _iFlyOutMaxWidth = 12; + private int _iFlyOutSpacer = 6; + private int _iLinePos = 0; + private double _dButtonPosition = 0; + private float _fAnimationSize = .1f; + + private Color _clrButtonAccentColor = Color.FromArgb(128, 64, 64, 64); + private Color _clrButtonBorderColor = Color.Black; + private Color _clrButtonColor = Color.FromArgb(160, 0, 0, 0); + private Color _clrBackColor = Color.Black; + private Color _clrTrackBorderColor = Color.FromArgb(160, Color.White); + private Color _clrTrackFillColor = Color.Transparent; + private Color _clrTrackProgressColor = Color.FromArgb(5, 101, 188); + private Color _clrTickColor = Color.DarkGray; + private Color _clrTrackShadowColor = Color.DarkGray; + + private Size _szButtonSize = new Size(14, 14); + private Size _szMinSize = new Size(0, 0); + private Size _szMaxSize = new Size(0, 0); + private ButtonType _eButtonStyle = ButtonType.Round; + private TrackType _eTrackStyle = TrackType.Value; + private SliderSelectedState _eSliderState = SliderSelectedState.None; + private Orientation _eOrientation = Orientation.Horizontal; + private TickMode _eTickType = TickMode.Standard; + private TickStyle _eTickStyle = TickStyle.None; + private FlyOutStyle _eFlyOutOnFocus = FlyOutStyle.None; + private AnimateSpeed _eAnimationSpeed = AnimateSpeed.Normal; + + private RECT _trackRect; + private RECT _buttonRect; + + private Bitmap _bmpBackground; + private Bitmap _bmpButton; + private Bitmap _bmpSprite; + private FadeTimer _cPulseTimer; + private cStoreDc _cControlDc; + private cStoreDc _cTrackDc; + private cStoreDc _cAnimationDc; + private ErrorProvider ErrorHandler; + + private delegate void ResetCallback(); + + [Description("Callback event for the FlyOut window")] + public event EventHandler FlyOutInfo; + #endregion + + #region Events and Delegates + [Description("Raised when the Slider Value property changes")] + public event EventHandler ValueChanged; + [Description("Raised when the mSlider has scrolled.")] + public event EventHandler Scrolled; + #endregion + + #region Constructor + public MediaSlider() + { + Init(); + InitializeComponent(); + } + + private void Init() + { + CreateGraphicsObjects(); + _bHasInitialized = true; + + if (this.DesignMode) + { + ErrorHandler = new ErrorProvider(); + this.Minimum = 0; + this.Maximum = 10; + this.SmallChange = 1; + this.LargeChange = 2; + } + _clrBackColor = Color.FromKnownColor(KnownColor.Control); + } + #endregion + + #region Destructor + private void DeInit() + { + _bHasInitialized = false; + DestroyGraphicsObjects(); + if (ErrorHandler != null) + ErrorHandler.Dispose(); + } + + public void Dispose() + { + DeInit(); + } + #endregion + + #region Properties + #region Hidden Properties + [Browsable(false)] + public new bool AllowDrop + { + get { return base.AllowDrop; } + set { base.AllowDrop = value; } + } + [Browsable(false)] + public new AnchorStyles Anchor + { + get { return base.Anchor; } + set { base.Anchor = value; } + } + [Browsable(false)] + public new bool AutoScroll + { + get { return base.AutoScroll; } + set { base.AutoScroll = value; } + } + [Browsable(false)] + public new Size AutoScrollMargin + { + get { return base.AutoScrollMargin; } + set { base.AutoScrollMargin = value; } + } + [Browsable(false)] + public new Size AutoScrollMinSize + { + get { return base.AutoScrollMinSize; } + set { base.AutoScrollMinSize = value; } + } + [Browsable(false)] + public new AutoSizeMode AutoSizeMode + { + get { return base.AutoSizeMode; } + set { base.AutoSizeMode = value; } + } + [Browsable(false)] + public new AutoValidate AutoValidate + { + get { return base.AutoValidate; } + set { base.AutoValidate = value; } + } + [Browsable(false)] + public new ImageLayout BackgroundImageLayout + { + get { return base.BackgroundImageLayout; } + set { base.BackgroundImageLayout = value; } + } + [Browsable(false)] + public new ContextMenu ContextMenuStrip + { + get { return base.ContextMenu; } + set { base.ContextMenu = value; } + } + [Browsable(false)] + public new DockStyle Dock + { + get { return base.Dock; } + set { base.Dock = value; } + } + [Browsable(false)] + public new Font Font + { + get { return base.Font; } + set { base.Font = value; } + } + [Browsable(false)] + public new Color ForeColor + { + get { return base.ForeColor; } + set { base.ForeColor = value; } + } + [Browsable(false)] + public new RightToLeft RightToLeft + { + get { return base.RightToLeft; } + set { base.RightToLeft = value; } + } + [Browsable(false)] + public new Padding Padding + { + get { return base.Padding; } + set { base.Padding = value; } + } + #endregion + + #region Private Properties + private int TrackPadding + { + get { return _iTrackPadding; } + set { _iTrackPadding = value; } + } + + private bool FinishedPropRun + { + get { return _bFinishedPropRun; } + set { _bFinishedPropRun = value; } + } + + private int FlyOutMaxWidth + { + get { return _iFlyOutMaxWidth; } + set { _iFlyOutMaxWidth = value; } + } + + private int FlyOutMaxDepth + { + get { return _iFlyOutMaxDepth; } + set { _iFlyOutMaxDepth = value; } + } + + private int FlyOutSpacer + { + get { return _iFlyOutSpacer; } + set { _iFlyOutSpacer = value; } + } + + private bool InTarget + { + get { return _bInTarget; } + set { _bInTarget = value; } + } + + private int TickMaxLength + { + get { return _iTickMaxLength; } + set { _iTickMaxLength = value; } + } + + private int TickMinPadding + { + get { return _iTickMinPadding; } + set { _iTickMinPadding = value; } + } + #endregion + + #region Public Properties + /// Run the animation effect when focused + [Browsable(true), Category("Appearence"), + Description("Run the animation effect when focused")] + public bool Animated + { + get { return _bAnimated; } + set + { + _bAnimated = value; + if (!this.DesignMode) + { + if (this.Focused && _bAnimated) + StartPulseTimer(); + else + StopPulseTimer(); + DrawSlider(); + } + } + } + + /// Animation cycle speed + [Browsable(true), Category("Appearence"), + Description("Animation cycle speed")] + public AnimateSpeed AnimationSpeed + { + get { return _eAnimationSpeed; } + set { _eAnimationSpeed = value; } + } + + /// Percentage of size of sprite height/width to track height/width [min .05 - max .2] + [Browsable(false), + Description("Percentage of size of sprite width to track width [min .05 - max .2]")] + public float AnimationSize + { + get { return _fAnimationSize; } + set + { + if (value < .02f) + _fAnimationSize = .02f; + else if (value < .2f) + _fAnimationSize = .2f; + else + _fAnimationSize = value; + } + } + + /// Use an image for the slider background + [Browsable(true), Category("Appearence"), + Description("Use an image for the slider background")] + public new Bitmap BackgroundImage + { + get { return _bmpBackground; } + set + { + try + { + if (value != null && value.GetType() == typeof(Bitmap)) + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _bmpBackground = value; + base.Width = _bmpBackground.Width; + base.Height = _bmpBackground.Height; + PropertyChange(); + } + else if (value != null && this.DesignMode) + { + throw new Exception("Invalid BackGroundImage Property Setting: Invalid image type. Base of image must be a Bitmap"); + } + else if (value == null) + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + if (_bmpBackground != null) + _bmpBackground.Dispose(); + _bmpBackground = null; + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + } + } + + /// Modify button accent color + [Browsable(true), Category("Appearence"), + Description("Modify button accent color")] + public Color ButtonAccentColor + { + get { return _clrButtonAccentColor; } + set + { + _clrButtonAccentColor = value; + PropertyChange(); + } + } + + /// Modify button border color + [Browsable(true), Category("Appearence"), + Description("Modify button border color")] + public Color ButtonBorderColor + { + get { return _clrButtonBorderColor; } + set + { + _clrButtonBorderColor = value; + PropertyChange(); + } + } + + /// Modify button base color + [Browsable(true), Category("Appearence"), + Description("Modify button base color")] + public Color ButtonColor + { + get { return _clrButtonColor; } + set + { + _clrButtonColor = value; + PropertyChange(); + } + } + + /// Adjusts the slider buttons corner radius + [Browsable(false), + Description("Adjusts the slider buttons corner radius")] + public uint ButtonCornerRadius + { + get { return (uint)_iButtonCornerRadius; } + set { _iButtonCornerRadius = (int)value; } + } + + /// Modify slider button size + [Browsable(true), Category("Appearence"), RefreshProperties(RefreshProperties.All), + Description("Modify slider button size")] + public Size ButtonSize + { + get { return _szButtonSize; } + set + { + _szButtonSize = value; + if (this.DesignMode && !this.AutoSize) + PropertyChange(); + } + } + + /// Set the button style + [Browsable(true), Category("Appearence"), RefreshProperties(RefreshProperties.All), + Description("Set the button style")] + public ButtonType ButtonStyle + { + get { return _eButtonStyle; } + set + { + + if (this.FinishedPropRun && this.DesignMode && _eButtonStyle != value) + { + _eButtonStyle = value; + DefaultButtonSize(_eButtonStyle); + } + else + { + _eButtonStyle = value; + } + PropertyChange(); + } + } + + /// Returns the property initiated state + [Browsable(false), + Description("Returns the property initiated state")] + public bool IsInited + { + get { return this.Visible && _bPropInit; } + private set { _bPropInit = this.Visible && value; } + } + + /// The number of clicks the slider moves in response to mouse clicks or pageup/pagedown + [Browsable(true), Category("Behavior"), + Description("The number of clicks the slider moves in response to mouse clicks or pageup/pagedown")] + public int LargeChange + { + get { return _iLargeChange; } + set + { + try + { + if (value < 1 && this.DesignMode && this.FinishedPropRun) + { + throw new Exception("Invalid LargeChange Property Setting: Large change can not be less then 1"); + } + else + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _iLargeChange = value; + PropertyChange(); + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + } + } + + /// The maximum value for the position of the slider + [Browsable(true), Category("Behavior"), + Description("The maximum value for the position of the slider")] + public int Maximum + { + get { return _iMaximum; } + set + { + try + { + if (value <= this.Minimum && this.FinishedPropRun) + { + if (this.DesignMode) + throw new Exception("Invalid Maximum Property Setting: Maximum can not be less then the Minimum value setting"); + } + else + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _iMaximum = value; + PropertyChange(); + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + finally + { + if (!this.DesignMode) + SliderFlyOut = _eFlyOutOnFocus; + } + } + } + + /// The maximum Size value for the control + [Browsable(true), Category("Behavior"), + Description("The maximum Size value for the control [private set]")] + public Size MaxSize + { + get { return _szMaxSize; } + private set { _szMaxSize = value; } + } + + /// The minimum value for the position of the slider + [Browsable(true), Category("Behavior"), + Description("The minimum value for the position of the slider")] + public int Minimum + { + get { return _iMinimum; } + set + { + try + { + if (value >= this.Maximum && this.FinishedPropRun) + { + if (this.DesignMode) + throw new Exception("Invalid Minimum Property Setting: Minimum can not be more then the Maximum value setting"); + } + else + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _iMinimum = value; + PropertyChange(); + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + } + } + + /// The minimum Size value for the control + [Browsable(true), Category("Behavior"), + Description("The minimum Size value for the control [private set]")] + public Size MinSize + { + get { return _szMinSize; } + private set { _szMinSize = value; } + } + + /// The orientation of the control + [Browsable(true), Category("Appearence"), RefreshProperties(RefreshProperties.All), + Description("The orientation of the control")] + public Orientation Orientation + { + get { return _eOrientation; } + set + { + _eOrientation = value; + if (this.FinishedPropRun && this.DesignMode && _eOrientation != value) + { + _eOrientation = value; + DefaultButtonSize(_eButtonStyle); + } + else + { + _eOrientation = value; + } + PropertyChange(); + } + } + + /// Returns the slider position to a floating point, requires SmoothScrolling set to true + [Browsable(false), + Description("Returns the slider position to a floating point, requires SmoothScrolling set to true")] + public double PrecisionValue + { + get { return IncrementalValue(); } + } + + /// Show the slider button only when control is focused or mouse is hovering + [Browsable(true), Category("Appearence"), + Description("Show the slider button only when control is focused or mouse is hovering")] + public bool ShowButtonOnHover + { + get { return _bShowButtonOnHover; } + set { _bShowButtonOnHover = value; } + } + + /// Enable the flyout caption window + [Browsable(true), Category("Appearence"), + Description("Enable the flyout caption window")] + public FlyOutStyle SliderFlyOut + { + get { return _eFlyOutOnFocus; } + set + { + _eFlyOutOnFocus = value; + if (_eFlyOutOnFocus != FlyOutStyle.None) + { + if (Orientation == Orientation.Horizontal) + { + this.FlyOutMaxDepth = 14; + this.FlyOutSpacer = 6; + if (this.Maximum < 10) + { + this.FlyOutMaxWidth = 10; + this.TrackPadding = 4; + } + else if (this.Maximum < 100) + { + this.FlyOutMaxWidth = 20; + this.TrackPadding = 6; + } + else if (this.Maximum < 1000) + { + this.FlyOutMaxWidth = 30; + this.TrackPadding = 12; + } + else if (this.Maximum > 999) + { + // probably time + this.FlyOutMaxWidth = 54; + this.TrackPadding = 24; + } + } + else + { + if (this.Minimum < 0) + { + // max 2 digit vertical + this.FlyOutSpacer = 2; + this.FlyOutMaxDepth = 30; + this.FlyOutMaxWidth = 20; + this.TrackPadding = 12; + } + else + { + // max 2 digit vertical + this.FlyOutSpacer = 2; + this.FlyOutMaxDepth = 20; + this.FlyOutMaxWidth = 18; + this.TrackPadding = 5; + } + } + } + else + { + this.FlyOutMaxDepth = 0; + this.TrackPadding = 2; + } + PropertyChange(); + } + } + + /// The number of positions the slider movers in response to arrow keys + [Browsable(true), Category("Behavior"), + Description("The number of positions the slider movers in response to arrow keys")] + public int SmallChange + { + get { return _iSmallChange; } + set + { + try + { + if (value < 1 && this.DesignMode && this.FinishedPropRun) + { + throw new Exception("Invalid SmallChange Property Setting: Small change can not be less then 1"); + } + else + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _iSmallChange = value; + PropertyChange(); + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + } + } + + /// Run the animation effect when focused + [Browsable(true), Category("Behavior"), + Description("Enable smooth scrolling style")] + public bool SmoothScrolling + { + get { return _bSmoothScrolling; } + set { _bSmoothScrolling = value; } + } + + /// Modify slider tick color + [Browsable(true), Category("Appearence"), + Description("Modify slider tick color")] + public Color TickColor + { + get { return _clrTickColor; } + set + { + _clrTickColor = value; + PropertyChange(); + } + } + + /// Select the tickstyle + [Browsable(true), Category("Appearence"), + Description("Select the tickstyle")] + public TickStyle TickStyle + { + get { return _eTickStyle; } + set + { + _eTickStyle = value; + PropertyChange(); + } + } + + /// Select the tick drawing style + [Browsable(true), Category("Appearence"), + Description("Select the tick drawing style")] + public TickMode TickType + { + get { return _eTickType; } + set + { + _eTickType = value; + PropertyChange(); + } + } + + /// Modify slider border color + [Browsable(true), Category("Appearence"), + Description("Modify slider border color")] + public Color TrackBorderColor + { + get { return _clrTrackBorderColor; } + set + { + _clrTrackBorderColor = value; + PropertyChange(); + } + } + + /// Adjust the slider track depth + [Browsable(true), Category("Appearence"), RefreshProperties(RefreshProperties.All), + Description("Adjust the slider track depth")] + public int TrackDepth + { + get { return _iTrackDepth; } + set + { + _iTrackDepth = value; + if (this.DesignMode && !this.AutoSize) + PropertyChange(); + } + } + + /// Set the track fill color + [Browsable(true), Category("Appearence"), + Description("Set the track fill color")] + public Color TrackFillColor + { + get { return _clrTrackFillColor; } + set + { + _clrTrackFillColor = value; + PropertyChange(); + } + } + + /// Set the track progress color + [Browsable(true), Category("Appearence"), + Description("Set the track progress color")] + public Color TrackProgressColor + { + get { return _clrTrackProgressColor; } + set + { + _clrTrackProgressColor = value; + PropertyChange(); + } + } + + /// Enable track border shadow + [Browsable(true), Category("Appearence"), + Description("Enable track shadow")] + public bool TrackShadow + { + get { return _bTrackShadow; } + set + { + _bTrackShadow = value; + PropertyChange(); + } + } + + /// Modify track shadow color + [Browsable(true), Category("Appearence"), + Description("Modify track shadow color")] + public Color TrackShadowColor + { + get { return _clrTrackShadowColor; } + set + { + _clrTrackShadowColor = value; + PropertyChange(); + } + } + + /// Modify the display style of track + [Browsable(true), Category("Appearence"), + Description("Modify the display style of track")] + public TrackType TrackStyle + { + get { return _eTrackStyle; } + set + { + _eTrackStyle = value; + PropertyChange(); + } + } + + /// The position of the slider + [Browsable(true), Category("Behavior"), + Description("The position of the slider")] + public int Value + { + get { return _iValue; } + set + { + this.IsInited = !this.DesignMode; + try + { + if (value > this.Maximum) + { + if (this.DesignMode && this.FinishedPropRun) + throw new Exception("Invalid Value Property Setting: Value can not be more then Maximum setting"); + else + _iValue = this.Maximum; + } + else if (value < this.Minimum) + { + if (this.DesignMode && this.FinishedPropRun) + throw new Exception("Invalid Value Property Setting: Value can not be less then Minimum setting"); + else + _iValue = this.Minimum; + } + else + { + if (this.ErrorHandler != null) + this.ErrorHandler.Clear(); + _iValue = value; + } + + if (this.DesignMode) + _dButtonPosition = IncrementalValue(); + + if (!this.FinishedPropRun) + { + PropertyChange(); + this.FinishedPropRun = true; + } + else if (!this.DesignMode) + { + _dButtonPosition = IncrementalValue(); + if (ValueChanged != null) + ValueChanged(this, new EventArgs()); + DrawSlider(); + } + } + catch (Exception ex) { this.ErrorHandler.SetError(this, ex.Message); } + } + } + #endregion + #endregion + + #region Overrides + protected override void OnCreateControl() + { + base.OnCreateControl(); + } + + protected override void OnHandleCreated(EventArgs e) + { + Init(); + base.OnHandleCreated(e); + } + + protected override void OnHandleDestroyed(EventArgs e) + { + DeInit(); + base.OnHandleDestroyed(e); + } + + protected override void OnPaint(PaintEventArgs e) + { + if (_bHasInitialized) + DrawSlider(); + base.OnPaint(e); + } + + protected override void OnBackColorChanged(EventArgs e) + { + Init(); + base.OnBackColorChanged(e); + } + + protected override void OnGotFocus(EventArgs e) + { + if (!this.InTarget) + { + _eSliderState = SliderSelectedState.Focused; + DrawSlider(); + } + base.OnGotFocus(e); + } + + protected override void OnLostFocus(EventArgs e) + { + _eSliderState = SliderSelectedState.None; + DrawSlider(); + base.OnLostFocus(e); + } + + protected override void OnMouseClick(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + HitTest tst = SliderHitTest(); + if (tst == HitTest.Track || tst == HitTest.Nowhere) + { + int pos; + if (Orientation == Orientation.Horizontal) + pos = e.X; + else + pos = e.Y; + if (this.SmoothScrolling) + { + ScrollThis(pos); + } + else + { + if (pos < IncrementalValue()) + ScrollChange(ChangeType.Large, (Orientation == Orientation.Horizontal)); + else + ScrollChange(ChangeType.Large, (Orientation != Orientation.Horizontal)); + } + } + } + base.OnMouseClick(e); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + this.InTarget = (SliderHitTest() == HitTest.Button); + if (this.InTarget) + _eSliderState = SliderSelectedState.Pressed; + else + _eSliderState = SliderSelectedState.Focused; + DrawSlider(); + base.OnMouseDown(e); + } + + protected override void OnMouseHover(EventArgs e) + { + _eSliderState = SliderSelectedState.Hover; + DrawSlider(); + base.OnMouseHover(e); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + _eSliderState = SliderSelectedState.Depressed; + _bInTarget = false; + DrawSlider(); + base.OnMouseUp(e); + } + + protected override void OnMouseLeave(EventArgs e) + { + _eSliderState = SliderSelectedState.MouseLeave; + DrawSlider(); + base.OnMouseLeave(e); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && _bInTarget) + { + if (Orientation == Orientation.Horizontal) + ScrollThis(e.X); + else + ScrollThis(e.Y); + } + base.OnMouseMove(e); + } + + protected override void OnResize(EventArgs e) + { + PropertyChange(); + base.OnResize(e); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + switch (keyData) + { + case Keys.Left: + ScrollChange(ChangeType.Small, true); + DrawSlider(); + return true; + case Keys.Right: + ScrollChange(ChangeType.Small, false); + DrawSlider(); + return true; + case Keys.Up: + ScrollChange(ChangeType.Small, false); + DrawSlider(); + return true; + case Keys.Down: + ScrollChange(ChangeType.Small, true); + DrawSlider(); + return true; + case Keys.Home: + this.Value = this.Minimum; + return true; + case Keys.End: + this.Value = this.Maximum; + return true; + } + return base.ProcessCmdKey(ref msg, keyData); + } + #endregion + + #region Methods + #region Graphics + #region Drawing + /// Drawing hub + private void DrawSlider() + { + Rectangle bounds = new Rectangle(0, 0, this.Width, this.Height); + using (Graphics g = Graphics.FromHdc(_cControlDc.Hdc)) + { + DrawTrack(); + if (this.SliderFlyOut == FlyOutStyle.Persistant) + DrawFlyOut(g); + + switch (_eSliderState) + { + case SliderSelectedState.None: + { + if (_bPulseTimerOn) + StopPulseTimer(); + if (!ShowButtonOnHover) + DrawButton(g, bounds, 1f); + break; + } + case SliderSelectedState.Disabled: + { + DrawButton(g, bounds, -1f); + break; + } + case SliderSelectedState.MouseLeave: + { + if (!ShowButtonOnHover || _bPulseTimerOn) + { + if (this.Focused) + DrawButton(g, bounds, 1.2f); + else + DrawButton(g, bounds, 1f); + } + break; + } + case SliderSelectedState.Hover: + { + if (this.SliderFlyOut == FlyOutStyle.OnFocus) + DrawFlyOut(g); + DrawButton(g, bounds, 1.2f); + break; + } + case SliderSelectedState.Depressed: + case SliderSelectedState.Focused: + { + if (!_bPulseTimerOn) + { + if (this.SliderFlyOut == FlyOutStyle.OnFocus) + DrawFlyOut(g); + DrawButton(g, bounds, 1.2f); + if (this.Animated) + StartPulseTimer(); + } + else if (!this.InTarget) + { + DrawButton(g, bounds, 1.0f); + } + break; + } + case SliderSelectedState.Pressed: + { + if (_bPulseTimerOn) + StopPulseTimer(); + if (this.SliderFlyOut == FlyOutStyle.OnFocus) + DrawFlyOut(g); + DrawButton(g, bounds, .9f); + break; + } + } + } + + if (!_bPulseTimerOn) + { + // draw buffer to control + using (Graphics g = Graphics.FromHwnd(this.Handle)) + { + BitBlt(g.GetHdc(), 0, 0, _cControlDc.Width, _cControlDc.Height, _cControlDc.Hdc, 0, 0, 0xCC0020); + g.ReleaseHdc(); + } + RECT r = new RECT(0, 0, this.Width, this.Height); + ValidateRect(this.Handle, ref r); + } + } + + /// Backfill the buffer + private void DrawBackGround(Graphics g, Rectangle bounds) + { + using (Brush br = new SolidBrush(this.BackColor)) + g.FillRectangle(br, bounds); + } + + /// Adjust gamma of an image [not used] + private void DrawBrightImage(Graphics g, Image img, Rectangle bounds, float gamma) + { + try + { + using (Bitmap buttonImage = new Bitmap(img)) + { + using (ImageAttributes imageAttr = new ImageAttributes()) + { + if (gamma > .9f) + gamma = .9f; + if (gamma < .2f) + gamma = .2f; + // raise gamma + imageAttr.SetGamma(gamma); + g.DrawImage(buttonImage, + bounds, + 0, 0, + buttonImage.Width, + buttonImage.Height, + GraphicsUnit.Pixel, + imageAttr); + } + } + } + catch { } + } + + private void DrawButton(Graphics g, Rectangle bounds, float level) + { + Rectangle buttonRect = GetButtonRectangle(); + if (level != 1f) + { + using (ImageAttributes ia = new ImageAttributes()) + { + ColorMatrix cm = new ColorMatrix(); + if (level == -1) + { + cm.Matrix00 = 1f;//r + cm.Matrix11 = 1f;//g + cm.Matrix22 = 1f;//b + cm.Matrix33 = .7f;//a + cm.Matrix44 = 1f;//w + } + else + { + cm.Matrix00 = level; + cm.Matrix11 = level; + cm.Matrix22 = level; + cm.Matrix33 = 1f; + cm.Matrix44 = 1f; + } + ia.SetColorMatrix(cm); + g.DrawImage(_bmpButton, buttonRect, 0, 0, _bmpButton.Width, _bmpButton.Height, GraphicsUnit.Pixel, ia); + } + } + else + { + DrawImage(g, _bmpButton, buttonRect); + } + } + + /// Draw a disabled image using the control + private void DrawDisabledImage(Graphics g, Image image, Rectangle bounds) + { + ControlPaint.DrawImageDisabled(g, image, bounds.X, bounds.Y, Color.Transparent); + } + + /// Draw an unaltered image + private void DrawImage(Graphics g, Image image, Rectangle bounds) + { + g.DrawImage(image, bounds); + } + + /// Draw the slider ticks + private void DrawTicks(Graphics g, Rectangle bounds) + { + Rectangle trackRect = GetTrackRectangle(); + float increment = (float)Increment(); + increment = (float)Increment(); + int count = (int)(IncrementScale()); + + float endcap = (Orientation == Orientation.Horizontal ? (float)trackRect.Right - (1 + this.ButtonSize.Width / 2) : (float)trackRect.Bottom - (1 + this.ButtonSize.Height / 2)); + float offset = 0; + int shadowlen = this.TickMaxLength - 1; + int spacer = this.TickMaxLength + this.TickMinPadding; + RectangleF buttonRect = GetButtonRectangle(); + + switch (this.TickType) + { + #region Composite Style + case TickMode.Composite: + { + using (GraphicsMode md = new GraphicsMode(g, SmoothingMode.None)) + { + switch (this.TickStyle) + { + case TickStyle.Both: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + } + else + { + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + } + else + { + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + } + } + } + } + break; + } + case TickStyle.BottomRight: + { + if (this.Orientation == Orientation.Horizontal) + { + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + } + else + { + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + } + } + } + } + else + { + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + } + else + { + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + } + } + } + } + break; + } + case TickStyle.TopLeft: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + } + else + { + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + } + else + { + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + } + } + } + } + break; + } + } + } + break; + } + #endregion + + #region Large Stepped Style + case TickMode.LargeStepped: + { + using (GraphicsMode md = new GraphicsMode(g, SmoothingMode.None)) + { + switch (this.TickStyle) + { + case TickStyle.Both: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + if (Mod(i, this.LargeChange)) + { + val = (increment * i) + offset; + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + if (Mod(i, this.LargeChange)) + { + val = (increment * i) + offset; + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + } + } + } + } + break; + } + case TickStyle.BottomRight: + { + if (this.Orientation == Orientation.Horizontal) + { + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + if (Mod(i, this.LargeChange)) + { + val = (increment * i) + offset; + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + } + } + } + } + else + { + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + } + } + } + } + break; + } + case TickStyle.TopLeft: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + } + } + } + } + break; + } + } + } + break; + } + #endregion + + #region Precision Style + case TickMode.Precision: + { + using (GraphicsMode md = new GraphicsMode(g, SmoothingMode.None)) + { + float split = increment * .5f; + bool valid = split > 2; + switch (this.TickStyle) + { + case TickStyle.Both: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + if (valid && val < endcap) + { + g.DrawLine(pn, new PointF(val + split, top), new PointF(val + split, top + 1)); + g.DrawLine(pn, new PointF(val + split, bottom), new PointF(val + split, bottom - 1)); + } + } + else + { + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + if (valid && val < endcap) + { + g.DrawLine(pn, new PointF(val + split, top), new PointF(val + split, top + 1)); + g.DrawLine(pn, new PointF(val + split, bottom), new PointF(val + split, bottom - 1)); + } + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + if (valid && val < endcap) + { + g.DrawLine(pn, new PointF(left, val + split), new PointF(left + 1, val + split)); + g.DrawLine(pn, new PointF(right, val + split), new PointF(right - 1, val + split)); + } + } + else + { + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + if (valid && val < endcap) + { + g.DrawLine(pn, new PointF(left, val + split), new PointF(left + 1, val + split)); + g.DrawLine(pn, new PointF(right, val + split), new PointF(right - 1, val + split)); + } + } + } + } + } + break; + } + case TickStyle.BottomRight: + { + if (this.Orientation == Orientation.Horizontal) + { + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, bottom), new PointF(val, bottom - this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, bottom), new PointF(val + 1, bottom - shadowlen)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(val + split, bottom), new PointF(val + split, bottom - 1)); + } + else + { + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(val + split, bottom), new PointF(val + split, bottom - 1)); + } + } + } + } + else + { + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(right, val), new PointF(right - this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(right, val + 1), new PointF(right - shadowlen, val + 1)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(right, val + split), new PointF(right - 2, val + split)); + } + else + { + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(right, val + split), new PointF(right - 2, val + split)); + } + } + } + } + break; + } + case TickStyle.TopLeft: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(val, top), new PointF(val, top + this.TickMaxLength)); + g.DrawLine(pn3, new PointF(val + 1, top + 1), new PointF(val + 1, top + shadowlen)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(val + split, top), new PointF(val + split, top + 1)); + } + else + { + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(val + split, top), new PointF(val + split, top + 1)); + } + } + } + } + else + { + float left = buttonRect.Left - spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + if (Mod(i, this.LargeChange)) + { + g.DrawLine(pn2, new PointF(left, val), new PointF(left + this.TickMaxLength, val)); + g.DrawLine(pn3, new PointF(left + 1, val + 1), new PointF(left + shadowlen, val + 1)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(left, val + split), new PointF(left + 2, val + split)); + } + else + { + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + if (valid && val < endcap) + g.DrawLine(pn, new PointF(left, val + split), new PointF(left + 2, val + split)); + } + } + } + } + break; + } + } + } + break; + } + #endregion + + #region Standard Tick Style + case TickMode.Standard: + { + using (GraphicsMode md = new GraphicsMode(g, SmoothingMode.None)) + { + switch (this.TickStyle) + { + case TickStyle.Both: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + } + } + } + else + { + float left = buttonRect.Left - spacer; + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + } + } + } + break; + } + case TickStyle.BottomRight: + { + if (this.Orientation == Orientation.Horizontal) + { + float bottom = buttonRect.Bottom + spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(val, bottom), new PointF(val, bottom - 2)); + } + } + } + else + { + float right = buttonRect.Right + spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(right, val), new PointF(right - 2, val)); + } + } + } + break; + } + case TickStyle.TopLeft: + { + if (this.Orientation == Orientation.Horizontal) + { + float top = buttonRect.Top - spacer; + offset = (this.ButtonSize.Width / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(val, top), new PointF(val, top + 2)); + } + } + } + else + { + float left = buttonRect.Left - spacer; + offset = (this.ButtonSize.Height / 2) + this.TrackPadding; + float val = offset; + using (Pen pn = new Pen(this.TickColor, .5f), pn2 = new Pen(this.TickColor, 1f), pn3 = new Pen(Color.FromArgb(100, Color.DarkGray), 1f)) + { + for (int i = 0; i < count + 1; i++) + { + val = (increment * i) + offset; + g.DrawLine(pn, new PointF(left, val), new PointF(left + 2, val)); + } + } + } + break; + } + } + } + break; + } + #endregion + } + } + + /// Draw slider and background dc + private void DrawTrack() + { + BitBlt(_cControlDc.Hdc, 0, 0, _cTrackDc.Width, _cTrackDc.Height, _cTrackDc.Hdc, 0, 0, 0xCC0020); + if (TrackStyle == TrackType.Progress) + { + Rectangle trackRect = GetTrackRectangle(); + Rectangle buttonRect = GetButtonRectangle(); + int length; + + trackRect.Inflate(-1, -1); + if (Orientation == Orientation.Horizontal) + { + if (_iValue == _iMinimum) + { + length = 0; + } + else if (_iValue == _iMaximum) + { + if (this.SmoothScrolling) + { + length = buttonRect.Right - (trackRect.Left + 1); + trackRect.Width = length; + } + else + { + length = buttonRect.Right - (trackRect.Left + 2); + trackRect.Width = length; + } + } + else + { + length = buttonRect.Right - (trackRect.Left + (int)(buttonRect.Width * .5f)); + trackRect.Width = length; + } + } + else + { + if (_iValue == _iMinimum) + { + length = 0; + } + else if (_iValue == _iMaximum) + { + if (this.SmoothScrolling) + { + length = trackRect.Bottom - (buttonRect.Top + 1); + trackRect.Y = buttonRect.Top - 1; + trackRect.Height = length; + } + else + { + length = trackRect.Bottom - (buttonRect.Top + 3); + trackRect.Height = length; + } + } + else + { + length = trackRect.Bottom - (buttonRect.Top + (int)(buttonRect.Height * .5f)); + trackRect.Y = buttonRect.Top + (int)(buttonRect.Height * .5f) - 2; + trackRect.Height = length; + } + } + if (length > 1) + { + using (Graphics g = Graphics.FromHdc(_cControlDc.Hdc)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = CreateRoundRectanglePath(g, trackRect, 2)) + { + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + buttonRect, + Color.FromArgb(120, Color.White), + Color.FromArgb(250, this.TrackProgressColor), + (Orientation == Orientation.Horizontal) ? LinearGradientMode.Vertical : LinearGradientMode.Horizontal)) + { + Blend blnd = new Blend(); + blnd.Positions = new float[] { 0f, .5f, 1f }; + blnd.Factors = new float[] { .5f, .7f, .3f }; + fillBrush.Blend = blnd; + g.FillPath(fillBrush, gp); + } + } + } + } + } + } + } + #endregion + + #region Graphics Creation + /// Create the button bitmap + private void CreateButtonBitmap() + { + Rectangle buttonRect = GetButtonRectangle(); + float fx; + float fy; + + buttonRect.X = 0; + buttonRect.Y = 0; + Rectangle accentRect = buttonRect; + + _bmpButton = new Bitmap(buttonRect.Width + 1, buttonRect.Height + 1); + + switch (this.ButtonStyle) + { + #region Precision + case ButtonType.PointerUpRight: + case ButtonType.PointerDownLeft: + { + using (Graphics g = Graphics.FromImage(_bmpButton)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + int offset = (int)(buttonRect.Height * .2); + buttonRect.Inflate(0, -offset); + buttonRect.Y = 0; + + using (GraphicsPath gp = CreatePointedRectangularPath(g, buttonRect, PointDirection.Bottom, 3, offset * 2, -1)) + { + using (Brush br = new SolidBrush(Color.LightGray)) + g.FillPath(br, gp); + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + buttonRect, + this.ButtonAccentColor, + this.ButtonColor, + LinearGradientMode.Horizontal)) + { + Blend blnd = new Blend(); + blnd.Positions = new float[] { 0f, .5f, 1f }; + blnd.Factors = new float[] { .2f, .8f, .2f }; + fillBrush.Blend = blnd; + g.FillPath(fillBrush, gp); + } + using (Pen borderPen = new Pen(Color.FromArgb(180, this.ButtonBorderColor), .5f)) + g.DrawPath(borderPen, gp); + } + } + } + if (this.ButtonStyle == ButtonType.PointerUpRight) + { + if (this.ButtonStyle == ButtonType.PointerUpRight) + _bmpButton.RotateFlip(RotateFlipType.Rotate180FlipX); + } + if (Orientation == Orientation.Vertical) + _bmpButton.RotateFlip(RotateFlipType.Rotate90FlipNone); + break; + } + #endregion + + #region Round + // round button style + case ButtonType.Round: + { + using (Graphics g = Graphics.FromImage(_bmpButton)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddEllipse(buttonRect); + // fill with base color + using (Brush br = new SolidBrush(Color.FromArgb(255, this.ButtonColor))) + g.FillPath(br, gp); + // add top sheen + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + buttonRect, + Color.FromArgb(180, Color.White), + this.ButtonColor, + LinearGradientMode.Vertical)) + { + Blend blnd = new Blend(); + blnd.Positions = new float[] { 0f, .1f, .2f, .3f, .6f, 1f }; + blnd.Factors = new float[] { .2f, .3f, .4f, .5f, 1f, 1f }; + fillBrush.Blend = blnd; + g.FillPath(fillBrush, gp); + } + // add the bottom glow + using (PathGradientBrush borderBrush = new PathGradientBrush(gp)) + { + using (GraphicsPath ga = new GraphicsPath()) + { + accentRect.Inflate(0, (int)-(accentRect.Height * .2f)); + accentRect.Offset(0, (int)(ButtonSize.Width * .2f)); + ga.AddEllipse(accentRect); + // center focus + fx = accentRect.Width * .5f; + fy = accentRect.Height * 1f; + borderBrush.CenterColor = this.ButtonColor; + borderBrush.SurroundColors = new Color[] { this.ButtonAccentColor }; + borderBrush.FocusScales = new PointF(1f, 0f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, ga); + } + // spotight offsets + fx = buttonRect.Width * .2f; + fy = buttonRect.Height * .05f; + // draw the spotlight + borderBrush.CenterColor = Color.FromArgb(120, Color.White); + borderBrush.SurroundColors = new Color[] { Color.FromArgb(5, Color.Silver) }; + borderBrush.FocusScales = new PointF(.2f, .2f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, gp); + } + // draw the border + using (Pen borderPen = new Pen(this.ButtonBorderColor, .5f)) + g.DrawPath(borderPen, gp); + } + } + } + break; + } + #endregion + + #region Hybrid + case ButtonType.Hybrid: + { + using (Graphics g = Graphics.FromImage(_bmpButton)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddEllipse(buttonRect); + using (PathGradientBrush borderBrush = new PathGradientBrush(gp)) + { + // center focus + fx = buttonRect.Width * .5f; + fy = buttonRect.Height * .5f; + borderBrush.CenterColor = this.ButtonColor; + borderBrush.SurroundColors = new Color[] { this.ButtonAccentColor }; + borderBrush.FocusScales = new PointF(.5f, .5f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, gp); + + }// draw the border + using (Pen borderPen = new Pen(this.ButtonBorderColor, .5f)) + g.DrawPath(borderPen, gp); + } + } + } + break; + } + #endregion + + #region Rounded Rectangle + case ButtonType.RoundedRectInline: + case ButtonType.RoundedRectOverlap: + { + using (Graphics g = Graphics.FromImage(_bmpButton)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = CreateRoundRectanglePath(g, buttonRect, this.ButtonCornerRadius)) + { + // fill with solid base color + using (Brush br = new SolidBrush(Color.FromArgb(255, this.ButtonColor))) + g.FillPath(br, gp); + fx = buttonRect.Width * .5f; + fy = buttonRect.Height * .5f; + // add a shine + LinearGradientMode md; + if (Orientation == Orientation.Horizontal) + { + if (this.ButtonStyle == ButtonType.RoundedRectOverlap) + md = LinearGradientMode.Horizontal; + else + md = LinearGradientMode.Vertical; + } + else + { + if (this.ButtonStyle == ButtonType.RoundedRectOverlap) + md = LinearGradientMode.Vertical; + else + md = LinearGradientMode.Horizontal; + } + // draw it + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + buttonRect, + Color.FromArgb(120, Color.White), + Color.FromArgb(5, Color.Silver), + md)) + { + Blend blnd = new Blend(); + blnd.Positions = new float[] { 0f, .2f, .4f, .7f, .8f, 1f }; + blnd.Factors = new float[] { .2f, .4f, .5f, .4f, .2f, .1f }; + fillBrush.Blend = blnd; + g.FillPath(fillBrush, gp); + } + // draw the border + using (Pen borderPen = new Pen(Color.FromArgb(220, this.ButtonBorderColor), .5f)) + g.DrawPath(borderPen, gp); + // add a spotlight underneath + accentRect.Offset(0, (int)(accentRect.Height * .6f)); + // center focus + if (Orientation == Orientation.Horizontal && this.ButtonStyle == ButtonType.RoundedRectOverlap + || Orientation == Orientation.Vertical && this.ButtonStyle == ButtonType.RoundedRectInline) + { + + fx = accentRect.Width * .1f; + fy = accentRect.Height * .5f; + // notch it down a little + accentRect.Offset(0, (int)(accentRect.Height * .2f)); + } + else + { + fx = accentRect.Width * .5f; + fy = accentRect.Height * .1f; + } + + using (GraphicsPath ga = new GraphicsPath()) + { + ga.AddEllipse(accentRect); + // draw bottom glow + using (PathGradientBrush borderBrush = new PathGradientBrush(ga)) + { + borderBrush.CenterColor = this.ButtonAccentColor; + borderBrush.SurroundColors = new Color[] { Color.Transparent }; + borderBrush.FocusScales = new PointF(.1f, .2f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, ga); + } + } + using (GraphicsPath ga = new GraphicsPath()) + { + if (this.ButtonStyle == ButtonType.RoundedRectOverlap) + ga.AddEllipse(0, 0, buttonRect.Width, 4); + else + ga.AddEllipse(2, 0, buttonRect.Width - 4, 4); + // spotight offsets + fx = buttonRect.Width * .5f; + fy = buttonRect.Height * .05f; + // draw the top spotlight + using (PathGradientBrush borderBrush = new PathGradientBrush(ga)) + { + borderBrush.CenterColor = Color.FromArgb(120, Color.White); + borderBrush.SurroundColors = new Color[] { Color.FromArgb(5, Color.Silver) }; + borderBrush.FocusScales = new PointF(.2f, .2f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, gp); + } + } + } + } + } + break; + } + #endregion + + #region Glass + case ButtonType.GlassInline: + case ButtonType.GlassOverlap: + { + using (Graphics g = Graphics.FromImage(_bmpButton)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = CreateRoundRectanglePath(g, buttonRect, this.ButtonCornerRadius)) + { + fx = buttonRect.Width * .5f; + fy = buttonRect.Height * .5f; + // add a shine + using (PathGradientBrush borderBrush = new PathGradientBrush(gp)) + { + borderBrush.CenterColor = Color.FromArgb(100, Color.DarkGray); + borderBrush.SurroundColors = new Color[] { Color.FromArgb(120, Color.Silver) }; + borderBrush.FocusScales = new PointF(1f, .5f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, gp); + } + // draw the border + using (Pen borderPen = new Pen(this.ButtonBorderColor, .5f)) + g.DrawPath(borderPen, gp); + // add a spotlight underneath + accentRect.Offset(0, (int)(accentRect.Height * .8f)); + // center focus + if (Orientation == Orientation.Horizontal && this.ButtonStyle == ButtonType.RoundedRectOverlap + || Orientation == Orientation.Vertical && this.ButtonStyle == ButtonType.RoundedRectInline) + { + + fx = accentRect.Width * .05f; + fy = accentRect.Height * .5f; + } + else + { + fx = accentRect.Width * .5f; + fy = accentRect.Height * .05f; + } + using (GraphicsPath ga = new GraphicsPath()) + { + ga.AddEllipse(accentRect); + using (PathGradientBrush borderBrush = new PathGradientBrush(ga)) + { + borderBrush.CenterColor = Color.FromArgb(120, this.ButtonAccentColor); + borderBrush.SurroundColors = new Color[] { Color.FromArgb(5, Color.Silver) }; + borderBrush.FocusScales = new PointF(.2f, .2f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, ga); + } + } + using (GraphicsPath ga = new GraphicsPath()) + { + ga.AddEllipse(0, 0, buttonRect.Width, 4); + // spotight offsets + fx = buttonRect.Width * .5f; + fy = buttonRect.Height * .05f; + // draw the top spotlight + using (PathGradientBrush borderBrush = new PathGradientBrush(ga)) + { + borderBrush.CenterColor = Color.FromArgb(120, Color.White); + borderBrush.SurroundColors = new Color[] { Color.FromArgb(5, Color.Silver) }; + borderBrush.FocusScales = new PointF(.2f, .2f); + borderBrush.CenterPoint = new PointF(fx, fy); + g.FillPath(borderBrush, gp); + } + } + } + } + } + break; + } + #endregion + } + _bmpButton.MakeTransparent(); + } + + /// Create graphics objects + private void CreateGraphicsObjects() + { + DestroyGraphicsObjects(); + // load primary buffer + _cControlDc = new cStoreDc(); + _cControlDc.Height = this.Height; + _cControlDc.Width = this.Width; + // track and background dc + _cTrackDc = new cStoreDc(); + _cTrackDc.Height = this.Height; + _cTrackDc.Width = this.Width; + // draw the track + CreateTrack(); + // create the button image + CreateButtonBitmap(); + // create the animation sprite + CreateSprite(); + } + + /// create the animations sprite + private void CreateSprite() + { + Rectangle trackRect = GetTrackRectangle(); + int width = (int)(Orientation == Orientation.Horizontal ? trackRect.Width * .1f : trackRect.Height * .1f); + int height = this.TrackDepth; + + DestroySprite(); + // draw the line sprite into a bmp + _bmpSprite = new Bitmap(width, height); + using (Graphics g = Graphics.FromImage(_bmpSprite)) + { + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + g.CompositingMode = CompositingMode.SourceOver; + Rectangle imageRect = new Rectangle(0, 0, width, height); + // draw sprite + using (LinearGradientBrush fillBrush = new LinearGradientBrush( + imageRect, + Color.White, + Color.Transparent, + LinearGradientMode.Horizontal)) + { + Blend blnd = new Blend(); + blnd.Positions = new float[] { 0f, .2f, .5f, .8f, 1f }; + blnd.Factors = new float[] { 1f, .6f, 0f, .4f, 1f }; + fillBrush.Blend = blnd; + using (GraphicsPath gp = CreateRoundRectanglePath(g, imageRect, 2)) + g.FillPath(fillBrush, gp); + } + } + } + // make transparent + _bmpSprite.MakeTransparent(); + // rotate + if (Orientation == Orientation.Vertical) + _bmpSprite.RotateFlip(RotateFlipType.Rotate90FlipX); + } + + /// Create the track dc + private void CreateTrack() + { + Rectangle bounds = new Rectangle(0, 0, this.Width, this.Height); + Rectangle trackRect = GetTrackRectangle(); + + using (Graphics g = Graphics.FromHdc(_cTrackDc.Hdc)) + { + // fill it in + if (this.BackgroundImage != null) + { + using (GraphicsMode md = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (ImageAttributes ia = new ImageAttributes()) + g.DrawImage(this.BackgroundImage, bounds, 0, 0, this.BackgroundImage.Width, this.BackgroundImage.Height, GraphicsUnit.Pixel, ia); + } + } + else + { + DrawBackGround(g, bounds); + } + // draw ticks + if (this.TickStyle != TickStyle.None) + DrawTicks(g, bounds); + // create the path + if (Orientation == Orientation.Horizontal)//***offsets wrong on createpath? + trackRect.Width -= 1; + else + trackRect.Height -= 1; + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + // draw track shadow + if (this.TrackShadow) + { + trackRect.Inflate(2, 2); + using (GraphicsPath gp = CreateRoundRectanglePath(g, trackRect, 2)) + { + // light shadow + using (Pen pn = new Pen(Color.FromArgb(80, this.TrackShadowColor), 1f)) + g.DrawPath(pn, gp); + } + trackRect.Inflate(-1, -1); + using (GraphicsPath gp = CreateRoundRectanglePath(g, trackRect, 2)) + { + // darker + using (Pen pn = new Pen(Color.FromArgb(120, this.TrackShadowColor), 1f)) + g.DrawPath(pn, gp); + } + trackRect.Inflate(-1, -1); + } + + using (GraphicsPath gp = CreateRoundRectanglePath(g, trackRect, 2)) + { + // fill color + if (this.TrackFillColor != Color.Transparent) + { + using (Brush br = new SolidBrush(this.TrackFillColor)) + g.FillPath(br, gp); + } + // draw the outline + using (Pen pn = new Pen(this.TrackBorderColor, 1f)) + g.DrawPath(pn, gp); + } + } + } + } + + /// Destroy grahics objects + private void DestroyGraphicsObjects() + { + // destroy sprite and timer + StopPulseTimer(); + DestroySprite(); + // destroy buffers + if (_cControlDc != null) + _cControlDc.Dispose(); + if (_cTrackDc != null) + _cTrackDc.Dispose(); + if (_cAnimationDc != null) + _cAnimationDc.Dispose(); + // destroy images + if (_bmpButton != null) + _bmpButton.Dispose(); + if (_bmpSprite != null) + _bmpSprite.Dispose(); + } + + /// Destroy animation sprite + private void DestroySprite() + { + if (_bmpSprite != null) + _bmpSprite.Dispose(); + } + #endregion + + #region Animation + #region Drawing + private void DrawPulse() + { + if (_cControlDc != null) + { + int offset = 0; + Rectangle buttonRect = GetButtonRectangle(); + Rectangle trackRect = GetTrackRectangle(); + Rectangle lineRect; + Rectangle clipRect = trackRect; + + // copy unaltered image into buffer + BitBlt(_cAnimationDc.Hdc, 0, 0, _cAnimationDc.Width, _cAnimationDc.Height, _cControlDc.Hdc, 0, 0, 0xCC0020); + + if (Orientation == Orientation.Horizontal) + { + _iLinePos += 2; + if (_iLinePos > trackRect.Right - _bmpSprite.Width) + _iLinePos = trackRect.Left + 1; + if (_iLinePos < buttonRect.Left) + { + if (_iLinePos + _bmpSprite.Width > buttonRect.Left) + offset = _iLinePos + _bmpSprite.Width - buttonRect.Left; + lineRect = new Rectangle(_iLinePos, trackRect.Y, _bmpSprite.Width - offset, _bmpSprite.Height); + // draw sprite -horz + DrawPulseSprite(_cAnimationDc.Hdc, _bmpSprite, lineRect, .4f); + } + } + else + { + _iLinePos -= 1; + if (_iLinePos < trackRect.Top + _bmpSprite.Height) + _iLinePos = trackRect.Bottom - _bmpSprite.Height; + if (_iLinePos > buttonRect.Bottom) + { + if (_iLinePos - _bmpSprite.Height < buttonRect.Bottom) + offset = buttonRect.Bottom - (_iLinePos - _bmpSprite.Height); + lineRect = new Rectangle(trackRect.X, _iLinePos, _bmpSprite.Width, _bmpSprite.Height - offset); + // draw sprite -vert + DrawPulseSprite(_cAnimationDc.Hdc, _bmpSprite, lineRect, .4f); + } + } + // draw to control + using (Graphics g = Graphics.FromHwnd(this.Handle)) + { + BitBlt(g.GetHdc(), 0, 0, _cAnimationDc.Width, _cAnimationDc.Height, _cAnimationDc.Hdc, 0, 0, 0xCC0020); + g.ReleaseHdc(); + } + RECT r = new RECT(0, 0, this.Width, this.Height); + ValidateRect(this.Handle, ref r); + } + } + + /// Draws the line sprite + private void DrawPulseSprite(IntPtr destdc, Bitmap source, Rectangle bounds, float intensity) + { + using (Graphics g = Graphics.FromHdc(destdc)) + { + g.CompositingMode = CompositingMode.SourceOver; + AlphaBlend(g, source, bounds, intensity); + } + } + #endregion + + #region Timer + private void StartPulseTimer() + { + if (_cPulseTimer == null) + { + _bPulseTimerOn = true; + if (_cAnimationDc != null) + _cAnimationDc.Dispose(); + _cAnimationDc = new cStoreDc(); + _cAnimationDc.Width = _cControlDc.Width; + _cAnimationDc.Height = _cControlDc.Height; + BitBlt(_cAnimationDc.Hdc, 0, 0, _cControlDc.Width, _cControlDc.Height, _cControlDc.Hdc, 0, 0, 0xCC0020); + // timer setup + _cPulseTimer = new FadeTimer(this); + _cPulseTimer.Tick += new FadeTimer.TickDelegate(_cPulseTimer_Tick); + _cPulseTimer.Complete += new FadeTimer.CompleteDelegate(_cPulseTimer_Complete); + _cPulseTimer.Interval = (int)this.AnimationSpeed; + _cPulseTimer.Fade(FadeTimer.FadeType.Loop); + } + } + + private void StopPulseTimer() + { + if (_cPulseTimer != null) + { + _iLinePos = 0; + if (_cAnimationDc != null) + _cAnimationDc.Dispose(); + // tear down the timer class + _cPulseTimer.Reset(); + _cPulseTimer.Tick -= _cPulseTimer_Tick; + _cPulseTimer.Complete -= _cPulseTimer_Complete; + _cPulseTimer.Dispose(); + _cPulseTimer = null; + _bPulseTimerOn = false; + } + } + + private void _cPulseTimer_Complete(object sender) + { + ResetCallback rs = new ResetCallback(StopPulseTimer); + this.Invoke(rs); + } + + private void _cPulseTimer_Tick(object sender) + { + DrawPulse(); + } + + #endregion + #endregion + + #region FlyOut Caption + private void DrawFlyOut(Graphics g) + { + Rectangle buttonRect = GetButtonRectangle(); + Rectangle flyoutRect; + int pos; + + if (Orientation == Orientation.Horizontal) + { + pos = buttonRect.Left + (int)(buttonRect.Width * .5f); + int offset = (int)(this.FlyOutMaxWidth * .5f); + flyoutRect = new Rectangle((int)pos - offset, buttonRect.Top - (this.FlyOutMaxDepth + this.FlyOutSpacer), this.FlyOutMaxWidth, this.FlyOutMaxDepth); + offset -= 8; + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = CreatePointedRectangularPath(g, flyoutRect, PointDirection.Bottom, 5, 4, offset)) + { + using (Brush br = new SolidBrush(Color.FromArgb(220, Color.White))) + g.FillPath(br, gp); + using (Pen pn = new Pen(Color.FromArgb(160, Color.DimGray))) + g.DrawPath(pn, gp); + + FlyOutEventArgs flyOutData = new FlyOutEventArgs(); + if (FlyOutInfo != null) + FlyOutInfo(this, flyOutData); + if (flyOutData.text.Length == 0) + flyOutData.text = this.Value.ToString("0"); + using (StringFormat sf = new StringFormat()) + { + sf.FormatFlags = StringFormatFlags.NoWrap; + sf.Alignment = StringAlignment.Center; + sf.LineAlignment = StringAlignment.Center; + using (Font ft = new Font("Arial", 8f, FontStyle.Regular)) + g.DrawString(flyOutData.text, ft, Brushes.Black, flyoutRect, sf); + } + } + } + } + else + { + pos = buttonRect.Top + (int)(buttonRect.Height * .5f); + int offset = (int)(this.FlyOutMaxWidth * .5f); + flyoutRect = new Rectangle(buttonRect.Left - (this.FlyOutMaxDepth + this.FlyOutSpacer), pos - offset, this.FlyOutMaxDepth, this.FlyOutMaxWidth); + using (GraphicsMode mode = new GraphicsMode(g, SmoothingMode.HighQuality)) + { + using (GraphicsPath gp = CreatePointedRectangularPath(g, flyoutRect, PointDirection.Right, 5, 4, 1)) + { + using (Brush br = new SolidBrush(Color.FromArgb(200, Color.White))) + g.FillPath(br, gp); + using (Pen pn = new Pen(Color.FromArgb(240, this.ButtonBorderColor))) + g.DrawPath(pn, gp); + + FlyOutEventArgs flyOutData = new FlyOutEventArgs(); + if (FlyOutInfo != null) + FlyOutInfo(this, flyOutData); + if (flyOutData.text.Length == 0) + flyOutData.text = this.Value.ToString("0"); + flyoutRect.Width -= 4; + using (StringFormat sf = new StringFormat()) + { + sf.FormatFlags = StringFormatFlags.NoWrap; + sf.Alignment = StringAlignment.Center; + sf.LineAlignment = StringAlignment.Center; + using (Font ft = new Font("Arial", 8f, FontStyle.Regular)) + g.DrawString(flyOutData.text, ft, Brushes.Black, flyoutRect, sf); + } + } + } + } + + } + #endregion + #endregion + + #region Helpers + /// AlphaBlend an image, alpha .1-1 + private void AlphaBlend(Graphics g, Bitmap bmp, Rectangle bounds, float alpha) + { + if (alpha > 1f) + alpha = 1f; + else if (alpha < .01f) + alpha = .01f; + using (ImageAttributes ia = new ImageAttributes()) + { + ColorMatrix cm = new ColorMatrix(); + cm.Matrix00 = 1f; + cm.Matrix11 = 1f; + cm.Matrix22 = 1f; + cm.Matrix44 = 1f; + cm.Matrix33 = alpha; + ia.SetColorMatrix(cm); + g.DrawImage(bmp, bounds, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, ia); + } + } + + /// Maximun size based on control options + private void CalculateMaximumSize() + { + Size sz = new Size(0, 0); + + if (this.TickStyle != TickStyle.None) + sz.Height += (this.TickMaxLength + this.TickMinPadding) * (this.TickStyle == TickStyle.Both ? 2 : 1); + if (this.SliderFlyOut != FlyOutStyle.None) + sz.Height += this.FlyOutMaxDepth + this.FlyOutSpacer; + + switch (this.ButtonStyle) + { + case ButtonType.GlassInline: + case ButtonType.RoundedRectInline: + { + sz.Height += TrackMaxDepth + 6; + break; + } + case ButtonType.GlassOverlap: + case ButtonType.RoundedRectOverlap: + { + sz.Height += TrackMaxDepth * 4; + break; + } + case ButtonType.Round: + case ButtonType.Hybrid: + { + sz.Height += TrackMaxDepth + 8; + break; + } + case ButtonType.PointerDownLeft: + case ButtonType.PointerUpRight: + { + sz.Height += TrackMaxDepth * 4; + break; + } + } + if (Orientation == Orientation.Horizontal) + { + this.MaxSize = sz; + } + else + { + Size flip = new Size(sz.Height, sz.Width); + this.MaxSize = flip; + } + } + + /// Minimum size based on control options + private void CalculateMinimumSize() + { + Size sz = new Size(0, 0); + + if (this.TickStyle != TickStyle.None) + sz.Height += (this.TickMaxLength + this.TickMinPadding) * (this.TickStyle == TickStyle.Both ? 2 : 1); + if (this.SliderFlyOut != FlyOutStyle.None) + sz.Height += this.FlyOutMaxDepth + this.FlyOutSpacer; + + switch (this.ButtonStyle) + { + case ButtonType.GlassInline: + case ButtonType.RoundedRectInline: + { + sz.Height += TrackMinDepth + 6; + break; + } + case ButtonType.GlassOverlap: + case ButtonType.RoundedRectOverlap: + { + sz.Height += TrackMinDepth * 4; + break; + } + case ButtonType.Round: + case ButtonType.Hybrid: + { + sz.Height += TrackMinDepth + 8; + break; + } + case ButtonType.PointerDownLeft: + case ButtonType.PointerUpRight: + { + sz.Height += TrackMinDepth * 4; + break; + } + } + if (Orientation == Orientation.Horizontal) + { + this.MinSize = sz; + } + else + { + Size flip = new Size(sz.Height, sz.Width); + this.MinSize = flip; + } + } + + private GraphicsPath CreatePointedRectangularPath(Graphics g, Rectangle bounds, PointDirection direction, float radius, int depth, int inset) + { + int diff = 0; + // create a path + GraphicsPath pathBounds = new GraphicsPath(); + switch (direction) + { + case PointDirection.Bottom: + { + // line left + pathBounds.AddLine(bounds.Left, bounds.Bottom - radius, bounds.Left, bounds.Top + radius); + if (radius > 0) + pathBounds.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90); + // line top + pathBounds.AddLine(bounds.Left + radius, bounds.Top, bounds.Right - radius, bounds.Top); + if (radius > 0) + pathBounds.AddArc(bounds.Right - radius, bounds.Top, radius, radius, 270, 90); + // line right + pathBounds.AddLine(bounds.Right, bounds.Top + radius, bounds.Right, bounds.Bottom - radius); + // pointed path // + if (inset == -1) + radius = 0; + if (radius > 0) + pathBounds.AddArc(bounds.Right - radius, bounds.Bottom - radius, radius, radius, 0, 90); + // line bottom right + pathBounds.AddLine(bounds.Right - radius, bounds.Bottom, bounds.Right - (radius + inset), bounds.Bottom); + // pointed center + diff = (bounds.Width / 2) - ((int)radius + inset); + // right half + pathBounds.AddLine(bounds.Right - (radius + inset), bounds.Bottom, bounds.Right - (radius + inset + diff), bounds.Bottom + depth); + // left half + pathBounds.AddLine(bounds.Right - (radius + inset + diff), bounds.Bottom + depth, bounds.Left + radius + inset, bounds.Bottom); + // line bottom left + pathBounds.AddLine(bounds.Left + radius + inset, bounds.Bottom, bounds.Left + radius, bounds.Bottom); + if (radius > 0) + pathBounds.AddArc(bounds.Left, bounds.Bottom - radius, radius, radius, 90, 90); + + break; + } + case PointDirection.Right: + { + // line top + pathBounds.AddLine(bounds.Left + radius, bounds.Top, bounds.Right - (radius + depth), bounds.Top); + // arc top right + if (radius > 0) + pathBounds.AddArc(bounds.Right - (radius + depth), bounds.Top, radius, radius, 270, 90); + + // top line + pathBounds.AddLine(bounds.Right - depth, bounds.Top + radius, bounds.Right - depth, bounds.Top + (radius + inset)); + // pointed center + diff = (bounds.Height / 2) - ((int)radius + inset); + // top half + pathBounds.AddLine(bounds.Right - depth, bounds.Top + (radius + inset), bounds.Right, bounds.Bottom - (radius + inset + diff)); + // bottom half + pathBounds.AddLine(bounds.Right, bounds.Bottom - (radius + inset + diff), bounds.Right - depth, bounds.Bottom - (radius + inset)); + // line right bottom + pathBounds.AddLine(bounds.Right - depth, bounds.Bottom - (radius + inset), bounds.Right - depth, bounds.Bottom - radius); + // arc bottom right + if (radius > 0) + pathBounds.AddArc(bounds.Right - (radius + depth), bounds.Bottom - radius, radius, radius, 0, 90); + // line bottom + pathBounds.AddLine(bounds.Right - (radius + depth), bounds.Bottom, bounds.Left + radius, bounds.Bottom); + if (inset == -1) + radius = 0; + // arc bottom left + if (radius > 0) + pathBounds.AddArc(bounds.Left, bounds.Bottom - radius, radius, radius, 90, 90); + // line left + pathBounds.AddLine(bounds.Left, bounds.Bottom - radius, bounds.Left, bounds.Top + radius); + // arc top left + if (radius > 0) + pathBounds.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90); + + /*// pointed path // + // line left bottom + pathBounds.AddLine(bounds.Left, bounds.Bottom - radius, bounds.Left, bounds.Bottom - (radius + inset)); + // pointed center + diff = (bounds.Height / 2) - ((int)radius + inset); + // bottom half + pathBounds.AddLine(bounds.Left, bounds.Bottom - (radius + inset), bounds.Left - depth, bounds.Bottom - (radius + inset + diff)); + // top half + pathBounds.AddLine(bounds.Left - depth, bounds.Bottom - (radius + inset + diff), bounds.Left, bounds.Top + (radius + inset)); + // top line + pathBounds.AddLine(bounds.Left, bounds.Top + (radius + inset), bounds.Left, bounds.Top + radius); + if (radius > 0) + pathBounds.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90);*/ + break; + } + } + + pathBounds.CloseFigure(); + return pathBounds; + } + + /// Create a round GraphicsPath [not used] + private GraphicsPath CreateRoundPath(Graphics g, Rectangle bounds) + { + int size = bounds.Width > bounds.Height ? bounds.Height : bounds.Width; + bounds.Height = size; + bounds.Width = size; + GraphicsPath circlePath = new GraphicsPath(); + // create the path + circlePath.AddEllipse(bounds); + circlePath.CloseFigure(); + return circlePath; + } + + /// Create a rounded rectangle GraphicsPath + private GraphicsPath CreateRoundRectanglePath(Graphics g, Rectangle bounds, float radius) + { + // create a path + GraphicsPath pathBounds = new GraphicsPath(); + // arc top left + pathBounds.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90); + // line top + pathBounds.AddLine(bounds.Left + radius, bounds.Top, bounds.Right - radius, bounds.Top); + // arc top right + pathBounds.AddArc(bounds.Right - radius, bounds.Top, radius, radius, 270, 90); + // line right + pathBounds.AddLine(bounds.Right, bounds.Top + radius, bounds.Right, bounds.Bottom - radius); + // arc bottom right + pathBounds.AddArc(bounds.Right - radius, bounds.Bottom - radius, radius, radius, 0, 90); + // line bottom + pathBounds.AddLine(bounds.Right - radius, bounds.Bottom, bounds.Left + radius, bounds.Bottom); + // arc bottom left + pathBounds.AddArc(bounds.Left, bounds.Bottom - radius, radius, radius, 90, 90); + // line left + pathBounds.AddLine(bounds.Left, bounds.Bottom - radius, bounds.Left, bounds.Top + radius); + pathBounds.CloseFigure(); + return pathBounds; + } + + /// Set default button sizes + private void DefaultButtonSize(ButtonType type) + { + Size sz = new Size(14, 14); + switch (type) + { + case ButtonType.Hybrid: + case ButtonType.Round: + _iTrackDepth = 4; + sz.Height = _iTrackDepth + 8; + sz.Width = sz.Height; + break; + case ButtonType.PointerDownLeft: + case ButtonType.PointerUpRight: + _iTrackDepth = 5; + sz.Height = _iTrackDepth * 4; + sz.Width = _iTrackDepth + 2; + break; + case ButtonType.RoundedRectInline: + _iTrackDepth = 4; + _iButtonCornerRadius = 6; + sz.Height = _iTrackDepth + 6; + sz.Width = sz.Height * 2; + break; + case ButtonType.RoundedRectOverlap: + _iTrackDepth = 4; + _iButtonCornerRadius = 4; + sz.Height = _iTrackDepth * 4; + sz.Width = _iTrackDepth + 6; + break; + case ButtonType.GlassInline: + _iTrackDepth = 6; + _iButtonCornerRadius = 2; + sz.Height = _iTrackDepth + 6; + sz.Width = sz.Height * 2; + break; + case ButtonType.GlassOverlap: + _iTrackDepth = 4; + _iButtonCornerRadius = 2; + sz.Height = _iTrackDepth * 4; + sz.Width = _iTrackDepth + 6; + break; + } + + if (Orientation == Orientation.Vertical) + { + Size sv = new Size(sz.Height, sz.Width); + _szButtonSize = sv; + } + else + { + _szButtonSize = sz; + } + } + + /// Return button size and coordinates + private Rectangle GetButtonRectangle() + { + Rectangle bounds = new Rectangle(0, 0, this.Width, this.Height); + RectangleF buttonRect = new RectangleF(); + int offsetX = (bounds.Width / 2); + int offset = 0; + double pos = _dButtonPosition + this.TrackPadding; + + if (Orientation == Orientation.Horizontal) + { + if (this.SliderFlyOut != FlyOutStyle.None) + offset = this.FlyOutMaxDepth + this.FlyOutSpacer; + if (this.TickStyle == TickStyle.TopLeft) + offset += this.TickMaxLength + this.TickMinPadding; + else if (this.TickStyle == TickStyle.BottomRight) + offset -= this.TickMaxLength + this.TickMinPadding; + offset += (int)((bounds.Height - offset) * .5f); + offset -= (int)(this.ButtonSize.Height * .5f); + buttonRect = new RectangleF((float)pos, offset, this.ButtonSize.Width, this.ButtonSize.Height); + } + else + { + // offset on track + if (this.SliderFlyOut != FlyOutStyle.None) + offset = this.FlyOutMaxDepth + this.FlyOutSpacer; + if (this.TickStyle == TickStyle.TopLeft) + offset += this.TickMaxLength + this.TickMinPadding; + else if (this.TickStyle == TickStyle.BottomRight) + offset -= this.TickMaxLength + this.TickMinPadding; + offset += (int)((bounds.Width - offset) * .5f); + offset -= (int)(this.ButtonSize.Width * .5f); + buttonRect = new RectangleF(offset, (float)pos, this.ButtonSize.Width, this.ButtonSize.Height); + } + // store it for hit testing + _buttonRect = new RECT((int)buttonRect.X, (int)buttonRect.Y, (int)buttonRect.Right, (int)buttonRect.Bottom); + return Rectangle.Round(buttonRect); + } + + /// Return track size and coordinates + private Rectangle GetTrackRectangle() + { + Rectangle bounds = new Rectangle(0, 0, this.Width, this.Height); + Rectangle trackRect; + int offset; + + if (Orientation == Orientation.Horizontal) + { + // reduce for padding and center rect + offset = (int)(this.TrackPadding); + bounds.Inflate(-offset, 0); + offset = 0; + if (this.SliderFlyOut != FlyOutStyle.None) + offset = this.FlyOutMaxDepth + this.FlyOutSpacer; + if (this.TickStyle == TickStyle.TopLeft) + offset += this.TickMaxLength + this.TickMinPadding; + else if (this.TickStyle == TickStyle.BottomRight) + offset -= this.TickMaxLength + this.TickMinPadding; + offset += (int)((bounds.Height - offset) * .5f); + offset -= (int)(this.TrackDepth * .5f); + trackRect = new Rectangle(bounds.X, offset, bounds.Width, this.TrackDepth); + } + else + { + offset = (int)(this.TrackPadding); + bounds.Inflate(0, -offset); + offset = 0; + if (this.SliderFlyOut != FlyOutStyle.None) + offset = this.FlyOutMaxDepth + this.FlyOutSpacer; + if (this.TickStyle == TickStyle.TopLeft) + offset += this.TickMaxLength + this.TickMinPadding; + else if (this.TickStyle == TickStyle.BottomRight) + offset -= this.TickMaxLength + this.TickMinPadding; + offset += (int)((bounds.Width - offset) * .5f); + offset -= (int)(this.TrackDepth * .5f); + trackRect = new Rectangle(offset, bounds.Y, this.TrackDepth, bounds.Height); + } + // store for hit testing + _trackRect = new RECT(trackRect.X, trackRect.Y, trackRect.Right, trackRect.Bottom); + return trackRect; + } + + /// Exact size between ticks + private double Increment() + { + Rectangle trackRect = GetTrackRectangle(); + + if (Orientation == Orientation.Horizontal) + return (double)((trackRect.Width - this.ButtonSize.Width) / IncrementScale()); + else + return (double)((trackRect.Height - this.ButtonSize.Height) / IncrementScale()); + } + + /// Offset number if Minimum and Maximum are negative to positive integers + private double IncrementOffset() + { + double center = 0; + if (this.Minimum < 0 && this.Maximum > 0) + center = 1; + return center; + } + + /// The total number of tick increments + private double IncrementScale() + { + return (double)Math.Abs(this.Maximum - this.Minimum); + } + + /// The incremental size between the Minimum and Value + private double IncrementalValue() + { + if (Orientation == Orientation.Horizontal) + return Increment() * (double)(Math.Abs(this.Value - this.Minimum)); + else + return Increment() * (double)(IncrementScale() - (Math.Abs(this.Value - this.Minimum))); + } + + /// Modulus returns true if divisible with no remainder + private bool Mod(int a, int b) + { + if (Math.IEEERemainder((double)a, (double)b) == 0) + return true; + return false; + } + + /// Return position coordinates from value + private double PosFromValue(int val) + { + if (Orientation == Orientation.Horizontal) + return Increment() * (double)(Math.Abs(val - this.Minimum + (val != this.Minimum ? IncrementOffset() : 0))); + else + return Increment() * (double)(IncrementScale() - (Math.Abs(val - this.Minimum) + (val != this.Minimum ? IncrementOffset() : 0))); + } + + /// Repaint and optionally resize + private void PropertyChange() + { + if (this.DesignMode) + { + if (this.SmoothScrolling) + _dButtonPosition = PosFromValue(this.Value); + else + _dButtonPosition = IncrementalValue(); + if (this.AutoSize && this.FinishedPropRun) + ResizeThis(); + CreateGraphicsObjects(); + } + else if (this.IsInited) + { + if (this.SmoothScrolling) + _dButtonPosition = PosFromValue(this.Value); + else + _dButtonPosition = IncrementalValue(); + CreateGraphicsObjects(); + } + DrawSlider(); + } + + /// Repaint the control + private void Repaint() + { + this.Invalidate(); + this.Update(); + } + + /// Resize the control via alignments and options + private void ResizeThis() + { + int offset = 0; + int depth = 0; + int diff = 0; + Size sz = new Size(14, 14); + + CalculateMaximumSize(); + CalculateMinimumSize(); + + if (this.Orientation == Orientation.Horizontal) + { + if (this.MinSize.Height == 0 || this.MaxSize.Height == 0) + return; + + if (this.Height <= this.MinSize.Height) + this.Height = this.MinSize.Height; + else if (this.Height >= this.MaxSize.Height) + this.Height = this.MaxSize.Height; + + offset = this.Height - this.MinSize.Height; + diff = this.MinSize.Height - ButtonSize.Height; + + if (offset < 2) + depth = TrackMinDepth; + else if (offset < 3) + depth = TrackMinDepth + 1; + else if (offset < 4) + depth = TrackMinDepth + 2; + else + depth = TrackMaxDepth; + + switch (this.ButtonStyle) + { + case ButtonType.GlassInline: + { + sz.Height = depth + 6; + sz.Width = sz.Height * 2; + break; + } + case ButtonType.RoundedRectInline: + { + sz.Height = depth + 6; + sz.Width = sz.Height * 2; + break; + } + case ButtonType.GlassOverlap: + { + sz.Height = depth * 4; + sz.Width = depth + 6; + break; + } + case ButtonType.RoundedRectOverlap: + { + sz.Height = depth * 4; + sz.Width = depth + 6; + break; + } + case ButtonType.Round: + case ButtonType.Hybrid: + { + sz.Height = depth + 8; + sz.Width = sz.Height; + break; + } + case ButtonType.PointerDownLeft: + case ButtonType.PointerUpRight: + { + sz.Height = depth * 4; + sz.Width = depth + 2; + break; + } + default: + { + sz.Height = depth * 4; + sz.Width = depth + 2; + break; + } + } + + if (this.Width < (this.ButtonSize.Height + this.TrackPadding) * 2) + this.Width = (this.ButtonSize.Height + this.TrackPadding) * 2; + if (sz.Height != this.ButtonSize.Height && this.Height >= sz.Height + diff) + { + _iTrackDepth = depth; + _szButtonSize = sz; + } + else if (sz.Height != this.ButtonSize.Height && this.Height < this.ButtonSize.Height + diff) + { + _iTrackDepth = depth; + _szButtonSize = sz; + } + } + else + { + if (this.MinSize.Width == 0 || this.MaxSize.Width == 0) + return; + + if (this.Width <= this.MinSize.Width) + this.Width = this.MinSize.Width; + else if (this.Width >= this.MaxSize.Width) + this.Width = this.MaxSize.Width; + + offset = this.Width - this.MinSize.Width; + + if (offset < 2) + depth = TrackMinDepth; + else if (offset < 3) + depth = TrackMinDepth + 1; + else if (offset < 4) + depth = TrackMinDepth + 2; + else + depth = TrackMaxDepth; + + switch (this.ButtonStyle) + { + case ButtonType.GlassInline: + { + sz.Width = depth + 6; + sz.Height = sz.Width * 2; + break; + } + case ButtonType.RoundedRectInline: + { + sz.Width = depth + 6; + sz.Height = sz.Width * 2; + break; + } + case ButtonType.GlassOverlap: + { + sz.Width = depth * 4; + sz.Height = depth + 6; + break; + } + case ButtonType.RoundedRectOverlap: + { + sz.Width = depth * 4; + sz.Height = depth + 6; + break; + } + case ButtonType.Round: + case ButtonType.Hybrid: + { + sz.Width = depth + 8; + sz.Height = sz.Width; + break; + } + case ButtonType.PointerDownLeft: + case ButtonType.PointerUpRight: + { + sz.Width = depth * 4; + sz.Height = depth + 2; + break; + } + default: + { + sz.Width = depth * 4; + sz.Height = depth + 2; + break; + } + } + + if (this.Height < (this.ButtonSize.Width + this.TrackPadding) * 2) + this.Height = (this.ButtonSize.Width + this.TrackPadding) * 2; + + if (sz.Width != this.ButtonSize.Width && this.Width >= sz.Width + diff) + { + _iTrackDepth = depth; + _szButtonSize = sz; + } + else if (sz.Width != this.ButtonSize.Width && this.Width < this.ButtonSize.Width + diff) + { + _iTrackDepth = depth; + _szButtonSize = sz; + } + } + } + + /// Scroll by large or small change values + private void ScrollChange(ChangeType change, bool decrease) + { + int count = 0; + if (change == ChangeType.Large) + { + if (decrease) + count = this.Value - LargeChange; + else + count = this.Value + LargeChange; + } + else + { + if (decrease) + count = this.Value - SmallChange; + else + count = this.Value + SmallChange; + } + + if (count < this.Minimum) + count = this.Minimum; + if (count > this.Maximum) + count = this.Maximum; + + this.Value = count; + } + + /// Estimate value from position + public int ValueFromPosition() + { + try + { + int pos = this.PointToClient(Cursor.Position).X; + double increment = Increment(); + int sz = (Orientation == Orientation.Horizontal) ? this.ButtonSize.Width : this.ButtonSize.Height; + double val = IncrementalValue(); + pos -= (sz / 2); + + if (pos > -sz) + { + if (pos < this.TrackPadding) + pos = this.TrackPadding; + if (this.Orientation == Orientation.Horizontal) + { + if (pos > PosFromValue(this.Maximum) + this.TrackPadding) + pos = (int)PosFromValue(this.Maximum) + this.TrackPadding; + } + else + { + if (pos > PosFromValue(this.Minimum) + this.TrackPadding) + pos = (int)PosFromValue(this.Minimum) + this.TrackPadding; + } + pos -= this.TrackPadding; + } + return ValueFromPos(pos); + } + catch { return 0; } + } + + /// Scroll the slider to a position and set value + private void ScrollThis(double pos) + { + bool redraw = false; + double val; + double store = _dButtonPosition; + double increment = Increment(); + int sz = (Orientation == Orientation.Horizontal) ? this.ButtonSize.Width : this.ButtonSize.Height; + + val = IncrementalValue(); + pos -= (sz / 2); //ju 1.3 + + if (pos > -sz) + { + if (SmoothScrolling) + { + if (pos < this.TrackPadding) + pos = this.TrackPadding; + if (this.Orientation == Orientation.Horizontal) + { + if (pos > PosFromValue(this.Maximum) + this.TrackPadding) + pos = PosFromValue(this.Maximum) + this.TrackPadding; + } + else + { + if (pos > PosFromValue(this.Minimum) + this.TrackPadding) + pos = PosFromValue(this.Minimum) + this.TrackPadding; + } + pos -= this.TrackPadding; + _dButtonPosition = pos; + if (store != pos) + { + val = this.Value; + _iValue = ValueFromPos(pos); + if (_iValue != val) + { + if (ValueChanged != null) + ValueChanged(this, EventArgs.Empty); + } + DrawSlider(); + } + if (Scrolled != null) + Scrolled(this, EventArgs.Empty); + } + else + { + store = this.Value; + //pos -= this.TrackPadding; ju 1.3 + + if (pos > val + increment && + (Orientation == Orientation.Horizontal && this.Value != this.Maximum) || + (Orientation == Orientation.Vertical && this.Value != this.Minimum)) + { + _iValue = ValueFromPos(pos); + if (Scrolled != null) + Scrolled(this, EventArgs.Empty); + } + else if (pos < val && // ju 1.3 + (Orientation == Orientation.Horizontal && this.Value != this.Minimum) || + (Orientation == Orientation.Vertical && this.Value != this.Maximum)) + { + _iValue = ValueFromPos(pos); + if (Scrolled != null) + Scrolled(this, EventArgs.Empty); + } + if (_iValue != store) + this.Value = _iValue; + else if (redraw) + DrawSlider(); + } + + } + } + + /// Mouse Hit test + private HitTest SliderHitTest() + { + Point pt = new Point(); + RECT tr = new RECT(); + + GetClientRect(this.Handle, ref tr); + GetCursorPos(ref pt); + ScreenToClient(this.Handle, ref pt); + if (PtInRect(ref _buttonRect, pt)) + return HitTest.Button; + else if (PtInRect(ref _trackRect, pt)) + return HitTest.Track; + else + return HitTest.Nowhere; + } + + /// The value at a provided position + private int ValueFromPos(double pos) + { + int val; + //pos -= this.TrackPadding; + if (Orientation == Orientation.Horizontal) + val = this.Minimum + (int)Math.Round(pos / Increment()); + else + val = this.Maximum - (int)Math.Round(pos / Increment()); + + if (val < this.Minimum) + val = this.Minimum; + if (val > this.Maximum) + val = this.Maximum; + return val; + } + #endregion + #endregion + + #region Graphics Mode + /// Maintains graphic object state + internal class GraphicsMode : IDisposable + { + #region Fields + private Graphics _gGraphicCopy; + private SmoothingMode _eOldMode; + #endregion + + #region Methods + /// + /// Initialize a new instance of the class. + /// + /// Graphics instance. + /// Desired Smoothing mode. + public GraphicsMode(Graphics g, SmoothingMode mode) + { + _gGraphicCopy = g; + _eOldMode = _gGraphicCopy.SmoothingMode; + _gGraphicCopy.SmoothingMode = mode; + } + + /// + /// Revert the SmoothingMode to original setting. + /// + public void Dispose() + { + _gGraphicCopy.SmoothingMode = _eOldMode; + } + #endregion + } + #endregion + + #region Effects Timer + /// Effect timer class + internal class FadeTimer : IDisposable + { + #region Enum + internal enum FadeType + { + None = 0, + FadeIn, + FadeOut, + FadeFast, + Loop + } + #endregion + + #region Structs + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public RECT(int X, int Y, int Width, int Height) + { + this.Left = X; + this.Top = Y; + this.Right = Width; + this.Bottom = Height; + } + public int Left; + public int Top; + public int Right; + public int Bottom; + } + #endregion + + #region API + [DllImport("user32.dll")] + private static extern IntPtr GetDC(IntPtr handle); + + [DllImport("user32.dll")] + private static extern int ReleaseDC(IntPtr handle, IntPtr hdc); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); + + [DllImport("user32.dll")] + private static extern IntPtr GetDesktopWindow(); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + #endregion + + #region Events + public delegate void CompleteDelegate(object sender); + public delegate void TickDelegate(object sender); + public event CompleteDelegate Complete; + public event TickDelegate Tick; + #endregion + + #region Fields + private bool _bCaptureScreen = false; + private bool _bCancelTimer; + private bool _bIsReset; + private int _iTickCounter; + private int _iTickMaximum; + private double _iTickRate; + private FadeType _eFadeType; + private cStoreDc _cButtonDc; + private UserControl _ctParentControl; + private System.Timers.Timer _aTimer; + private bool _bInvalidating = false; + #endregion + + #region Constructor + public FadeTimer(object sender) + { + _iTickCounter = 0; + _iTickMaximum = 10; + _ctParentControl = (UserControl)sender; + _aTimer = new System.Timers.Timer(); + _iTickRate = _aTimer.Interval; + _aTimer.SynchronizingObject = (ISynchronizeInvoke)sender; + _aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); + } + #endregion + + #region Properties + public cStoreDc ButtonDc + { + get { return _cButtonDc; } + set { _cButtonDc = value; } + } + + public bool CaptureScreen + { + get { return _bCaptureScreen; } + set { _bCaptureScreen = value; } + } + + public bool Invalidating + { + get { return _bInvalidating; } + set { _bInvalidating = value; } + } + + public bool IsReset + { + get { return _bIsReset; } + set { _bIsReset = value; } + } + + public bool Cancel + { + get { return _bCancelTimer; } + set { _bCancelTimer = value; } + } + + public bool Enabled + { + get { return _aTimer.Enabled; } + } + + public FadeType FadeStyle + { + get { return _eFadeType; } + set { _eFadeType = value; } + } + + public double Interval + { + get { return _iTickRate; } + set + { + _iTickRate = value; + _aTimer.Interval = _iTickRate; + } + } + + public int TickCount + { + get { return _iTickCounter; } + set { _iTickCounter = value; } + } + + public int TickMaximum + { + get { return _iTickMaximum; } + set { _iTickMaximum = value; } + } + #endregion + + #region Public Methods + public void Dispose() + { + Reset(); + if (_cButtonDc != null) + _cButtonDc.Dispose(); + if (_aTimer != null) + _aTimer.Dispose(); + GC.SuppressFinalize(this); + } + + public void Fade(FadeType ft) + { + Cancel = false; + IsReset = false; + Invalidating = false; + _eFadeType = ft; + if (_eFadeType == FadeType.FadeIn) + { + TickCount = 0; + if (CaptureScreen) + CaptureDc(); + } + else if (_eFadeType == FadeType.FadeOut) + { + TickCount = 10; + } + else if (_eFadeType == FadeType.FadeFast) + { + TickCount = 10; + } + else if (_eFadeType == FadeType.Loop) + { + TickMaximum = 100000; + TickCount = 0; + if (CaptureScreen) + CaptureDc(); + } + _aTimer.Enabled = true; + } + + public void Stop() + { + _aTimer.Stop(); + } + + public void Reset() + { + TickCount = 0; + _eFadeType = FadeType.None; + IsReset = true; + _aTimer.Stop(); + _aTimer.Enabled = false; + } + #endregion + + #region Event Handlers + private void OnTimedEvent(object source, ElapsedEventArgs e) + { + if (Cancel) + { + Invalidating = true; + if (Complete != null) Complete(this); + return; + } + else + { + switch (_eFadeType) + { + case FadeType.FadeIn: + FadeIn(); + break; + case FadeType.FadeFast: + FadeOut(); + break; + case FadeType.FadeOut: + FadeOut(); + break; + case FadeType.Loop: + FadeLoop(); + break; + } + } + } + #endregion + + #region private Methods + private void CaptureDc() + { + try + { + _cButtonDc.Width = _ctParentControl.Width; + _cButtonDc.Height = _ctParentControl.Height; + if (_cButtonDc.Hdc != IntPtr.Zero) + { + using (Graphics g = Graphics.FromHdc(_cButtonDc.Hdc)) + { + RECT boundedRect = new RECT(); + GetWindowRect(_ctParentControl.Handle, ref boundedRect); + g.CopyFromScreen(boundedRect.Left, boundedRect.Top, 0, 0, new Size(_cButtonDc.Width, _cButtonDc.Height), CopyPixelOperation.SourceCopy); + } + } + } + catch { } + } + + private void FadeIn() + { + if (TickCount < TickMaximum) + { + TickCount++; + if (Tick != null) + Tick(this); + } + else + { + TickCount = TickMaximum; + } + } + + private void FadeLoop() + { + if (TickCount < TickMaximum) + { + TickCount++; + if (Tick != null) + Tick(this); + } + else + { + TickCount = TickMaximum; + Reset(); + Invalidating = true; + if (Complete != null) + Complete(this); + } + } + + private void FadeOut() + { + if (TickCount > 0) + { + if (_eFadeType == FadeType.FadeFast) + { + TickCount -= 2; + if (TickCount < 0) + TickCount = 0; + } + else + { + TickCount--; + } + if (Tick != null) + Tick(this); + } + else + { + Reset(); + Invalidating = true; + if (Complete != null) + Complete(this); + } + } + + ~FadeTimer() + { + Dispose(); + } + #endregion + } + #endregion + + public class FlyOutEventArgs : EventArgs + { + public string text = ""; + } + + #region StoreDc + /// DC buffer class + internal class cStoreDc + { + #region API + [DllImport("gdi32.dll")] + private static extern IntPtr CreateDCA([MarshalAs(UnmanagedType.LPStr)]string lpszDriver, [MarshalAs(UnmanagedType.LPStr)]string lpszDevice, [MarshalAs(UnmanagedType.LPStr)]string lpszOutput, int lpInitData); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateDCW([MarshalAs(UnmanagedType.LPWStr)]string lpszDriver, [MarshalAs(UnmanagedType.LPWStr)]string lpszDevice, [MarshalAs(UnmanagedType.LPWStr)]string lpszOutput, int lpInitData); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, int lpInitData); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("gdi32.dll")] + private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteDC(IntPtr hdc); + + [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true)] + private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + + [DllImport("gdi32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteObject(IntPtr hObject); + #endregion + + #region Fields + private int _Height = 0; + private int _Width = 0; + private IntPtr _Hdc = IntPtr.Zero; + private IntPtr _Bmp = IntPtr.Zero; + private IntPtr _BmpOld = IntPtr.Zero; + #endregion + + #region Methods + public IntPtr Hdc + { + get { return _Hdc; } + } + + public IntPtr HBmp + { + get { return _Bmp; } + } + + public int Height + { + get { return _Height; } + set + { + if (_Height != value) + { + _Height = value; + ImageCreate(_Width, _Height); + } + } + } + + public int Width + { + get { return _Width; } + set + { + if (_Width != value) + { + _Width = value; + ImageCreate(_Width, _Height); + } + } + } + + public void SelectImage(Bitmap image) + { + if (Hdc != IntPtr.Zero && image != null) + SelectObject(Hdc, image.GetHbitmap()); + } + + private void ImageCreate(int Width, int Height) + { + IntPtr pHdc = IntPtr.Zero; + + ImageDestroy(); + pHdc = CreateDCA("DISPLAY", "", "", 0); + _Hdc = CreateCompatibleDC(pHdc); + _Bmp = CreateCompatibleBitmap(pHdc, _Width, _Height); + _BmpOld = SelectObject(_Hdc, _Bmp); + if (_BmpOld == IntPtr.Zero) + { + ImageDestroy(); + } + else + { + _Width = Width; + _Height = Height; + } + DeleteDC(pHdc); + pHdc = IntPtr.Zero; + } + + private void ImageDestroy() + { + if (_BmpOld != IntPtr.Zero) + { + SelectObject(_Hdc, _BmpOld); + _BmpOld = IntPtr.Zero; + } + if (_Bmp != IntPtr.Zero) + { + DeleteObject(_Bmp); + _Bmp = IntPtr.Zero; + } + if (_Hdc != IntPtr.Zero) + { + DeleteDC(_Hdc); + _Hdc = IntPtr.Zero; + } + } + + public void Dispose() + { + ImageDestroy(); + } + #endregion + } + #endregion + } +} + diff --git a/CUEControls/PeakMeterCtrl.Designer.cs b/CUEControls/PeakMeterCtrl.Designer.cs new file mode 100644 index 0000000..47d8b59 --- /dev/null +++ b/CUEControls/PeakMeterCtrl.Designer.cs @@ -0,0 +1,39 @@ +using System; +namespace Ernzo.WinForms.Controls +{ + partial class PeakMeterCtrl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + StopAnimation(); + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + + } +} diff --git a/CUEControls/PeakMeterCtrl.cs b/CUEControls/PeakMeterCtrl.cs new file mode 100644 index 0000000..2266db7 --- /dev/null +++ b/CUEControls/PeakMeterCtrl.cs @@ -0,0 +1,767 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2008 Ernest Laurentin (elaurentin@netzero.net) +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +/////////////////////////////////////////////////////////////////////////////// +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Threading; + +namespace Ernzo.WinForms.Controls +{ + public enum PeakMeterStyle + { + PMS_Horizontal = 0, + PMS_Vertical = 1 + } + + internal struct PeakMeterData + { + public int Value; + public int Falloff; + public int Speed; + } + + [ToolboxBitmap(typeof(MyResourceNamespace), "pmicon.bmp")] + public partial class PeakMeterCtrl : Control + { + private const byte DarkenByDefault = 40; + private const byte LightenByDefault = 150; + private const int MinRangeDefault = 60; + private const int MedRangeDefault = 80; + private const int MaxRangeDefault = 100; + private const int FalloffFast = 1; + private const int FalloffNormal = 10; + private const int FalloffSlow = 100; + private const int FalloffDefault = 10; + private const int DecrementPercent = 10; + private const int BandsMin = 1; + private const int BandsMax = 1000; + private const int BandsDefault = 8; + private const int LEDMin = 1; + private const int LEDMax = 1000; + private const int LEDDefault = 8; + private const int cxyMargin = 1; + + private int _AnimDelay; + private int _MinRangeValue; + private int _MedRangeValue; + private int _MaxRangeValue; + private PeakMeterData[] _meterData; + private System.Threading.Timer _animationTimer; + public PeakMeterCtrl() + { + InitializeComponent(); + InitDefault(); + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.SetStyle(ControlStyles.UserPaint, true); + //this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + } + + private void InitDefault() + { + _AnimDelay = Timeout.Infinite; + _MinRangeValue = MinRangeDefault; // [0,60[ + _MedRangeValue = MedRangeDefault; // [60,80[ + _MaxRangeValue = MaxRangeDefault; // [80,100[ + _meterData = null; + _animationTimer = null; + _ShowGrid = true; + _ColoredGrid = false; + _GridColor = Color.Gainsboro; + _ColorNormal = Color.Green; + _ColorMedium = Color.Yellow; + _ColorHigh = Color.Red; + _ColorNormalBack = LightenColor(_ColorNormal, LightenByDefault); + _ColorMediumBack = LightenColor(_ColorMedium, LightenByDefault); + _ColorHighBack = LightenColor(_ColorHigh, LightenByDefault); + _BandsCount = BandsDefault; + _LEDCount = LEDDefault; + _FalloffSpeed = FalloffNormal; + _FalloffEffect = true; + _FalloffColor = DarkenColor(_GridColor, DarkenByDefault); + ResetControl(); + } + + #region Control properties + private PeakMeterStyle _PmsMeterStyle; + [Category("Appearance"), DefaultValue(PeakMeterStyle.PMS_Horizontal)] + public PeakMeterStyle MeterStyle + { + get { return _PmsMeterStyle; } + set { _PmsMeterStyle = value; Refresh(); } + } + + private bool _ShowGrid; + [Category("Appearance"), DefaultValue(true)] + public bool ShowGrid + { + get { return _ShowGrid; } + set { _ShowGrid = value; Refresh(); } + } + + private bool _ColoredGrid; + [Category("Appearance"), DefaultValue(false)] + public bool ColoredGrid + { + get { return _ColoredGrid; } + set { _ColoredGrid = value; Refresh(); } + } + + private Color _GridColor; + [Category("Appearance")] + public Color GridColor + { + get { return _GridColor; } + set { _GridColor = value; Refresh(); } + } + + private Color _ColorNormal; + [Category("Appearance")] + public Color ColorNormal + { + get { return _ColorNormal; } + set { _ColorNormal = value; Refresh(); } + } + + private Color _ColorMedium; + [Category("Appearance")] + public Color ColorMedium + { + get { return _ColorMedium; } + set { _ColorMedium = value; Refresh(); } + } + + private Color _ColorHigh; + [Category("Appearance")] + public Color ColorHigh + { + get { return _ColorHigh; } + set { _ColorHigh = value; Refresh(); } + } + + private Color _ColorNormalBack; + [Category("Appearance")] + public Color ColorNormalBack + { + get { return _ColorNormalBack; } + set { _ColorNormalBack = value; Refresh(); } + } + + private Color _ColorMediumBack; + [Category("Appearance")] + public Color ColorMediumBack + { + get { return _ColorMediumBack; } + set { _ColorMediumBack = value; Refresh(); } + } + + private Color _ColorHighBack; + [Category("Appearance")] + public Color ColorHighBack + { + get { return _ColorHighBack; } + set { _ColorHighBack = value; Refresh(); } + } + + private int _BandsCount; + [Category("Appearance"), DefaultValue(BandsDefault)] + public int BandsCount + { + get { return _BandsCount; } + set { + if (value >= BandsMin && value <= BandsMax) + { + _BandsCount = value; + ResetControl(); + Refresh(); + } + } + } + + private int _LEDCount; + [Category("Appearance"), DefaultValue(LEDDefault)] + public int LEDCount + { + get { return _LEDCount; } + set { + if (value >= LEDMin && value <= LEDMax) + { + _LEDCount = value; + Refresh(); + } + } + } + + private int _FalloffSpeed; + [Category("Falloff Effect"), DefaultValue(FalloffDefault)] + public int FalloffSpeed + { + get { return _FalloffSpeed; } + set { _FalloffSpeed = value; } + } + + private bool _FalloffEffect; + [Category("Falloff Effect"), DefaultValue(true)] + public bool FalloffEffect + { + get { return _FalloffEffect; } + set { _FalloffEffect = value; } + } + + private Color _FalloffColor; + [Category("Falloff Effect")] + public Color FalloffColor + { + get { return _FalloffColor; } + set { _FalloffColor = value; } + } + + #endregion + + [Browsable(false)] + public bool IsActive + { + get { return (_animationTimer != null); } + } + + #region Control Methods + + // support for thread-safe version + private delegate bool StartDelegate(int delay); + /// + /// Start animation + /// + /// + /// + public bool Start(int delay) + { + if (this.InvokeRequired) + { + StartDelegate startDelegate = new StartDelegate(this.Start); + return (bool)this.Invoke(startDelegate, delay); + } + _AnimDelay = delay; + return StartAnimation(delay); + } + + // support for thread-safe version + private delegate bool StopDelegate(); + /// + /// Stop Animation + /// + /// + public bool Stop() + { + if (this.InvokeRequired) + { + StopDelegate stopDelegate = new StopDelegate(this.Stop); + return (bool)this.Invoke(stopDelegate); + } + _AnimDelay = Timeout.Infinite; + return StopAnimation(); + } + + /// + /// Set number of LED bands + /// + /// Number of bands + /// Number of LED per bands + public void SetMeterBands(int BandsCount, int LEDCount) + { + if (BandsCount < BandsMin || BandsCount > BandsMax) + throw new ArgumentOutOfRangeException("BandsCount"); + if (LEDCount < LEDMin || LEDCount > LEDMax) + throw new ArgumentOutOfRangeException("LEDCount"); + _BandsCount = BandsCount; + _LEDCount = LEDCount; + ResetControl(); + Refresh(); + } + + /// + /// Set range info + /// + /// Min Range + /// Medium Range + /// High Range + public void SetRange(int minRangeVal, int medRangeVal, int maxRangeVal) + { + if (maxRangeVal <= medRangeVal || medRangeVal < minRangeVal ) + throw new ArgumentOutOfRangeException("minRangeVal"); + _MinRangeValue = minRangeVal; + _MedRangeValue = medRangeVal; + _MaxRangeValue = maxRangeVal; + ResetControl(); + Refresh(); + } + + // support for thread-safe version + private delegate bool SetDataDelegate(int[] arrayValue, int offset, int size); + /// + /// Set meter band value + /// + /// Array value for the bands + /// Starting offset position + /// Number of values to set + /// + public bool SetData(int[] arrayValue, int offset, int size) + { + if (arrayValue == null) + throw new ArgumentNullException("arrayValue"); + if (arrayValue.Length < (offset + size)) + throw new ArgumentOutOfRangeException("arrayValue"); + + if (this.InvokeRequired) + { + SetDataDelegate setDelegate = new SetDataDelegate(this.SetData); + return (bool)this.Invoke(setDelegate, arrayValue, offset, size); + } + bool isRunning = this.IsActive; + + Monitor.Enter(this._meterData); + + int maxIndex = offset + size; + for (int i = offset; i < maxIndex; i++) + { + if (i < this._meterData.Length) + { + PeakMeterData pm = this._meterData[i]; + pm.Value = Math.Min(arrayValue[i], this._MaxRangeValue); + pm.Value = Math.Max(pm.Value, 0); + if (pm.Falloff < pm.Value) + { + pm.Falloff = pm.Value; + pm.Speed = this._FalloffSpeed; + } + this._meterData[i] = pm; + } + } + Monitor.Exit(this._meterData); + + // check that timer should be restarted + if (_AnimDelay != Timeout.Infinite) + { + if (_animationTimer == null) + { + StartAnimation(_AnimDelay); + } + } + else + { + Refresh(); + } + + return isRunning; + } + #endregion + + /// + /// Make a color darker + /// + /// Color to darken + /// Value to decrease by + protected virtual Color DarkenColor(Color color, byte darkenBy) + { + byte red = (byte)(color.R > darkenBy ? (color.R - darkenBy) : 0); + byte green = (byte)(color.G > darkenBy ? (color.G - darkenBy) : 0); + byte blue = (byte)(color.B > darkenBy ? (color.B - darkenBy) : 0); + return Color.FromArgb(red, green, blue); + } + /// + /// Make a color lighter + /// + /// + /// + /// + protected virtual Color LightenColor(Color color, byte lightenBy) + { + byte red = (byte)((color.R + lightenBy) <= 255 ? (color.R + lightenBy) : 255); + byte green = (byte)((color.G + lightenBy) <= 255 ? (color.G + lightenBy) : 255); + byte blue = (byte)((color.B + lightenBy) <= 255 ? (color.B + lightenBy) : 255); + return Color.FromArgb(red, green, blue); + } + + protected static bool InRange(int value, int rangeMin, int rangeMax) + { + return (value >= rangeMin && value <= rangeMax); + } + + protected void ResetControl() + { + _meterData = new PeakMeterData[_BandsCount]; + PeakMeterData pm; + pm.Value = _MaxRangeValue; + pm.Falloff = _MaxRangeValue; + pm.Speed = _FalloffSpeed; + for (int i = 0; i < _meterData.Length; i++) + { + _meterData[i] = pm; + } + } + protected bool StartAnimation(int period) + { + if ( !IsActive ) + { + TimerCallback timerDelegate = + new TimerCallback(TimerCallback); + _animationTimer = new System.Threading.Timer(timerDelegate, this, Timeout.Infinite, Timeout.Infinite); + } + return _animationTimer.Change(period, period); + } + protected bool StopAnimation() + { + bool result = false; + if ( IsActive ) + { + try + { + result = _animationTimer.Change(Timeout.Infinite, Timeout.Infinite); + _animationTimer.Dispose(); + _animationTimer = null; + result = true; + } + catch (Exception) + { + } + } + return result; + } + + protected override void OnHandleDestroyed(EventArgs e) + { + Stop(); + base.OnHandleDestroyed(e); + } + protected override void OnBackColorChanged(EventArgs e) + { + Refresh(); + } + protected override void OnPaint(PaintEventArgs e) + { + // Calling the base class OnPaint + base.OnPaint(e); + + Graphics g = e.Graphics; + Rectangle rect = new Rectangle(0, 0, this.Width, this.Height); + Brush backColorBrush = new SolidBrush(this.BackColor); + + g.FillRectangle(backColorBrush, rect); + //rect.Inflate(-this.Margin.Left, -this.Margin.Top); + if (MeterStyle == PeakMeterStyle.PMS_Horizontal) + { + DrawHorzBand(g, rect); + } + else + { + DrawVertBand(g, rect); + } + } + + protected void TimerCallback(Object thisObject) + { + try + { + // refresh now! + Control thisControl = thisObject as Control; + if (thisControl != null && thisControl.IsHandleCreated) + { + thisControl.Invoke(new MethodInvoker(Refresh)); + } + else + { + return; + } + } + catch (Exception) + { + // just ignore + } + + int nDecValue = _MaxRangeValue / _LEDCount; + bool noUpdate = true; + + Monitor.Enter(this._meterData); + for (int i = 0; i < _meterData.Length; i++) + { + PeakMeterData pm = _meterData[i]; + + if (pm.Value > 0) + { + pm.Value -= (_LEDCount > 1 ? nDecValue : (_MaxRangeValue * DecrementPercent) / 100); + if (pm.Value < 0) + pm.Value = 0; + noUpdate = false; + } + + if (pm.Speed > 0) + { + pm.Speed -= 1; + noUpdate = false; + } + + if (pm.Speed == 0 && pm.Falloff > 0) + { + pm.Falloff -= (_LEDCount > 1 ? nDecValue >> 1 : 5); + if (pm.Falloff < 0) + pm.Falloff = 0; + noUpdate = false; + } + + // re-assign PeakMeterData + _meterData[i] = pm; + } + Monitor.Exit(this._meterData); + + if (noUpdate) // Stop timer if no more data but do not reset ID + { + StopAnimation(); + } + } + protected void DrawHorzBand(Graphics g, Rectangle rect) + { + int nMaxRange = (_MedRangeValue == 0) ? Math.Abs(_MaxRangeValue - _MinRangeValue) : _MaxRangeValue; + int nVertBands = (_LEDCount > 1 ? _LEDCount : (nMaxRange * DecrementPercent) / 100); + int nMinVertLimit = _MinRangeValue * nVertBands / nMaxRange; + int nMedVertLimit = _MedRangeValue * nVertBands / nMaxRange; + int nMaxVertLimit = nVertBands; + + if (_MedRangeValue == 0) + { + nMedVertLimit = Math.Abs(_MinRangeValue) * nVertBands / nMaxRange; + nMinVertLimit = 0; + } + Size size = new Size(rect.Width/_BandsCount, rect.Height/nVertBands); + Rectangle rcBand = new Rectangle(rect.Location, size); + + // Draw band from bottom + rcBand.Offset(0, rect.Height-size.Height); + int xDecal = (_BandsCount>1 ? cxyMargin : 0); + //int yDecal = 0; + + Color gridPenColor = (this.ShowGrid ? GridColor : BackColor); + Pen gridPen = new Pen(gridPenColor); + Pen fallPen = new Pen( this.FalloffColor ); + + for(int nHorz=0; nHorz < _BandsCount; nHorz++) + { + int nValue = _meterData[nHorz].Value; + int nVertValue = nValue*nVertBands/nMaxRange; + Rectangle rcPrev = rcBand; + + for(int nVert=0; nVert < nVertBands; nVert++) + { + // Find color based on range value + Color colorBand = gridPenColor; + + // Draw grid line (level) bar + if ( this.ShowGrid && (nVert == nMinVertLimit || nVert == nMedVertLimit || nVert == (nVertBands-1))) + { + Point []points = new Point[2]; + points[0].X = rcBand.Left; + points[0].Y = rcBand.Top + (rcBand.Height>>1); + points[1].X = rcBand.Right; + points[1].Y = points[0].Y; + g.DrawPolygon(gridPen, points); + } + + if ( _MedRangeValue == 0 ) + { + int nVertStart = nMedVertLimit+nVertValue; + if ( InRange(nVert, nVertStart, nMedVertLimit-1) ) + colorBand = this.ColorNormal; + else if ( nVert >= nMedVertLimit && InRange(nVert, nMedVertLimit, nVertStart) ) + colorBand = this.ColorHigh; + else { + colorBand = (nVert < nMedVertLimit) ? this.ColorNormalBack : this.ColorHighBack; + } + } + else if ( nVertValue < nVert ) + { + if ( this.ShowGrid && this.ColoredGrid ) + { + if ( InRange(nVert, 0, nMinVertLimit) ) + colorBand = this.ColorNormalBack; + else if ( InRange(nVert, nMinVertLimit+1, nMedVertLimit) ) + colorBand = this.ColorMediumBack; + else if ( InRange(nVert, nMedVertLimit+1, nMaxVertLimit) ) + colorBand = this.ColorHighBack; + } + } else { + if (nValue == 0) + { + if (this.ShowGrid && this.ColoredGrid) + colorBand = this.ColorNormalBack; + } + else if ( InRange(nVert, 0, nMinVertLimit) ) + colorBand = this.ColorNormal; + else if ( InRange(nVert, nMinVertLimit+1, nMedVertLimit) ) + colorBand = this.ColorMedium; + else if ( InRange(nVert, nMedVertLimit+1, nMaxVertLimit) ) + colorBand = this.ColorHigh; + } + + if (colorBand != this.BackColor) + { + SolidBrush fillBrush = new SolidBrush(colorBand); + if (this._LEDCount > 1) + rcBand.Inflate(-cxyMargin, -cxyMargin); + g.FillRectangle(fillBrush, rcBand.Left, rcBand.Top, rcBand.Width+1, rcBand.Height); + if (this._LEDCount > 1) + rcBand.Inflate(cxyMargin, cxyMargin); + } + rcBand.Offset(0, -size.Height); + } + + // Draw falloff effect + if (this.FalloffEffect && this.IsActive) + { + int nMaxHeight = size.Height*nVertBands; + Point []points = new Point[2]; + points[0].X = rcPrev.Left + xDecal; + points[0].Y = rcPrev.Bottom - (_meterData[nHorz].Falloff * nMaxHeight) / _MaxRangeValue; + points[1].X = rcPrev.Right - xDecal; + points[1].Y = points[0].Y; + g.DrawPolygon(fallPen, points); + } + + // Move to Next Horizontal band + rcBand.Offset(size.Width, size.Height * nVertBands); + } + } + protected void DrawVertBand(Graphics g, Rectangle rect) + { + int nMaxRange = (_MedRangeValue == 0) ? Math.Abs(_MaxRangeValue - _MinRangeValue) : _MaxRangeValue; + int nHorzBands = (_LEDCount > 1 ? _LEDCount : (nMaxRange * DecrementPercent) / 100); + int nMinHorzLimit = _MinRangeValue*nHorzBands/nMaxRange; + int nMedHorzLimit = _MedRangeValue*nHorzBands/nMaxRange; + int nMaxHorzLimit = nHorzBands; + + if ( _MedRangeValue == 0 ) + { + nMedHorzLimit = Math.Abs(_MinRangeValue)*nHorzBands/nMaxRange; + nMinHorzLimit = 0; + } + + Size size = new Size(rect.Width/nHorzBands, rect.Height/_BandsCount); + Rectangle rcBand = new Rectangle(rect.Location, size); + + // Draw band from top + rcBand.Offset(0, rect.Height-size.Height*_BandsCount); + //int xDecal = 0; + int yDecal = (_BandsCount>1 ? cxyMargin : 0); + + Color gridPenColor = (this.ShowGrid ? GridColor : BackColor); + Pen gridPen = new Pen(gridPenColor); + Pen fallPen = new Pen( this.FalloffColor ); + + for(int nVert=0; nVert < _BandsCount; nVert++) + { + int nValue = _meterData[nVert].Value; + int nHorzValue = nValue*nHorzBands/nMaxRange; + Rectangle rcPrev = rcBand; + + for(int nHorz=0; nHorz < nHorzBands; nHorz++) + { + // Find color based on range value + Color colorBand = gridPenColor; + + if ( this.ShowGrid && (nHorz == nMinHorzLimit || nHorz == nMedHorzLimit || nHorz == (nHorzBands-1))) + { + Point []points = new Point[2]; + points[0].X = rcBand.Left + (rcBand.Width>>1); + points[0].Y = rcBand.Top; + points[1].X = points[0].X; + points[1].Y = rcBand.Bottom; + g.DrawPolygon(gridPen, points); + } + + if (_MedRangeValue == 0) + { + int nHorzStart = nMedHorzLimit+nHorzValue; + if ( InRange(nHorz, nHorzStart, nMedHorzLimit-1) ) + colorBand = this.ColorNormal; + else if ( nHorz >= nMedHorzLimit && InRange(nHorz, nMedHorzLimit, nHorzStart) ) + colorBand = this.ColorHigh; + else { + colorBand = (nHorz < nMedHorzLimit) ? this.ColorNormalBack : this.ColorHighBack; + } + } + else if ( nHorzValue < nHorz ) + { + if ( this.ShowGrid && this.ColoredGrid ) + { + if ( InRange(nHorz, 0, nMinHorzLimit) ) + colorBand = this.ColorNormalBack; + else if ( InRange(nHorz, nMinHorzLimit+1, nMedHorzLimit) ) + colorBand = this.ColorMediumBack; + else if ( InRange(nHorz, nMedHorzLimit+1, nMaxHorzLimit) ) + colorBand = this.ColorHighBack; + } + } else { + if (nValue == 0) + { + if (this.ShowGrid && this.ColoredGrid) + colorBand = this.ColorNormalBack; + } + else if (InRange(nHorz, 0, nMinHorzLimit)) + colorBand = this.ColorNormal; + else if ( InRange(nHorz, nMinHorzLimit+1, nMedHorzLimit) ) + colorBand = this.ColorMedium; + else if ( InRange(nHorz, nMedHorzLimit+1, nMaxHorzLimit) ) + colorBand = this.ColorHigh; + } + + if (colorBand != this.BackColor) + { + SolidBrush fillBrush = new SolidBrush(colorBand); + if (this._LEDCount > 1) + rcBand.Inflate(-cxyMargin, -cxyMargin); + g.FillRectangle(fillBrush, rcBand.Left, rcBand.Top, rcBand.Width, rcBand.Height+1); + if (this._LEDCount > 1) + rcBand.Inflate(cxyMargin, cxyMargin); + } + rcBand.Offset(size.Width, 0); + } + + // Draw falloff effect + if (this.FalloffEffect && this.IsActive) + { + int nMaxWidth = size.Width * nHorzBands; + Point[] points = new Point[2]; + points[0].X = rcPrev.Left + (_meterData[nVert].Falloff * nMaxWidth) / _MaxRangeValue; + points[0].Y = rcPrev.Top + yDecal; + points[1].X = points[0].X; + points[1].Y = rcPrev.Bottom - yDecal; + g.DrawPolygon(fallPen, points); + } + + // Move to Next Vertical band + rcBand.Offset(-size.Width*nHorzBands, size.Height); + } + } + } +} + +// use this to find resource namespace +internal class MyResourceNamespace +{ +} diff --git a/CUEControls/Properties/Resources.Designer.cs b/CUEControls/Properties/Resources.Designer.cs index 27d4fe8..af671fe 100644 --- a/CUEControls/Properties/Resources.Designer.cs +++ b/CUEControls/Properties/Resources.Designer.cs @@ -66,5 +66,12 @@ namespace CUEControls.Properties { return ((System.Drawing.Icon)(obj)); } } + + internal static System.Drawing.Bitmap pmicon { + get { + object obj = ResourceManager.GetObject("pmicon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/CUEControls/Properties/Resources.resx b/CUEControls/Properties/Resources.resx index fa9cc89..d8636ef 100644 --- a/CUEControls/Properties/Resources.resx +++ b/CUEControls/Properties/Resources.resx @@ -121,4 +121,7 @@ ..\Resources\folder.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\pmicon.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/CUEControls/Resources/pmicon.bmp b/CUEControls/Resources/pmicon.bmp new file mode 100644 index 0000000..4ffc45a Binary files /dev/null and b/CUEControls/Resources/pmicon.bmp differ diff --git a/CUEControls/pmicon.bmp b/CUEControls/pmicon.bmp new file mode 100644 index 0000000..4ffc45a Binary files /dev/null and b/CUEControls/pmicon.bmp differ