Click here to Skip to main content
15,881,172 members
Articles / Desktop Programming / Windows Forms

Crystal Image Toolkit: thumbnail image control and picture viewing.

Rate me:
Please Sign up or sign in to vote.
4.68/5 (21 votes)
11 May 2011LGPL37 min read 109.4K   8.3K   95  
Thumbnail and image viewing controls for Windows Forms, using C#.
#region gnu_license

/*
    Crystal Controls - C# control library containing the following tools:
        CrystalControl - base class
        CrystalGradientControl - a control that can either have a gradient background or be totally transparent.
        CrystalLabel - a homegrown label that can have a gradient or transparent background.
        CrystalPanel - a panel that can have a gradient or transparent background.
        CrystalTrackBar - a homegrown trackbar that can have a gradient or transparent background.
        CrystalToolStripTrackBar - a host for CrystalTrackBar that allows it to work in a ToolStrip.
        
        CrystalImageGridView - a control that hosts thumbnail images in a virtual grid.
        CrystalImageGridModel - a data model that holds a collection of CrystalImageItems
                                to feed to CrystalImageGridView.
        CrystalImageItem - a class that describes an Image file.
        CrystalThumbnailer - provides thumbnailing methods for images.

        CrystalCollector - a base class for a controller that links 
                            CrystalImageGridView to the CrystalImageGridModel.
        CrystalFileCollector - a controller that works on disk-based Image files.
        CrystalDesignCollector - a controller that works in Visual Studio toolbox designer.
        CrystalMemoryCollector - a controller that can be used to add images from memory.
        CrystalMemoryZipCollector - a controller that accesses images in zip files by streaming them into memory.
        CrystalZipCollector - a controller that accesses images in zip files by unpacking them.
        CrystalRarCollector - a controller that accesses images in rar files by unpacking them.

        CrystalPictureBox - a picture box control, derived from CrystalGradientControl.
        CrystalPictureShow - a control for viewing images and processing slideshows.
        CrystalComicShow - a control for viewing comic-book images in the CDisplay format.
 
    Copyright (C) 2006, 2008 Richard Guion
    Attilan Software Factory: http://www.attilan.com
    Contact: richard@attilan.com

   Version 1.0.0
        This is a work in progress: USE AT YOUR OWN RISK!  Interfaces/Methods may change!
 
    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using Attilan.Crystal.Tools;

namespace Attilan.Crystal.Controls
{
    /// <summary>
    /// Represents visual thumb states for CrystalTrackBar
    /// </summary>
    public enum CrystalThumbState
    {
        /// <summary>
        /// The mouse is outside the CrystalTrackBar control.
        /// </summary>
        Normal = 0,
        /// <summary>
        /// user has clicked on the thumb inside the CrystalTrackBar control.
        /// </summary>
        Pressed = 1,
        /// <summary>
        /// The thumb inside the CrystalTrackBar control is being erased.
        /// </summary>
        Erased = 2,
        /// <summary>
        /// The mouse has entered the CrystalTrackBar control.
        /// </summary>
        Hover = 3,
        /// <summary>
        /// The control is disabled and the thumb state is disabled.
        /// </summary>
        Disabled = 4
    }

    /// <summary>
    /// CrystalTrackBar is a TrackBar replacement that can have a gradient or
    /// transparent background.
    /// </summary>
    [DesignerCategory("code")]
    [
        ToolboxItem(true),
        ToolboxBitmap(typeof (resfinder), "CrystalToolkit.Resources.CrystalTrackBar.bmp")
        //ToolboxBitmap(typeof(System.Windows.Forms.TrackBar))
    ]
    public class CrystalTrackBar : CrystalGradientControl
    {
        #region Fields

        // rects for trackbar, tickbar, and thumb
        private Rectangle _trackRectangle = new Rectangle();
        private Rectangle _ticksRectangleBottomRight = new Rectangle();
        private Rectangle _ticksRectangleTopLeft = new Rectangle();
        private RectangleF _thumbRectangle = new Rectangle();

        // tick info
        private int _value = 0;
        private int _currentTickValue = 0;
        private int _numberTicks = 11;
        private int _minimum = 0;
        private int _maximum = 10;
        private int _tickFrequency = 1;
        private int _smallChange = 1;
        private int _largeChange = 5;
        private float _tickSpace = 0;
        private float _tickSingle = 0;
        private TickStyle _tickStyle = TickStyle.BottomRight;
        private EdgeStyle _tickEdgeStyle = EdgeStyle.Etched;

        private List<int> _tickValues = null;

        // states
        private CrystalThumbState _thumbState = CrystalThumbState.Normal;
        private Orientation _orientation = Orientation.Horizontal;

        // track bar
        private int _trackBarMargin = 10;
        private int _trackBarHeight = 4;

        // thumb images
        private Image _thumbImage = null;
        private Image _thumbHotImage = null;
        private Image _thumbWarmImage = null;
        private Image _thumbDisabledImage = null;

        // keyboard input
        private bool _keyboardControl = true;

        #endregion

        #region Events

        /// <summary>
        /// Any subscribers to the event handler will be called when CrystalTrackBar's value has changed.
        /// </summary>
        public event EventHandler ValueChanged;

        #endregion

        #region Public Properties

        /// <summary>
        /// Indicates where the ticks appear on the CrystalTrackBar.
        /// </summary>
        [
            Browsable(true),
            Category("Appearance"),
            DefaultValue(TickStyle.BottomRight),
            Description("Indicates where the ticks appear on the CrystalTrackBar.")
        ]
        public TickStyle TickStyle
        {
            get { return _tickStyle; }
            set
            {
                if (_tickStyle != value)
                {
                    _tickStyle = value;
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// The position of the thumb on the slider.
        /// </summary>
        [
            Bindable(true),
            Browsable(true),
            Category("Behavior"),
            DefaultValue(0),
            Description("The position of the thumb on the slider.")
        ]
        public int Value
        {
            get { return _value; }
            set
            {
                if (value < _minimum || value > _maximum)
                    throw new ArgumentException(
                        string.Format(
                            "'{0}' is not a valid value for 'Value'. 'Value' should be between '{1}' and '{2}'", value,
                            _minimum, _maximum));

                // BUG FIX: Process part of this code even with value change,
                // to make the thumb display properly.

                // Cache the boolean that tells us if the value has changed.
                bool valChanged = (_value != value);

                // save the new value
                _value = value;
                _currentTickValue = _value;

                // Update subscribers when all of the above is done.
                if ((ValueChanged != null) && (valChanged))
                    ValueChanged(this, new EventArgs());

                // Recalc the thumb position                 
                UpdateThumbRectangle();

                if (!IsThumbPressed())
                {
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// The maximum value for the thumb position of the slider of the CrystalTrackBar.
        /// </summary>
        [
            Browsable(true),
            Category("Behavior"),
            DefaultValue(10),
            Description("The maximum value for the thumb position of the slider of the CrystalTrackBar.")
        ]
        public int Maximum
        {
            get { return _maximum; }
            set
            {
                if (_maximum != value)
                {
                    _maximum = value;
                    //CalcTicks();
                    SetupTrackBar();
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// The minimum value for the thumb position of the slider of the CrystalTrackBar.
        /// </summary>
        [
            Browsable(true),
            Category("Behavior"),
            DefaultValue(0),
            Description("The minimum value for the thumb position of the slider of the CrystalTrackBar.")
        ]
        public int Minimum
        {
            get { return _minimum; }
            set
            {
                if (_minimum != value)
                {
                    _minimum = value;
                    //CalcTicks();
                    SetupTrackBar();
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// Indicates the interval between ticks on the CrystalTrackBar.
        /// </summary>
        [
            Browsable(true),
            Category("Appearance"),
            DefaultValue(1),
            Description("Indicates the interval between ticks on the CrystalTrackBar.")
        ]
        public int TickFrequency
        {
            get { return _tickFrequency; }
            set
            {
                if (_tickFrequency != value)
                {
                    _tickFrequency = value;
                    SetupTrackBar();
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// The number of positions the slider moves in response to keyboard input (arrow keys).
        /// </summary>
        [
            Browsable(true),
            Category("Behavior"),
            DefaultValue(1),
            Description("The number of positions the slider moves in response to keyboard input (arrow keys).")
        ]
        public int SmallChange
        {
            get { return _smallChange; }
            set { _smallChange = value; }
        }

        /// <summary>
        /// The number of positions the slider moves in response to mouse clicks or the PAGE UP and PAGE DOWN keys.
        /// </summary>
        [
            Browsable(true),
            Category("Behavior"),
            DefaultValue(5),
            Description(
                "The number of positions the slider moves in response to mouse clicks or the PAGE UP and PAGE DOWN keys."
                )
        ]
        public int LargeChange
        {
            get { return _largeChange; }
            set { _largeChange = value; }
        }

        /// <summary>
        /// A list of integer values for each tick item on trackbar.
        /// </summary>
        [
            Browsable(false),
            Description("A list (List<int>) of integer values for each tick item on trackbar.")
        ]
        public List<int> TickValues
        {
            get { return _tickValues; }
        }

        /// <summary>
        /// Allows the user to adjust trackbar with the keys: left arrow, right arrow, up, down, home, end.
        /// </summary>
        [
            Browsable(true),
            Category("Behavior"),
            DefaultValue(true),
            Description(
                "Allows the user to adjust trackbar with the keys: left arrow, right arrow, up, down, home, end.")
        ]
        public bool KeyboardControl
        {
            get { return _keyboardControl; }
            set { _keyboardControl = value; }
        }

        /// <summary>
        /// Allows the CrystalTrackBar to be displayed horizontally or vertically.
        /// </summary>
        [
            Browsable(true),
            Category("Appearance"),
            Description("Allows the CrystalTrackBar to be displayed horizontally or vertically.")
        ]
        public Orientation Orientation
        {
            get { return _orientation; }
            set
            {
                if (_orientation != value)
                {
                    _orientation = value;

                    // flip the size
                    Size = new Size(Size.Height, Size.Width);
                }
            }
        }

        /// <summary>
        /// Determines the appearance of the ticks on the CrystalTrackBar.
        /// </summary>
        [
            Browsable(true),
            Category("Appearance"),
            Description("Determines the appearance of the ticks on the CrystalTrackBar.")
        ]
        public EdgeStyle TickEdgeStyle
        {
            get { return _tickEdgeStyle; }
            set
            {
                if (_tickEdgeStyle != value)
                {
                    _tickEdgeStyle = value;
                    RedrawControl();
                }
            }
        }

        /// <summary>
        /// Enabled thumb image.
        /// </summary>
        public Image ThumbNormalImage
        {
            get { return _thumbImage; }
            set { _thumbImage = value; }
        }

        /// <summary>
        /// Pressed thumb image. 
        /// </summary>
        public Image ThumbPressImage
        {
            get { return _thumbHotImage; }
            set { _thumbHotImage = value; }
        }

        /// <summary>
        /// Hover thumb image.
        /// </summary>
        public Image ThumbHoverImage
        {
            get { return _thumbWarmImage; }
            set { _thumbWarmImage = value; }
        }

        /// <summary>
        /// Disabled thumb image.
        /// </summary>
        public Image ThumbDisabledImage
        {
            get { return _thumbDisabledImage; }
            set { _thumbDisabledImage = value; }
        }

        #endregion

        #region Constructors and Initializers

        /// <summary>
        /// Default constructor for CrystalTrackBar.
        /// </summary>
        public CrystalTrackBar()
        {
            // this call says "I'll draw it myself"
            SetStyle(ControlStyles.AllPaintingInWmPaint |
                     ControlStyles.UserPaint |
                     ControlStyles.ResizeRedraw |
                     ControlStyles.DoubleBuffer, true);

            SetDefaultSize();
        }

        /// <summary>
        /// Sets the default size for the control, used for display in the Forms designer.
        /// </summary>
        protected void SetDefaultSize()
        {
            // Set default size so that user will see the control in the designer
            if (IsHorizontal())
                Size = new Size(105, 45);
            else
                Size = new Size(45, 105);
        }

        #endregion

        #region Tick Methods

        /// <summary>
        /// Calculates the tick intervals based on the Minimum, Maximum, and TickFrequency values.
        /// </summary>
        protected virtual void CalcTicks()
        {
            _numberTicks = 0;
            _tickValues = new List<int>();
            for (int tickCount = _minimum; tickCount <= _maximum; tickCount += _tickFrequency)
            {
                _tickValues.Add(tickCount);
                _numberTicks++;
            }

            // Make sure that maximum value is the last value.
            _tickValues.RemoveAt(_numberTicks - 1);
            _tickValues.Add(_maximum);
        }

        /// <summary>
        /// Draws the ticks using the TrackBarRenderer.
        /// </summary>
        /// <param name="gfx">Graphics object.</param>
        private void DrawRenderTicks(Graphics gfx)
        {
            if (TickStyle != TickStyle.None)
            {
                if (IsHorizontal())
                {
                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.BottomRight))
                        TrackBarRenderer.DrawHorizontalTicks(gfx, _ticksRectangleBottomRight, _numberTicks,
                                                             _tickEdgeStyle);

                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.TopLeft))
                        TrackBarRenderer.DrawHorizontalTicks(gfx, _ticksRectangleTopLeft, _numberTicks, _tickEdgeStyle);
                }
                else
                {
                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.BottomRight))
                        TrackBarRenderer.DrawVerticalTicks(gfx, _ticksRectangleBottomRight, _numberTicks, _tickEdgeStyle);

                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.TopLeft))
                        TrackBarRenderer.DrawVerticalTicks(gfx, _ticksRectangleTopLeft, _numberTicks, _tickEdgeStyle);
                }
            }
        }

        /// <summary>
        /// Draws the Ticks using a line, horizontally.
        /// </summary>
        /// <param name="gfx">Graphics object.</param>
        /// <param name="ticksRect">Ticks rectangle.</param>
        private void DrawHorizLineTicks(Graphics gfx, Rectangle ticksRect)
        {
            for (int index = 0; index < _numberTicks; index++)
            {
                // Calculate where the current tick value X coordinate is.
                float xTick = ticksRect.X;
                xTick += (_tickSpace*(index));

                using (Pen linePen = new Pen(Color.White, 2))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, xTick,
                                 ticksRect.Y,
                                 xTick,
                                 ticksRect.Y + ticksRect.Height);
                }
            }
        }

        /// <summary>
        /// Draws the Ticks using a line, vertically.
        /// </summary>
        /// <param name="gfx">Graphics object.</param>
        /// <param name="ticksRect">Ticks rectangle.</param>
        private void DrawVertLineTicks(Graphics gfx, Rectangle ticksRect)
        {
            for (int index = 0; index < _numberTicks; index++)
            {
                // Calculate where the current tick value X coordinate is.
                float yTick = ticksRect.Y;
                yTick += (_tickSpace*(index));

                float xTick = ticksRect.X;

                using (Pen linePen = new Pen(Color.White, 2))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, xTick,
                                 yTick,
                                 xTick + ticksRect.Width,
                                 yTick);
                }
            }
        }

        /// <summary>
        /// Draws the Ticks using a dash line.
        /// </summary>
        /// <param name="gfx">Graphics object.</param>
        private void DrawLineTicks(Graphics gfx)
        {
            if (TickStyle != TickStyle.None)
            {
                if (IsHorizontal())
                {
                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.BottomRight))
                        DrawHorizLineTicks(gfx, _ticksRectangleBottomRight);

                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.TopLeft))
                        DrawHorizLineTicks(gfx, _ticksRectangleBottomRight);
                }
                else
                {
                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.BottomRight))
                        DrawVertLineTicks(gfx, _ticksRectangleBottomRight);

                    if ((_tickStyle == TickStyle.Both) || (_tickStyle == TickStyle.TopLeft))
                        DrawVertLineTicks(gfx, _ticksRectangleTopLeft);
                }
            }
        }

        /// <summary>
        /// Draws the ticks above, below, left, or right of the trackbar.
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        protected virtual void DrawTicks(Graphics gfx)
        {
            if (TrackBarRenderer.IsSupported)
                DrawRenderTicks(gfx);
            else
                DrawLineTicks(gfx);
        }

        /// <summary>
        /// Calculates the current X tick coordinate for a horizontal CrystalTrackBar.
        /// </summary>
        /// <returns></returns>
        protected float CurrentTickXCoordinate()
        {
            // Calculate where the current tick value X coordinate is.
            float xTick = _tickSingle*_currentTickValue;
            xTick += _trackBarMargin;

            // Take half the width of the thumb...
            float halfThumb = _thumbRectangle.Width/2.0f;

            // ...subtract this from the current X tick and return
            return xTick - halfThumb;
        }

        /// <summary>
        /// Calculates the current Y tick coordinate for a horizontal CrystalTrackBar.
        /// </summary>
        /// <returns></returns>
        protected float CurrentTickYCoordinate()
        {
            // Calculate where the current tick value X coordinate is.
            float yTick = _tickSingle*_currentTickValue;
            yTick += _trackBarMargin;

            // Take half the height of the thumb...
            float halfThumb = _thumbRectangle.Height/2;

            // ...subtract this from the current Y tick and return
            return yTick - halfThumb;
        }

        /// <summary>
        /// Returns the current tick coordinate based on the orientation of the CrystalTrackBar.
        /// </summary>
        /// <returns>Returns the current tick coordinate.</returns>
        protected virtual float CurrentThumbTickCoordinate()
        {
            if (IsHorizontal())
                return CurrentTickXCoordinate();
            else
                return CurrentTickYCoordinate();
        }

        /// <summary>
        /// Returns the thumb tick at the given position
        /// </summary>
        /// <param name="location">Point location to determine the thumb tick</param>
        /// <returns>Thumb tick integer.</returns>
        protected virtual int GetThumbTick(Point location)
        {
            //float xVal = location.X - _trackBarMargin;
            float valCoord;
            if (IsHorizontal())
                valCoord = location.X;
            else
                valCoord = location.Y;

            if (valCoord > 0.0f)
            {
                int retval = (int) (valCoord/_tickSingle);
                return retval;
            }

            return 0;
        }

        #endregion

        #region TrackBar Methods

        /// <summary>
        /// Draws the TrackBar using TrackBarRenderer
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        private void DrawRendererTrackBar(Graphics gfx)
        {
            if (IsHorizontal())
                TrackBarRenderer.DrawHorizontalTrack(gfx, _trackRectangle);
            else
                TrackBarRenderer.DrawVerticalTrack(gfx, _trackRectangle);
        }

        /// <summary>
        /// Draws the TrackBar using a line.
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        private void DrawLineTrackBar(Graphics gfx)
        {
            if (IsHorizontal())
            {
                using (Pen linePen = new Pen(Color.White, 4))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, _trackRectangle.X, _trackRectangle.Y + 2,
                                 _trackRectangle.X + _trackRectangle.Width,
                                 _trackRectangle.Y + 2);
                }
                using (Pen linePen = new Pen(Color.Gray, 2))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, _trackRectangle.X, _trackRectangle.Y,
                                 _trackRectangle.X + _trackRectangle.Width,
                                 _trackRectangle.Y);
                }
            }
            else
            {
                using (Pen linePen = new Pen(Color.White, 4))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, _trackRectangle.X + 2, _trackRectangle.Y,
                                 _trackRectangle.X + 2,
                                 _trackRectangle.Y + _trackRectangle.Height);
                }
                using (Pen linePen = new Pen(Color.Gray, 2))
                {
                    linePen.EndCap = LineCap.Round;
                    gfx.DrawLine(linePen, _trackRectangle.X, _trackRectangle.Y,
                                 _trackRectangle.X,
                                 _trackRectangle.Y + _trackRectangle.Height);
                }
            }
        }

        /// <summary>
        /// Draws the solid track bar line.
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        protected virtual void DrawTrackBar(Graphics gfx)
        {
            if (!TrackBarRenderer.IsSupported)
                DrawLineTrackBar(gfx);
            else
                DrawRendererTrackBar(gfx);
        }

        /// <summary>
        /// Calculates the sizes of the bar, thumb, and ticks rectangle for a horizontal track bar.
        /// </summary>
        private void SetupHorizontalTrackBar()
        {
            using (Graphics g = CreateGraphics())
            {
                _trackRectangle.X = ClientRectangle.X + _trackBarMargin;
                _trackRectangle.Y = ClientRectangle.Height/2;
                _trackRectangle.Width = ClientRectangle.Width - (_trackBarMargin*2);
                _trackRectangle.Height = _trackBarHeight;

                // Calculate the size of the rectangle in which to 
                // draw the ticks.
                _ticksRectangleBottomRight.X = _trackRectangle.X;
                _ticksRectangleBottomRight.Y = _trackRectangle.Y + 8; // ticks on the bottom
                _ticksRectangleBottomRight.Width = _trackRectangle.Width;
                _ticksRectangleBottomRight.Height = 4;

                _ticksRectangleTopLeft = _ticksRectangleBottomRight;
                _ticksRectangleTopLeft.Y = _trackRectangle.Y - 8; // ticks on the Top

                _tickSpace = ((float) _ticksRectangleBottomRight.Width - 1)/
                             ((float) _numberTicks - 1);

                float numTicks = Maximum - Minimum;
                _tickSingle = _trackRectangle.Width/numTicks;

                // Calculate the size of the thumb.
                // w = 11.0, h = 21.0
                if (TrackBarRenderer.IsSupported)
                {
                    _thumbRectangle.Size =
                        TrackBarRenderer.GetBottomPointingThumbSize(g,
                                                                    TrackBarThumbState.Normal);
                }
                else
                {
                    _thumbRectangle.Size = new SizeF(11.0f, 21.0f);
                }

                UpdateThumbRectangle();
                _thumbRectangle.Y = _trackRectangle.Y - 8;
            }
        }

        /// <summary>
        /// Calculates the sizes of the bar, thumb, and ticks rectangle for a vertical track bar.
        /// </summary>
        private void SetupVerticalTrackBar()
        {
            using (Graphics g = CreateGraphics())
            {
                _trackRectangle.X = ClientRectangle.Width/2;
                _trackRectangle.Y = ClientRectangle.Y + _trackBarMargin;
                _trackRectangle.Width = _trackBarHeight;
                _trackRectangle.Height = ClientRectangle.Height - (_trackBarMargin*2);

                // Calculate the size of the rectangle in which to 
                // draw the ticks.
                _ticksRectangleBottomRight.Y = _trackRectangle.Y;
                _ticksRectangleBottomRight.X = _trackRectangle.X + 8; // ticks on the right
                _ticksRectangleBottomRight.Width = 4;
                _ticksRectangleBottomRight.Height = _trackRectangle.Height;

                _ticksRectangleTopLeft = _ticksRectangleBottomRight;
                _ticksRectangleTopLeft.X = _trackRectangle.X - 8; // ticks on the left

                _tickSpace = ((float) _ticksRectangleBottomRight.Height - 1)/
                             ((float) _numberTicks - 1);

                float numTicks = Maximum - Minimum;
                _tickSingle = _trackRectangle.Height/numTicks;

                // Calculate the size of the thumb.
                // w = 21.0, h = 11.0
                if (TrackBarRenderer.IsSupported)
                {
                    _thumbRectangle.Size =
                        TrackBarRenderer.GetRightPointingThumbSize(g,
                                                                   TrackBarThumbState.Normal);
                }
                else
                {
                    _thumbRectangle.Size = new SizeF(21.0f, 11.0f);
                }

                UpdateThumbRectangle();
                _thumbRectangle.X = _trackRectangle.X - 8;
            }
        }

        protected virtual void SetupThumbImages()
        {
            GetTransparentThumbImage(CrystalThumbState.Disabled);
            GetTransparentThumbImage(CrystalThumbState.Normal);
            GetTransparentThumbImage(CrystalThumbState.Pressed);
            GetTransparentThumbImage(CrystalThumbState.Hover);
        }

        /// <summary>
        /// Calculates the sizes of the bar, thumb, and ticks rectangle.
        /// </summary>
        protected virtual void SetupTrackBar()
        {
            SetupThumbImages();

            CalcTicks();

            if (IsHorizontal())
                SetupHorizontalTrackBar();
            else
                SetupVerticalTrackBar();
        }

        /// <summary>
        /// Tells the CrystalTrackBar that it needs to repaint the entire client area.
        /// </summary>
        public virtual void RedrawTrackBar()
        {
            Rectangle rc = new Rectangle(Location, Size);
            base.InvalidateEx(rc);
        }

        #endregion

        #region Thumb Methods

        /// <summary>
        /// Gets the string containing the resource name of the thumb image matching the "Normal" state.
        /// </summary>
        /// <returns>The string containing the resource name of the image.</returns>
        protected static string GetNormalThumbImageName()
        {
            return CrystalTools.ToolstripThumbImageName;
        }

        /// <summary>
        /// Gets the string containing the resource name of the thumb image matching the "Hover" state.
        /// </summary>
        /// <returns>The string containing the resource name of the image.</returns>
        protected static string GetWarmThumbImageName()
        {
            return CrystalTools.ToolstripThumbWarmImageName;
        }

        /// <summary>
        /// Gets the string containing the resource name of the thumb image matching the "Pressed" state.
        /// </summary>
        /// <returns>The string containing the resource name of the image.</returns>
        protected static string GetHotThumbImageName()
        {
            return CrystalTools.ToolstripThumbHotImageName;
        }

        /// <summary>
        /// Gets the string containing the resource name of the thumb image matching the "Disabled" state.
        /// </summary>
        /// <returns>The string containing the resource name of the image.</returns>
        protected static string GetDisabledThumbImageName()
        {
            return CrystalTools.ToolstripThumbDisabledImageName;
        }

        /// <summary>
        /// Gets the thumb image name that matches the given state.
        /// </summary>
        /// <param name="thumbState">The state of the desired thumb image.</param>
        /// <returns></returns>
        protected static string GetThumbImageName(CrystalThumbState thumbState)
        {
            switch (thumbState)
            {
                case CrystalThumbState.Pressed:
                    return GetHotThumbImageName();
                case CrystalThumbState.Hover:
                    return GetWarmThumbImageName();
                case CrystalThumbState.Disabled:
                    return GetDisabledThumbImageName();
                default:
                    return GetNormalThumbImageName();
            }
        }

        /// <summary>
        /// Gets the image field matching a given thumb state.
        /// </summary>
        /// <param name="thumbState">The desired thumb state.</param>
        /// <returns>The Image matching the thumb state.</returns>
        protected Image GetCachedThumbImage(CrystalThumbState thumbState)
        {
            switch (thumbState)
            {
                case CrystalThumbState.Pressed:
                    return ThumbPressImage;
                case CrystalThumbState.Hover:
                    return ThumbHoverImage;
                case CrystalThumbState.Disabled:
                    return ThumbDisabledImage;
                default:
                    return ThumbNormalImage;
            }
        }

        /// <summary>
        /// Sets the image field matching a given thumb state, allowing us to cache the image.
        /// </summary>
        /// <param name="thumbState">The desired thumb state.</param>
        /// <param name="thumbImage">The image to cache.</param>
        protected void SetCachedThumbImage(CrystalThumbState thumbState, Image thumbImage)
        {
            switch (thumbState)
            {
                case CrystalThumbState.Pressed:
                    ThumbPressImage = thumbImage;
                    break;
                case CrystalThumbState.Hover:
                    ThumbHoverImage = thumbImage;
                    break;
                case CrystalThumbState.Disabled:
                    ThumbDisabledImage = thumbImage;
                    break;
                default:
                    ThumbNormalImage = thumbImage;
                    break;
            }
        }

        protected virtual Image GetTransparentThumbImage(CrystalThumbState thumbState)
        {
            Image thumbImage = GetCachedThumbImage(thumbState);

            if (thumbImage == null)
            {
                thumbImage = CrystalTools.GetAssemblyImage(
                    Assembly.GetAssembly(typeof(CrystalTrackBar)),
                    GetThumbImageName(thumbState));

                thumbImage = CrystalTools.MakeTransparentImage(thumbImage);

                SetCachedThumbImage(thumbState, thumbImage);
            }

            return thumbImage;
        }

        /// <summary>
        /// Gets the thumb image matching the current thumb state.
        /// </summary>
        /// <returns>The image matching the current thumb state.</returns>
        protected Image GetThumbImage()
        {
            return GetTransparentThumbImage(_thumbState);
        }

        /// <summary>
        /// Draws the image of the thumb on the CrystalTrackBar.
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        /// <param name="thumbRect">The rectangle area where the thumb should be drawn.</param>
        protected void DrawThumbImage(Graphics gfx, RectangleF thumbRect)
        {
            Image thumbImage = GetThumbImage();

            if (thumbImage != null)
            {
                if (IsHorizontal())
                {
                    // The thumb images in our assembly are smaller than the
                    // actual thumb rect supplied by TrackBarRenderer.
                    // Drawing smaller than the system rect allows us to erase
                    // the thumb easier.                    
                    thumbRect.Height = thumbImage.Height;
                    thumbRect.Width = thumbImage.Width;
                }
                else
                {
                    // If the track bar is vertical, we're going to take
                    // the horizontal thumb image and rotate it to match
                    // the tickstyle.
                    Bitmap flipImage = new Bitmap(thumbImage);

                    if (_tickStyle == TickStyle.TopLeft)
                        flipImage.RotateFlip(RotateFlipType.Rotate90FlipNone);
                    else
                        flipImage.RotateFlip(RotateFlipType.Rotate270FlipNone);
                    thumbImage = flipImage;

                    // The thumb images in our assembly are smaller than the
                    // actual thumb rect supplied by TrackBarRenderer.
                    // Drawing smaller than the system rect allows us to erase
                    // the thumb easier.
                    thumbRect.Height = thumbImage.Height;
                    thumbRect.Width = thumbImage.Width;
                }

                gfx.DrawImage(thumbImage, thumbRect);
            }
        }

        /// <summary>
        /// Draws the thumb on the CrystalTrackBar.
        /// </summary>
        /// <param name="gfx">The Graphics object to draw on.</param>
        protected virtual void DrawThumb(Graphics gfx)
        {
            // if the thumb is getting erased, we just want to draw
            // the track bar and ticks.  A second repaint will have us
            // draw the thumb in an updated position later.
            if (IsThumbErasing())
                return;

            DrawThumbImage(gfx, _thumbRectangle);
        }

        /// <summary>
        /// Erases the thumb from the CrystalTrackBar.
        /// </summary>
        protected virtual void EraseThumb()
        {
            CrystalThumbState oldThumbState = _thumbState;
            _thumbState = CrystalThumbState.Erased;

            // This will force a repaint in the base class...
            RectangleF _eraseRect = new RectangleF(_thumbRectangle.Location, _thumbRectangle.Size);
            _eraseRect.Inflate(2.0f, 2.0f);
            InvalidateEx(_thumbRectangle);

            //...by the time we reach here, we know that the
            // trackbar and ticks have been repainted without the thumb.
            _thumbState = oldThumbState;
        }

        /// <summary>
        /// Updates the thumb using the current tick value of the CrystalTrackBar.
        /// </summary>
        private void UpdateThumbRectangle()
        {
            if (IsHorizontal())
                _thumbRectangle.X = CurrentTickXCoordinate();
            else
                _thumbRectangle.Y = CurrentTickYCoordinate();
        }

        #endregion

        #region Helper Methods: States, Tick Adjustment, etc.

        /// <summary>
        /// Returns the current X or Y location depending on the orientation.
        /// </summary>
        /// <param name="location">A point within the CrystalTrackBar.</param>
        /// <returns>Returns the current X or Y location depending on the orientation.</returns>
        protected float CurrentLocation(Point location)
        {
            if (IsHorizontal())
                return location.X;
            else
                return location.Y;
        }

        /// <summary>
        /// Determines if the current thumb position on the track bar has changed.
        /// </summary>
        /// <param name="location">The current point location of the mouse.</param>
        /// <returns>Boolean indicating that the thumb position has changed.</returns>
        private bool HasTrackValueChanged(Point location)
        {
#if CRYSTAL_TRACKBAR_DEBUG
            System.Console.WriteLine("\nHasTrackValueChanged:");
            System.Console.WriteLine("_currentTickValue = {0}, CurrentLocation(location) = {1}, CurrentThumbTick = {2}",
                                    _currentTickValue, CurrentLocation(location), CurrentThumbTickCoordinate());
            System.Console.WriteLine("Minimum = {0}, Maximum = {1}", Minimum, Maximum);
#endif

            if (_currentTickValue < Maximum &&
                CurrentLocation(location) > CurrentThumbTickCoordinate())
            {
                return true;
            }
                // Track movements to the next tick to the left, if 
                // cursor has moved halfway to the next tick.
            else if (_currentTickValue > Minimum &&
                     CurrentLocation(location) < CurrentThumbTickCoordinate())
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Increments the value of the CrystalTrackBar.
        /// </summary>
        protected void IncrementValue(int changeValue)
        {
            if (_currentTickValue < Maximum)
            {
                EraseThumb();

                _currentTickValue += changeValue;
                if (_currentTickValue > Maximum)
                    _currentTickValue = Maximum;

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("IncrementValue, _currentTickValue = {0}, changeValue = {1}", _currentTickValue, changeValue);
#endif

                Value = _currentTickValue;

                RedrawControl();
            }
        }

        /// <summary>
        /// Decrements the value of the CrystalTrackBar.
        /// </summary>
        protected void DecrementValue(int changeValue)
        {
            if (_currentTickValue > Minimum)
            {
                EraseThumb();

                _currentTickValue -= changeValue;
                if (_currentTickValue < Minimum)
                    _currentTickValue = Minimum;

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("IncrementValue, _currentTickValue = {0}, changeValue = {1}", _currentTickValue, changeValue);
#endif

                Value = _currentTickValue;

                RedrawControl();
            }
        }

        /// <summary>
        /// Sets the value of the CrystalTrackBar to the first tick item.
        /// </summary>
        protected void FirstValue()
        {
            if (_currentTickValue != Minimum)
            {
                EraseThumb();

                _currentTickValue = Minimum;
                Value = Minimum;

                RedrawControl();
            }
        }

        /// <summary>
        /// Sets the value of the CrystalTrackBar to the last tick item.
        /// </summary>
        protected void LastValue()
        {
            if (_currentTickValue != Maximum)
            {
                EraseThumb();

                _currentTickValue = Maximum;
                Value = Maximum;

                RedrawControl();
            }
        }

        /// <summary>
        /// Determines if the thumb on the CrystalTrackBar has been clicked.
        /// </summary>
        /// <returns>Boolean value indicating that the thumb was pressed.</returns>
        protected bool IsThumbPressed()
        {
            return (_thumbState == CrystalThumbState.Pressed);
        }

        /// <summary>
        /// Determines if the thumb on the CrystalTrackBar is being erased.
        /// </summary>
        /// <returns>Boolean value indicating that the thumb is being erased.</returns>
        protected bool IsThumbErasing()
        {
            return (_thumbState == CrystalThumbState.Erased);
        }

        /// <summary>
        /// Determines if the CrystalTrackBar is horizontal.
        /// </summary>
        /// <returns>Boolean value indicating that the CrystalTrackBar is horizontal.</returns>
        private bool IsHorizontal()
        {
            return (_orientation == Orientation.Horizontal);
        }

        #endregion

        #region Overriden Methods

        /// <summary>
        /// Handles the Enabled state of the control.
        /// </summary>
        /// <param name="e">The event argument.</param>
        protected override void OnEnabledChanged(EventArgs e)
        {
            base.OnEnabledChanged(e);

            if (!Enabled)
                _thumbState = CrystalThumbState.Disabled;
            else
                _thumbState = CrystalThumbState.Normal;

            RedrawTrackBar();
        }

        /// <summary>
        /// Provides the initial layout for the CrystalTrackBar.
        /// </summary>
        protected override void InitLayout()
        {
            SetupTrackBar();
            base.InitLayout();
        }

        /// <summary>
        /// Adjusts the CrystalTrackBar when the size has been changed.
        /// </summary>
        /// <param name="e">The event argument.</param>
        protected override void OnSizeChanged(EventArgs e)
        {
            SetupTrackBar();
            base.OnSizeChanged(e);
        }

        /// <summary>
        /// Paints the CrystalTrackBar.
        /// </summary>
        /// <param name="e">The paint event argument.</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            DrawTrackBar(e.Graphics);
            DrawTicks(e.Graphics);
            DrawThumb(e.Graphics);
        }

        /// <summary>
        /// Determine whether the user has clicked the mouse button on the track bar thumb.
        /// </summary>
        /// <param name="e">The mouse event argument.</param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (!Enabled)
                return;

            Focus();

            if (_thumbRectangle.Contains(e.Location))
            {
                _thumbState = CrystalThumbState.Pressed;
            }
            else
            {
                if ((IsThumbPressed()) || (IsThumbErasing()))
                    return;

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("OnMouseDown, checking for LargeChange with Mouse click.");
#endif

                if (CurrentLocation(e.Location) > CurrentThumbTickCoordinate())
                {
                    IncrementValue(LargeChange);
                }
                else if (CurrentLocation(e.Location) < CurrentThumbTickCoordinate())
                {
                    DecrementValue(LargeChange);
                }
            }
        }

        // Redraw the track bar thumb if the user has moved it.
        /// <summary>
        /// Determine whether the user has released the mouse button.
        /// </summary>
        /// <param name="e">The mouse event argument.</param>
        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (!Enabled)
                return;

            _thumbState = CrystalThumbState.Hover;
            RedrawControl();
        }

        /// <summary>
        /// Tracks the mouse movement within the CrystalTrackBar.
        /// </summary>
        /// <param name="e">The mouse event argument.</param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (!Enabled)
                return;

            // The user is moving the thumb.
            if (IsThumbPressed())
            {
                // if the value hasn't changed (the user is just holding at one position),
                // then it may not necessary to erase/redraw the thumb,
                // as it may cause too much flicker.
                if (!HasTrackValueChanged(e.Location))
                {
                    // BUG FIX: Without this call to redraw the control,
                    // the background gets erased when the user holds the mouse
                    // down, but does not move the track value.
                    if (TransparentMode)
                        RedrawControl();

#if CRYSTAL_TRACKBAR_DEBUG
                    System.Console.WriteLine("OnMouseMove, HasTrackValueChanged == false");
#endif

                    return;
                }

                int newTickPos = GetThumbTick(e.Location);

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("OnMouseMove, before adjust, _currentTickValue = {0}, newTickPos = {1}", _currentTickValue, newTickPos);
#endif

                // Track movements to the next tick to the right, if 
                // the cursor has moved halfway to the next tick.
                if (newTickPos > _currentTickValue)
                {
                    int delta = newTickPos - _currentTickValue;
                    IncrementValue(delta);
                }
                    // Track movements to the next tick to the left, if 
                    // cursor has moved halfway to the next tick.
                else
                {
                    int delta = _currentTickValue - newTickPos;
                    DecrementValue(delta);
                }

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("OnMouseMove, after adjust, _currentTickValue = {0}", _currentTickValue);
#endif
            }
            else
            {
                // ThumbState switches to hover because user is still
                // in trackbar area.
                _thumbState = CrystalThumbState.Hover;

#if CRYSTAL_TRACKBAR_DEBUG
                System.Console.WriteLine("OnMouseMove, switch to Hover mode.");
#endif

                // Don't call redraw here, it will cause more flicker than necessary.
                //RedrawControl();
            }
        }

        /// <summary>
        /// Determines whether the mouse cursor has entered the CrystalTrackBar.
        /// </summary>
        /// <param name="e">The mouse event argument.</param>
        protected override void OnMouseEnter(EventArgs e)
        {
            if (!Enabled)
                return;

            _thumbState = CrystalThumbState.Hover;
            RedrawControl();
            base.OnMouseEnter(e);
        }

        /// <summary>
        /// Determines whether the mouse cursor has left the CrystalTrackBar.
        /// </summary>
        /// <param name="e">The mouse event argument.</param>
        protected override void OnMouseLeave(EventArgs e)
        {
            if (!Enabled)
                return;

            _thumbState = CrystalThumbState.Normal;
            RedrawControl();
            base.OnMouseLeave(e);
        }

        /// <summary>
        /// Processes the keystroke for the trackbar.
        /// </summary>
        /// <param name="keyData">The keystroke data.</param>
        /// <returns>True if the CrystalTrackBar processes a keystroke, false if it does not process the keystroke.</returns>
        public virtual bool ProcessKeystroke(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.PageUp:
                    IncrementValue(LargeChange);
                    return true;

                case Keys.PageDown:
                    DecrementValue(LargeChange);
                    return true;

                case Keys.Left:
                    if (IsHorizontal())
                        DecrementValue(SmallChange);
                    return true;

                case Keys.Right:
                    if (IsHorizontal())
                        IncrementValue(SmallChange);
                    return true;

                case Keys.Up:
                    if (!IsHorizontal())
                        DecrementValue(SmallChange);
                    return true;

                case Keys.Down:
                    if (!IsHorizontal())
                        IncrementValue(SmallChange);
                    return true;

                case Keys.Home:
                    FirstValue();
                    return true;

                case Keys.End:
                    LastValue();
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Processes the up, down, left, right, pgup, pgdn keystrokes.
        /// Note this only takes effect when the property KeyboardControl is true.
        /// </summary>
        /// <param name="msg">The windows message.</param>
        /// <param name="keyData">The keystroke data.</param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (KeyboardControl)
            {
                if (msg.Msg == (int)WindowsMessages.WM_KEYDOWN)
                {
                    if (ProcessKeystroke(keyData))
                        return true;
                }
            }

            return base.ProcessCmdKey(ref msg, keyData);
        }

        #endregion
    }
}

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 GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior)
United States United States
Richard has been working with Windows software since 1991. He has worked for Borland, Microsoft, Oracle, and various startup companies such as Livescribe. Currently he is developing projects in C#, Windows Forms, and Net Framework. Visit his blog Attilan (www.attilan.com) to learn more about his tools, projects and discoveries.

Comments and Discussions