Click here to Skip to main content
15,897,371 members
Articles / Desktop Programming / MFC

Multimedia PeakMeter Control

Rate me:
Please Sign up or sign in to vote.
4.94/5 (64 votes)
4 Sep 2008CPOL6 min read 321.2K   22.3K   222  
Multimedia PeakMeter control - .NET version
///////////////////////////////////////////////////////////////////////////////
//  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), "PeakMeterCtrl.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);
        /// <summary>
        /// Start animation
        /// </summary>
        /// <param name="delay"></param>
        /// <returns></returns>
        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();
        /// <summary>
        /// Stop Animation
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            if (this.InvokeRequired)
            {
                StopDelegate stopDelegate = new StopDelegate(this.Stop);
                return (bool)this.Invoke(stopDelegate);
            }
            _AnimDelay = Timeout.Infinite;
            return StopAnimation();
        }

        /// <summary>
        /// Set number of LED bands
        /// </summary>
        /// <param name="BandsCount">Number of bands</param>
        /// <param name="LEDCount">Number of LED per bands</param>
        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();
        }

        /// <summary>
        /// Set range info
        /// </summary>
        /// <param name="minRangeVal">Min Range</param>
        /// <param name="medRangeVal">Medium Range</param>
        /// <param name="maxRangeVal">High Range</param>
        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);
        /// <summary>
        /// Set meter band value
        /// </summary>
        /// <param name="arrayValue">Array value for the bands</param>
        /// <param name="offset">Starting offset position</param>
        /// <param name="size">Number of values to set</param>
        /// <returns></returns>
        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

        /// <summary>
        /// Make a color darker
        /// </summary>
        /// <param name="color">Color to darken</param>
        /// <param name="darkenBy">Value to decrease by</param>
        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);
        }
        /// <summary>
        /// Make a color lighter
        /// </summary>
        /// <param name="color"></param>
        /// <param name="lightenBy"></param>
        /// <returns></returns>
        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
{
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Ernest is a multi-discipline software engineer.
Skilled at software design and development for all Windows platforms.
-
MCSD (C#, .NET)
Interests: User Interface, GDI/GDI+, Scripting, Android, iOS, Windows Mobile.
Programming Skills: C/C++, C#, Java (Android), VB and ASP.NET.

I hope you will enjoy my contributions.

Comments and Discussions