Click here to Skip to main content
15,884,007 members
Articles / Desktop Programming / Windows Forms

Developing Rating Bar

Rate me:
Please Sign up or sign in to vote.
4.56/5 (24 votes)
10 Mar 2009CPOL5 min read 47.3K   1.1K   71  
An advance rating control for .NET
using System;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

namespace XSystem.WinControls
{
    public enum IconStyle
    {
        Star,
        Heart,
        Misc
    }
    public delegate void OnRateChanged(object sender, RatingBarRateEventArgs e);
    [DefaultEvent("RateChanged")]
    public class RatingBar : Control
    {
        public event OnRateChanged RateChanged;

        byte gap = 1;
        byte iconsCount = 10;
        float rate = 0;
        float tempRateHolder = 0;
        bool readOnly = false;
        bool rateOnce = false;
        bool isVoted = false;

        ContentAlignment alignment = ContentAlignment.MiddleCenter;
        Image iconEmpty;
        Image iconHalf;
        Image iconFull;
        IconStyle iconStyle = IconStyle.Star;
        PictureBox pb;

        public RatingBar()
        {
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);
            pb = new PictureBox();
            pb.BackgroundImageLayout = ImageLayout.None;
            pb.MouseMove += new MouseEventHandler(pb_MouseMove);
            pb.MouseLeave += new EventHandler(pb_MouseLeave);
            pb.MouseClick += new MouseEventHandler(pb_MouseClick);
            pb.Cursor = Cursors.Hand;
            this.Controls.Add(pb);
            UpdateIcons();
            UpdateBarSize();
            #region --- Drawing Empty Icons ---
            Bitmap tb = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(tb);
            DrawEmptyIcons(g, 0, iconsCount);
            g.Dispose();
            pb.BackgroundImage = tb;
            #endregion
        }

        #region --- Mouse Events ---
        void pb_MouseMove(object sender, MouseEventArgs e)
        {
            if (readOnly || (rateOnce && isVoted))
                return;
            Bitmap tb = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(tb);
            int x = 0;
            float trate = 0;
            byte ticonsCount = iconsCount; // temporary variable to hold the iconsCount value, because we're decreasing it on each loop
            for (int a = 0; a < iconsCount; a++)
            {
                if (e.X > x && e.X <= x + iconEmpty.Width / 2)
                {
                    g.DrawImage(iconHalf, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                    trate += 0.5f;
                }
                else if (e.X > x)
                {
                    g.DrawImage(iconFull, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                    trate += 1.0f;
                }
                else
                    break;
                ticonsCount--;
            }
            tempRateHolder = trate;
            DrawEmptyIcons(g, x, ticonsCount);
            g.Dispose();
            pb.BackgroundImage = tb;

        }
        void pb_MouseLeave(object sender, EventArgs e)
        {
            if (readOnly || (rateOnce && isVoted))
                return;
            Bitmap tb = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(tb);
            int x = 0;
            byte ticonsCount = iconsCount;
            float trate = rate;
            while (trate > 0)
            {
                if (trate > 0.5f)
                {
                    g.DrawImage(iconFull, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                }
                else if (trate == 0.5f)
                {
                    g.DrawImage(iconHalf, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                }
                else
                    break;
                ticonsCount--;
                trate--;
            }
            DrawEmptyIcons(g, x, ticonsCount);

            g.Dispose();
            pb.BackgroundImage = tb;
        }
        void pb_MouseClick(object sender, MouseEventArgs e)
        {
            float toldRate = rate;
            rate = tempRateHolder;
            isVoted = true;
            if (rateOnce)
                pb.Cursor = Cursors.Default;
            OnRateChanged(new RatingBarRateEventArgs(rate, toldRate));
        }
        #endregion
        #region --- Override Functions ---
        protected override void OnResize(EventArgs e)
        {
            UpdateBarLocation();
            base.OnResize(e);
        }
        #endregion
        #region --- Custom Functions ---
        void DrawIcons()
        {
            Bitmap tb = new Bitmap(pb.Width, pb.Height);
            Graphics g = Graphics.FromImage(tb);
            int x = 0;
            byte ticonsCount = iconsCount;
            float trate = rate;
            while (trate > 0)
            {
                if (trate > 0.5f)
                {
                    g.DrawImage(iconFull, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                }
                else if (trate == 0.5f)
                {
                    g.DrawImage(iconHalf, x, 0, iconEmpty.Width, iconEmpty.Height); // Draw icons with the dimension of iconEmpty, so that they do not look odd
                    x += gap + iconEmpty.Width;
                }
                else
                    break;
                ticonsCount--;
                trate--;
            }
            DrawEmptyIcons(g, x, ticonsCount);

            g.Dispose();
            pb.BackgroundImage = tb;
        }
        void DrawEmptyIcons(Graphics g, int x, byte count)
        {
            for (byte a = 0; a < count; a++)
            {
                g.DrawImage(iconEmpty, x, 0);
                x += gap + iconEmpty.Width;
            }
        }
        void UpdateBarSize()
        {
            pb.Size = new Size((iconEmpty.Width * iconsCount) + (gap * (iconsCount - 1)), iconEmpty.Height); // Last icon wont need gap, therefore we need to use -1
            UpdateBarLocation();
        }
        void UpdateBarLocation()
        {
            if (alignment == ContentAlignment.TopLeft) { }// Leave it blank, Since we're calling this from Resize Event then we dont need to set same point again and again
            else if (alignment == ContentAlignment.TopRight)
                pb.Location = new Point(this.Width - pb.Width, 0);
            else if (alignment == ContentAlignment.TopCenter)
                pb.Location = new Point(this.Width / 2 - pb.Width / 2, 0);
            else if (alignment == ContentAlignment.BottomLeft)
                pb.Location = new Point(0, this.Height - pb.Height);
            else if (alignment == ContentAlignment.BottomRight)
                pb.Location = new Point(this.Width - pb.Width, this.Height - pb.Height);
            else if (alignment == ContentAlignment.BottomCenter)
                pb.Location = new Point(this.Width / 2 - pb.Width / 2, this.Height - pb.Height);
            else if (alignment == ContentAlignment.MiddleLeft)
                pb.Location = new Point(0, this.Height / 2 - pb.Height / 2);
            else if (alignment == ContentAlignment.MiddleRight)
                pb.Location = new Point(this.Width - pb.Width, this.Height / 2 - pb.Height / 2);
            else if (alignment == ContentAlignment.MiddleCenter)
                pb.Location = new Point(this.Width / 2 - pb.Width / 2, this.Height / 2 - pb.Height / 2);
        }
        void UpdateIcons()
        {
            if (iconStyle == IconStyle.Star)
            {
                iconEmpty = Properties.Resources.iconStarEmpty;
                iconFull = Properties.Resources.iconStarFull;
                iconHalf = Properties.Resources.iconStartHalf;
            }
            else if (iconStyle == IconStyle.Heart)
            {
                iconEmpty = Properties.Resources.iconHeartEmpty;
                iconFull = Properties.Resources.iconHeartFull;
                iconHalf = Properties.Resources.iconHeartHalf;
            }
            else if (iconStyle == IconStyle.Misc)
            {
                iconEmpty = Properties.Resources.iconMiscEmpty;
                iconFull = Properties.Resources.iconMiscFull;
                iconHalf = Properties.Resources.iconMiscHalf;
            }
        }
        #endregion
        #region --- Virtual Functions ---
        protected virtual void OnRateChanged(RatingBarRateEventArgs e)
        {
            if (RateChanged != null && e.NewRate != e.OldRate)
                RateChanged(this, e);
        }
        #endregion
        #region --- Properties ---
        public byte Gap
        {
            get { return gap; }
            set { gap = value; }
        }
        public byte IconsCount
        {
            get { return iconsCount; }
            set { if (value <= 10) { iconsCount = value; UpdateBarSize(); } }
        }
        [DefaultValue(typeof(ContentAlignment), "middlecenter")]
        public ContentAlignment Alignment
        {
            get { return alignment; }
            set
            {
                alignment = value;
                if (value == ContentAlignment.TopLeft)
                    pb.Location = new Point(0, 0);
                else UpdateBarLocation();
            }
        }

        public Image IconEmpty
        {
            get { return iconEmpty; }
            set { iconEmpty = value; }
        }
        public Image IconHalf
        {
            get { return iconHalf; }
            set { iconHalf = value; }
        }
        public Image IconFull
        {
            get { return iconFull; }
            set { iconFull = value; }
        }

        [DefaultValue(false)]
        public bool ReadOnly
        {
            get { return readOnly; }
            set { readOnly = value; if (value)pb.Cursor = Cursors.Default; else pb.Cursor = Cursors.Hand; }
        }
        [DefaultValue(false)]
        public bool RateOnce
        {
            get { return rateOnce; }
            set { rateOnce = value; if (!value) pb.Cursor = Cursors.Hand; /* Set hand cursor, if false is set from true*/ }
        }
        [Browsable(false)]
        public float Rate
        {
            get { return rate; }
            set
            {
                if (value >= 0 && value <= (float)iconsCount)
                {
                    float toldRate = rate;
                    rate = value;
                    DrawIcons();
                    OnRateChanged(new RatingBarRateEventArgs(value, toldRate));
                }
                else
                    throw new ArgumentOutOfRangeException("Rate", "Value '" + value + "' is not valid for 'Rate'. Value must be Non-negative and less than or equals to '" + iconsCount + "'");
            }
        }

        public Color BarBackColor
        {
            get { return pb.BackColor; }
            set { pb.BackColor = value; }
        }

        [DefaultValue(typeof(IconStyle), "star")]
        public IconStyle IconStyle
        {
            get { return iconStyle; }
            set { iconStyle = value; UpdateIcons(); }
        }
        
        #endregion
    }
    public class RatingBarRateEventArgs : EventArgs
    {
        public float NewRate;
        public float OldRate;
        public RatingBarRateEventArgs(float newRate, float oldRate)
        {
            NewRate = newRate;
            OldRate = oldRate;
        }
    }
}

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
CEO
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions