.NET Compact Framework Distributed Weight Slider Control

By Robbe D. Morris

Printer Friendly Version

Robbe Morris
Robbe & Melisa Morris
  Download C# Source Code
Many of you have already run across the JavaScript Distributed Weight Slider Control that I wrote two years ago for our web based decision analysis software tools.  In order to prepare for upcoming client requests for scaled down versions of our decision making software, I've decided to recreate the functionality of this web based slider control via a .NET custom control that works on both standard .NET Windows Forms as well as .NET Compact Framework Windows Forms.  If you want to get a feel for how this control works before you download the source code, visit the JavaScript version via the link above.


The control offers a very user friendly experience in that it allows the user to use their stylus / mouse to adjust the weight for a given decision criteria instead of typing the numbers.  The first mode, Percent, sets the desired percentage of importance for a given criteria.  Then automatically redistributes the remaining values evenly across the other sliders.  Thus, the user only has to change one value instead of several while guaranteeing that the total of all the slider rows equals 100  The second mode is a simple one to nine range that allow the grouped sliders to be autonomous from each other.  The following is a pair of rough screen shots of the two modes:
 
 
The first thing I discovered with this endeavor was that the .NET Compact Framework doesn't have a Panel control with scroll bars.  Rather than reinvent the wheel, I borrowed a portion of the Pocket Fuzzy Quotient Calculator by Andy Weston.  It is a simple yet effective implementation of using a panel control that integrates a VScrollBar control duplicating the desired functionality normally found in .NET Windows Forms applications.  The ScrollablePanel.cs class used by my slider control is almost entirely his code plus a tweak or two of mine.
Those of you who have been writing .NET Compact Framework applications already know that none of the controls have the .Name property.  For the life of me, I'll never understand why.  That said, you'll notice that I created custom versions of the Panel, PictureBox, and Label controls that do nothing but inherit standard base controls and add a property named ControlName.  I named it this instead of .Name so as to avoid conflicts when the exact same Slider.cs class is used in a .NET Windows Forms application or when that infamous Microsoft Innovation Bill Gates is always talking about kicks in and the .Name property is added in the next release of the .NET Compact Framework.
My choice of controls as well as how I opted to raise events deals with the differences in the two application environments.  For example, the Label control in the .NET Compact Framework does not raise certain mouse events while the Label control in .NET Windows Forms does.  So, what may seem like unnecessary code to raise or react to events is actually necessary.  You'll also run across a few bits of code that deal with control repaint issues on slower .NET Compact Framework devices that don't show themselves on more powerful devices or in .NET Windows Forms applications.
Instructions
There are three folders in the zip file above.  CustomControls contains the Slider.cs and ScrollablePanel.cs classes that make up the slider control.  I've kept them separate yet integrated to enable you to use the Scrollable Panel control elsewhere in your application.  The .NET Windows Form demo is in the folder named WindowsForms and the .NET Compact Framework demo is in the folder NETCF.
When reviewing the code, you'll want to look at the form constructor and load methods of form1.cs.  The embedded comments will explain how to create an instance of the control, set its properties, and trigger the loading of sample data from sampledata.xml (formatted DataSet).  Form1.cs also has sample code for all of the slider events that I allowed to bubble up out of the control and how to interact with the slider when they are triggered.
 
How To Use The Control
The business rules and user-interface options are strictly for demo purposes only.  I suspect you'll use a different methodology in your own implementation.  The slider requires three images: Help Icon, Lock On, and Lock Off.  The help icon is optional and the slider will render without it.  It simply allows the UI to react to a user asking for assistance regarding this specific decision criteria.  The slider lock enables a user to pin a slider bar preventing it from being adjusted manully or by the weight distribution mechanism of the Percent math option.
The user navigates to the next lowest level of detail under the current decision criteria by clicking the description.  The demo business rules will then repaint the slider with the selected node's children.  The lowest level of data reflects a change in the math option to Range 1 to 9.  If the user clicks the description of one of these nodes, it starts back at the top level of the hierarchy again.
Depending on the speed of the device, there is an option to refresh the text value labels as the slider is being moved or only when the slider mouse up event is fired.  You also have the option of suppressing help and lock events plus a variety of other customization properties (fonts, varied bar colors, etc...).
The slider itself does not update or alter data stores (ie DataSets, DataTables, Collections, etc...).  Your business logic in the form that is fired by the various events bubbled up from the slider control will need to handle this.  The sample code in form1.cs demonstrates how you might want to accomplish this task.
I hope you find the sample code useful for your own applications.  As always, the sample source code on NullSkull.com is royalty free for commercial or personal use.
 
Other .NET Compact Framework Articles
.NET Compact Framework Save Handwritten Signature To File
.NET Compact Framework App.Config Workaround
.NET Compact Framework Encryption And File Transmittal With Mobile Devices In C#
.NET Compact Framework - Web Services
.NET Compact Framework - Encrypted WebService Communications with CryptoAPI
 
ScrollablePanel.cs
using System;
using System.Windows.Forms;
using System.Drawing;

namespace CustomControls
{
    public class ScrollablePanel : Panel
    {
        public Panel Contents
        {
            get { return contents; }
        }

        Panel contents;
        VScrollBar vScroll;

        public ScrollablePanel()
        {
            this.vScroll = new VScrollBar();
            this.vScroll.Parent = this;
            this.vScroll.Visible = true;
            this.vScroll.Minimum = 0;
            this.vScroll.SmallChange = 20;
            this.vScroll.ValueChanged += new EventHandler (this.scrollbar_ValueChanged);
            this.contents = new Panel();
            this.contents.Parent = this;
            this.contents.Width = this.ClientSize.Width - this.vScroll.Size.Width;
            this.contents.SendToBack();  

        }

        public void scrollbar_ValueChanged (object o, EventArgs e)
        {
            if (o == this.vScroll)
            {
                //By decreasing the top y coordinate
                //the contents panel appears to scroll
                this.contents.Top = -this.vScroll.Value;
                this.Update();
            }
        }

        void CheckScrollBars()
        {
            this.vScroll.Value =  this.vScroll.Minimum;
            this.vScroll.Visible = this.contents.Size.Height > this.ClientSize.Height;
        }

        protected override void OnResize (EventArgs e)
        {
            this.contents.Width = this.ClientSize.Width - this.vScroll.Size.Width;

            this.vScroll.Bounds = new Rectangle (this.ClientSize.Width - this.vScroll.Size.Width, 0,
                                                                                this.vScroll.Size.Width, 
                                                                                this.ClientSize.Height);

            if (this.ClientSize.Height >= 0) {  this.vScroll.LargeChange = this.ClientSize.Height; }

            CheckScrollBars();
        }

        public void SetScrollHeight(int height)
        {
            this.contents.Height = height;

            this.vScroll.Maximum = this.contents.Size.Height;

            CheckScrollBars();

            this.vScroll.Left =  this.Contents.Width;
        }


    }
}

Slider.cs
using System;
using System.Data;
using System.Collections;
using System.Text; 
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics; 

namespace CustomControls
{
 
   

 #region Slider
	public class Slider : Control
	{

        #region Local Variables

        private const string HelpIconName = "sldHelpIcon_";
        private const string LockIconName = " sldLockIcon_";
        private const string DescriptionName = "sldDescription_";
        private const string DescriptionPanelName = "sldDescriptionPanel_";
        private const string SliderBarName = "sldBar_";
        private const string ValueName = "sldValue_";
        private bool IsMouseDown=false;
        private int MinSliderBar=0;
        private int MaxSliderBar=0;
        private int CurrentIndex=0;
        private SliderPanel ActiveBar;
        private SliderRow[] SliderRows;
        private SliderRow CurrentRow;
        private SliderStyle CurrentStyle;
        protected CustomControls.ScrollablePanel PanelReference;

        #endregion
 
        #region Event Declarations

        public event  System.Windows.Forms.MouseEventHandler  SliderDescriptionMouseUp;
        public event  System.Windows.Forms.MouseEventHandler  SliderDescriptionMouseDown;
        public event  System.Windows.Forms.MouseEventHandler  SliderLockMouseUp;
        public event  System.Windows.Forms.MouseEventHandler  SliderLockMouseDown;
        public event  System.Windows.Forms.MouseEventHandler  SliderHelpMouseUp;
        public event  System.Windows.Forms.MouseEventHandler  SliderHelpMouseDown;
        public event  System.Windows.Forms.MouseEventHandler  SliderBarMouseUp;

        #endregion
        
        #region Constructor
        public Slider() 
        {
   
        }
        #endregion



        #region Description Mouse Events
        public void OnSliderDescriptionMouseDown(object sender, MouseEventArgs e)
        {

            CustomControls.SliderPanel oPanel;
            CustomControls.SliderLabel oLabel;
 
            try
            {
   
                if (sender.GetType().ToString() == "CustomControls.SliderPanel")
                {
                    // Event occured while running under the .NET Compact Framework.
                    oPanel = (CustomControls.SliderPanel)sender; 
                    oLabel = (CustomControls.SliderLabel)oPanel.Controls[0]; 
                }
                else 
                {
                    // Event occured while running a PC windows application.
                    oLabel = (CustomControls.SliderLabel)sender;
                }
                
                CurrentIndex = int.Parse(oLabel.ControlName.Replace(DescriptionName,""));

                CurrentRow = SliderRows[CurrentIndex];

            
                
                SliderDescriptionMouseDown(oLabel, e);

            }
            catch (Exception) { throw; }
        }

        public void OnSliderDescriptionMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                SliderDescriptionMouseUp(sender, e);
            }
           catch (Exception) { throw; }
        }
        #endregion

        #region Lock Mouse Events
        public void OnSliderLockMouseDown(object sender, MouseEventArgs e)
        {
            try
            {

                if (!CurrentStyle.RaiseLockEvents)  { return; }
                
                CustomControls.SliderPictureBox oControl = (CustomControls.SliderPictureBox)sender;
                
                CurrentIndex = int.Parse(oControl.ControlName.Replace(LockIconName,""));
                
                CurrentRow = SliderRows[CurrentIndex];
               
                CurrentRow.IsLocked = !CurrentRow.IsLocked;
    
                SliderRows[CurrentIndex] = CurrentRow;
                
                if (CurrentRow.IsLocked) { oControl.Image = CurrentStyle.LockOnIcon; }
                else { oControl.Image = CurrentStyle.LockOffIcon; }
                
                SliderLockMouseDown(oControl, e);

            }
            catch (Exception) { throw; }
        }

        public void OnSliderLockMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                
                if (!CurrentStyle.RaiseLockEvents)  { return; }
                
                SliderLockMouseUp(sender, e);

            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Help Mouse Events
        public void OnSliderHelpMouseDown(object sender, MouseEventArgs e)
        {
            try
            {
                if (!CurrentStyle.RaiseHelpEvents)  { return; }
                CustomControls.SliderPictureBox oControl = (CustomControls.SliderPictureBox)sender;
                CurrentRow = SliderRows[int.Parse(oControl.ControlName.Replace(HelpIconName,""))];
                SliderHelpMouseDown(oControl, e);
            }
            catch (Exception) { throw; }
        }

        public void OnSliderHelpMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                if (!CurrentStyle.RaiseHelpEvents)  { return; }
                SliderHelpMouseUp(sender, e);
            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Bar Mouse Events    
        private void OnSliderBarMouseDown(object sender, MouseEventArgs e)
        {
            try
            {

                IsMouseDown = true;
                
                ActiveBar = (CustomControls.SliderPanel)sender;
                
                CurrentIndex = int.Parse(ActiveBar.ControlName.Replace(SliderBarName,""));

                if (!ValidateSliderBarsUnlocked()) { IsMouseDown = false; return; }
                
                CurrentRow = SliderRows[CurrentIndex];
                
                if (CurrentRow.IsLocked) { IsMouseDown=false; }

            }
            catch (Exception) { throw; }
        }

        public void OnSliderBarMouseUp(object sender, MouseEventArgs e)
        {
            try
            {
                IsMouseDown=false;
               
                // Exit out if Range1To9 because it ignore the refresh on mouse up only flag.
                // Exit out if we refreshed other bars as the selected bar was moving.

                if ((!CurrentStyle.RefreshValueOnMouseUpOnly) || (CurrentStyle.Type == SliderType.Range1To9))
                {
                    SliderBarMouseUp(sender, e);
                    return;
                }
             
                RedistributeValues();
             
                MoveOtherSliderBars(); 
           
                RefreshSliderValueLabels();
 
                this.Invalidate(); 
 
                SliderBarMouseUp(sender, e);
                
            }
            catch (Exception) { throw; }
        }
        
        private void OnSliderBarMouseMove(object sender, MouseEventArgs e)
        {
           
            try
            {
                  
                if (!IsMouseDown)   {  return;  }
                
                MoveSliderBar(ActiveBar,e.X,CurrentIndex,CurrentRow,true);

                ActiveBar.Invalidate(); 

                // If true, refresh this bar's label.  The others will be addressed in OnMouseUp.
                // Range1To9 doesn't alter other sliders so exit out after refreshing this bar's label.

                if ((CurrentStyle.RefreshValueOnMouseUpOnly) || (CurrentStyle.Type == SliderType.Range1To9))
                { 
                   RefreshSliderValueLabel(CurrentIndex);
                   return;
                }
 
               RedistributeValues();
             
               MoveOtherSliderBars(); 
           
               RefreshSliderValueLabels();

            }
            catch (Exception) { throw; }

        }

        #endregion




        #region Load
        public void Load(ref CustomControls.ScrollablePanel SliderPanel,
                         CustomControls.SliderStyle Style,
                         CustomControls.SliderRow[] Rows)
        { 

            try
            {
                      
               this.PanelReference  = SliderPanel;
               
               this.SliderRows = null;

               this.SliderRows = Rows;
 
               CurrentStyle = Style;

               this.LoadRows();
                 
            }
            catch (Exception) { throw; }

        }
        #endregion

        #region ReSize
        public void ReSize()
        {
            try
            {
                this.Enabled = false;
                this.LoadRows();
            }
            catch (Exception) { throw; }
            finally { this.Enabled = true;  }
        }
        #endregion

        #region Load Rows
        private void LoadRows()
        {
            int Top=2;
            int ControlBuffer = 3;

            try
            {
               
                this.PanelReference.Contents.Controls.Clear(); 

                // There is a refreshing problem on .NET CF devices.
                // Force the scroll to be high enough to repaint new
                // controls properly.  Then, reset the scroll afterwards.

                this.PanelReference.SetScrollHeight(this.Height + 10); 
                               
                if (this.SliderRows == null) { return; }
                if (this.SliderRows.Length < 1) { return; }
                
                for(int i=0;i<this.SliderRows.Length;i++)
                {
                    if (i==0) { CurrentRow = this.SliderRows[i]; }
                    CreateRow(ref this.SliderRows[i],Top,i);
                    Top += CurrentStyle.RowHeight + ControlBuffer;
                }
                 
                // Remove the scroll bar if unneeded.

                this.PanelReference.SetScrollHeight(Top); 
                
            }
            catch (Exception) { throw; }
       
        }
        #endregion

        #region Create Row
        private void CreateRow(ref SliderRow Row,int Top,int SliderIndex)
        {
 
            try
            {

                CustomControls.SliderPictureBox oHelp = new CustomControls.SliderPictureBox();
                oHelp.ControlName = HelpIconName + SliderIndex.ToString(); 
                oHelp.Width = CurrentStyle.HelpTextIcon.Width;  
                oHelp.Height = CurrentStyle.RowHeight; 
                if (CurrentStyle.RaiseHelpEvents) {  oHelp.Image = CurrentStyle.HelpTextIcon; }
                oHelp.Location  = new Point(1,Top);
                oHelp.MouseDown += new   MouseEventHandler(OnSliderHelpMouseDown); 
                oHelp.MouseUp += new  MouseEventHandler(OnSliderHelpMouseUp); 
                Row.ControlIndexHelp = this.PanelReference.Contents.Controls.Count;
                this.PanelReference.Contents.Controls.Add(oHelp);
              
                CustomControls.SliderPictureBox oLock = new CustomControls.SliderPictureBox();
                oLock.ControlName = LockIconName + SliderIndex.ToString(); 
                oLock.Width = CurrentStyle.LockOffIcon.Width;  
                oLock.Height = CurrentStyle.RowHeight; 

                if ((Row.IsLocked == true) || (CurrentStyle.RaiseLockEvents == false)) 
                {
                    oLock.Image = CurrentStyle.LockOnIcon; 
                }
                else  {  oLock.Image = CurrentStyle.LockOffIcon;  }

                oLock.Location  = new Point(oHelp.Left + oHelp.Width,Top);
                oLock.MouseDown += new   MouseEventHandler(OnSliderLockMouseDown); 
                oLock.MouseUp += new  MouseEventHandler(OnSliderLockMouseUp); 
                Row.ControlIndexLock = this.PanelReference.Contents.Controls.Count;
                this.PanelReference.Contents.Controls.Add(oLock); 
          
                // The Label control can raise mouse events in a PC based application
                // but not in a .NET Compact Framework application (at the time of this writing).
                // However, a .NET CF app will raise the mouse events for a Panel.  So, we'll
                // place our Label control on a Panel and use its mouse events while still
                // showing the Label to the user.  When the events are raised, we'll check
                // what type of control the "sender" is and act accordingly.

                CustomControls.SliderLabel oDescription = new SliderLabel();
                oDescription.ControlName = DescriptionName + SliderIndex.ToString(); 
                oDescription.Width = CurrentStyle.DescriptionWidth; 
                oDescription.Height = CurrentStyle.RowHeight; 
                oDescription.Location  = new Point(0,0);
                oDescription.Font = Row.Font;
                oDescription.TextAlign = System.Drawing.ContentAlignment.TopLeft;
                oDescription.Text = Row.Description; 
                oDescription.ForeColor = Row.ForeColor; 
                oDescription.MouseDown += new  MouseEventHandler(OnSliderDescriptionMouseDown); 
                oDescription.MouseUp += new  MouseEventHandler(OnSliderDescriptionMouseUp); 
                oDescription.SendToBack(); 
                
                CustomControls.SliderPanel oDescriptionPanel = new SliderPanel();
                oDescriptionPanel.ControlName = DescriptionPanelName + SliderIndex.ToString(); 
                oDescriptionPanel.Width = CurrentStyle.DescriptionWidth; 
                oDescriptionPanel.Height = CurrentStyle.RowHeight; 
                oDescriptionPanel.Location  = new Point(oLock.Left + oLock.Width,Top);
                oDescriptionPanel.MouseDown += new   MouseEventHandler(OnSliderDescriptionMouseDown); 
                oDescriptionPanel.MouseUp += new  MouseEventHandler(OnSliderDescriptionMouseUp); 
                oDescriptionPanel.BringToFront(); 
                oDescriptionPanel.Controls.Add(oDescription);
               
                Row.ControlIndexBar  = this.PanelReference.Contents.Controls.Count;
                this.PanelReference.Contents.Controls.Add(oDescriptionPanel); 
                             
                CustomControls.SliderPanel oBar = new SliderPanel();
                oBar.ControlName = SliderBarName + SliderIndex.ToString(); 
                oBar.Location  = new Point(oDescriptionPanel.Left + oDescriptionPanel.Width + 2,Top);
                oBar.Width = this.GetBarWidth(oBar.Left);
                oBar.Height = CurrentStyle.RowHeight;
                oBar.BackColor = Row.SliderBarColor; 
                oBar.MouseDown += new   MouseEventHandler(OnSliderBarMouseDown); 
                oBar.MouseUp += new  MouseEventHandler(OnSliderBarMouseUp); 
                oBar.MouseMove += new  MouseEventHandler(OnSliderBarMouseMove); 
                Row.ControlIndexBar = this.PanelReference.Contents.Controls.Count;
                MinSliderBar = 1;
                MaxSliderBar = oBar.Width;
                oBar.Width = this.ConvertValueToWidth(Row.Value);  
                this.PanelReference.Contents.Controls.Add(oBar); 
             
                CustomControls.SliderLabel oValue = new SliderLabel();
                oValue.ControlName = ValueName + SliderIndex.ToString(); 
                oValue.Width = CurrentStyle.ValueWidth; 
                oValue.Height = CurrentStyle.RowHeight; 
                oValue.Font = Row.Font;
                oValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
                oValue.Location = new Point(this.GetValueLeft(),Top);
                oValue.Text = System.Math.Round(Row.Value,CurrentStyle.DecimalPoints).ToString();
                oValue.ForeColor = Row.ForeColor; 
                Row.ControlIndexValue = this.PanelReference.Contents.Controls.Count;
                this.PanelReference.Contents.Controls.Add(oValue); 
                
            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Move Slider Bar
        private void MoveSliderBar(CustomControls.SliderPanel oBar,
                                   int Width,int SliderIndex,
                                   SliderRow Row,bool ResetValue)
        {
 
            try
            {
               
                oBar.Width = ValidateSliderBarWidth(Width);

                if (ResetValue) { Row.Value = ConvertWidthToValue(oBar.Width); }
           
                this.SliderRows[SliderIndex] = Row;

            }
            catch (Exception) { throw; }

        }
        #endregion

        #region Redistribute Values
        private void RedistributeValues()
        {
            double TotalAllValues = 0;
            double TotalNonExcludedValues = 0;
            double TotalBase=100;
            bool ZerosAdjusted=false;
            double Delta = 0;
            double AdjustableItems = 0;
            double Adjuster = 0;

            // This method should only be called with SliderType.Percent.  It grabs
            // the selected bar's value and evenly distributes the remaining values
            // across the other sliders either increase or decreasing their values.

            CustomControls.SliderRow Row;

            try
            {

                for(int i=0;i<this.SliderRows.Length;i++)
                {

                     Row = this.SliderRows[i];

                     TotalAllValues += Row.Value;

                     if ((!Row.IsLocked) && (i !=CurrentIndex))
                     {
                         TotalNonExcludedValues += Row.Value;
                          AdjustableItems++;
                     }
                    
                }

                if (AdjustableItems == 0) { return; }

                if (TotalNonExcludedValues == 0) 
                {
                     ZerosAdjusted = true;
                }

                Delta = (TotalBase - TotalAllValues);
               
 
                for(int i=0;i<this.SliderRows.Length;i++)
                {

                    Row = this.SliderRows[i];

                    if ((!Row.IsLocked) && (i !=CurrentIndex))
                    {
         
                        if (ZerosAdjusted)  // Selected bar reached 100.
                        {
                           Row.Value = Delta / AdjustableItems;
                        }
                        else // All other conditions
                        {
                            Row.Value = Row.Value + ((Row.Value / TotalNonExcludedValues) * Delta);
                        }
                         
                        // In odd number situations, the total of all bars may not always equal 100.  In
                        // these rare cases, we'll tack on the extra to the selected bar and redraw it.

                        if (Row.Value < 0.0001) { Adjuster += Row.Value; Row.Value = 0; }

                        this.SliderRows[i] = Row;

                    }

                }

                if (Adjuster == 0 ) { return; }  // All bars totaled 100.  No need to redraw the selected bar.
                
                this.SliderRows[CurrentIndex].Value += Adjuster;
                
               CustomControls.SliderPanel CurPanel;
               CurPanel = (CustomControls.SliderPanel)
                this.PanelReference.Contents.Controls[CurrentRow.ControlIndexBar];
            
                int ThisWidth = this.ConvertValueToWidth(this.SliderRows[CurrentIndex].Value);
                MoveSliderBar(CurPanel,ThisWidth,CurrentIndex, this.SliderRows[CurrentIndex],false);        
            
            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Move Other Slider Bars
        private void MoveOtherSliderBars()
        {
            CustomControls.SliderRow Row;
            CustomControls.SliderPanel CurPanel;

            try
            {

                for(int i=0;i<this.SliderRows.Length;i++)
                {

                    Row = this.SliderRows[i];
                    
                    if ((Row.IsLocked) || (i==CurrentIndex)) { continue; }
    
                    CurPanel = (CustomControls.SliderPanel)this.PanelReference.Contents.Controls[Row.ControlIndexBar];
                       
                    MoveSliderBar(CurPanel,this.ConvertValueToWidth(Row.Value),i,Row,false); 

                }
          
            }
            catch (Exception) { throw; }
        }
        #endregion

        #region Convert Width To Value
        private double ConvertWidthToValue(int Width)
        {
             double Ret = 0;

             try
             {
                 
                 switch (CurrentStyle.Type)
                 {

                     case SliderType.Percent: 
 
                         Ret =  ((double)Width / (double)MaxSliderBar) * 100;
                         
                         if (Width==1) { Ret = 0; }
      
                         break;

                     case SliderType.Range1To9: 
 
                         Ret = ((double)Width / (double)MaxSliderBar) *  9;	
                         
                         if (Ret < 1) { Ret = 1; }

                         break;
                 }

             }
             catch (Exception) { throw; }
             return Ret;
        }
        #endregion

        #region Convert Value To Width
        private int ConvertValueToWidth(double Weight)
        {

            int Ret = 0;

            // Take passed in value and turn it into pixel width for the bar.
            // Essentionally, take the Weight as a percentage of the maximum size
            // the bar is allowed to return pixel width.

            try
            {
 
                switch (CurrentStyle.Type)
                {

                    case SliderType.Percent: 

                        if (Weight == 0 ) { Weight = 0.01; }

                        Ret = int.Parse(System.Math.Round(MaxSliderBar * (Weight / 100),0).ToString()); 

                        break;

                    case SliderType.Range1To9: 

                        if ((Weight < 1) || (Weight > 9)) { Weight = 1;}
                                                
                        Ret = int.Parse(System.Math.Round(MaxSliderBar * (Weight / 9),0).ToString()); 

                        break;

                }
 
            }
            catch (Exception) {  throw;  }
            return Ret;
        }
        #endregion

        #region Validate Slider Bar Width
        private int ValidateSliderBarWidth(int Width)
        {
            // Make sure the slider stays in bounds
            if (Width <  MinSliderBar)  { Width = 1; }
            if (Width > MaxSliderBar)  { Width = MaxSliderBar; }
            return Width;
        }
        #endregion

        #region Validate Slider Bars Unlocked
        private bool ValidateSliderBarsUnlocked()
        {
            
                try
                {

                    for(int i=0;i<this.SliderRows.Length;i++)
                    {
 
                       if (i==CurrentIndex) { continue; }
                       if (this.SliderRows[i].IsLocked == false)
                       {
                           return true; 
                       }
                     
                    }
          
                }
                catch (Exception) { throw; }
                return false;
        }
        #endregion

        #region Refresh Slider Value Labels
        private void RefreshSliderValueLabels()
        {
 
            try
            {

                for(int i=0;i<this.SliderRows.Length;i++)
                {
                   this.RefreshSliderValueLabel(i);               
                }
          
            }
            catch (Exception)  { throw;  }

        }
        #endregion

        #region Refresh Slider Value Label
        private void RefreshSliderValueLabel(int RowIndex)
        {

            try
            {
 
                SliderRow Row = this.SliderRows[RowIndex];

                double ThisValue = System.Math.Round(Row.Value,CurrentStyle.DecimalPoints);
                    
                if (ThisValue >= 100) { ThisValue = 100; }

                SliderLabel oValue = (SliderLabel)this.PanelReference.Contents.Controls[Row.ControlIndexValue];

                oValue.Text = ThisValue.ToString();

                oValue.Invalidate(); 

            }
            catch (Exception)  {  throw;  }

        }
        #endregion

        #region Get ValueLeft
        private int GetValueLeft()
        {
           return((this.PanelReference.Contents.Width - 3) - CurrentStyle.ValueWidth);
        }
        #endregion
        
        #region Get Bar Width
        private int GetBarWidth(int BarLeft)
        {
          return GetValueLeft() - BarLeft;
        }
        #endregion

        #region Public Properties
        public SliderRow SelectedRow
        {
            get { return this.CurrentRow; }
        }

        public SliderRow[] SelectedRows
        {
            get { return this.SliderRows; }
        }
        #endregion
        
	}
    #endregion





    #region Slider Type
    public enum SliderType
    {
        Percent = 1,
        Range1To9 = 2
    }
    #endregion

    #region Slider Row
    public class SliderRow
    {
        private double WeightValue = 0;
        private bool Locked = false;

        public SliderRow()
        {
           this.IsDirty=false;
        }

         public int ID;
         public int SelectedIndex;
         public int ControlIndexHelp;
         public int ControlIndexLock;
         public int ControlIndexDescription;
         public int ControlIndexBar;
         public int ControlIndexValue;
         public int Width;
         public int Height;
         public bool HasHelpText;
         public bool IsDirty;
         public string Description;
         public System.Drawing.Font Font;
         public System.Drawing.Color ForeColor;
         public System.Drawing.Color SliderBarColor;


         public bool IsLocked
         {
            get { return Locked; }
            set { this.IsDirty = true; Locked = value; }
         }

         public double Value
         {
             get { return WeightValue; }
             set { this.IsDirty = true; WeightValue = value; }
         }

    }
    #endregion

    #region Slider Style
    public struct SliderStyle
    {
        public int RowHeight;
        public int DescriptionWidth;
        public int ValueWidth;
        public int DecimalPoints; 
        public bool RaiseLockEvents;
        public bool RaiseHelpEvents;
        public bool RefreshValueOnMouseUpOnly;
        public SliderType Type;
        public System.Drawing.Image HelpTextIcon;
        public System.Drawing.Image LockOnIcon;
        public System.Drawing.Image LockOffIcon;
    }
    #endregion

    #region Slider Label
    public class SliderLabel : System.Windows.Forms.Label
    {
        public SliderLabel() { }
        public string ControlName = "";
    }
    #endregion

    #region Slider Panel
    public class SliderPanel : System.Windows.Forms.Panel
    {
        public SliderPanel() { }
        public string ControlName = "";
    }
    #endregion

    #region Slider PictureBox
    public class SliderPictureBox : System.Windows.Forms.PictureBox 
    {
        public SliderPictureBox() { }
        public string ControlName = "";
    }
    #endregion

}


Robbe has been a Microsoft MVP in C# since 2004.  He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.