vbAccelerator - Contents of code file: acclExplorerBar_ControllableScrollableControl.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace vbAccelerator.Components.Controls.ExplorerBarUtility
{
   #region Enum
   /// <summary>
   /// Scroll bar orientation constants for the 
   /// <see cref="ControllableScrollableControl"/>.
   /// </summary>
   public enum ScrollBarOrientation : int
   {
      /// <summary>
      /// The Horizontal scroll bar.
      /// </summary>
      Horizontal = 0, // SB_HORZ
      /// <summary>
      /// The Vertical scroll bar.
      /// </summary>
      Vertical = 1 // SB_VERT
   }
   #endregion

   /// <summary>
   /// Event Arguments describing a scroll event.
   /// </summary>
   public class ControllableScrollEventArgs : ScrollEventArgs
   {
      /// <summary>
      /// The bar orientation
      /// </summary>
      private ScrollBarOrientation orientation =
       ScrollBarOrientation.Horizontal;

      /// <summary>
      /// Constructs a new instance of this class.
      /// </summary>
      /// <param name="orientation">Scroll bar orientation which has
       changed.</param>
      /// <param name="type">Scroll bar event type.</param>
      /// <param name="newValue">New scroll bar value.</param>
      internal ControllableScrollEventArgs(
         ScrollBarOrientation orientation,
         ScrollEventType type , 
         int newValue
         ) : base(type, newValue)
      {
         this.orientation = orientation;
      }

      /// <summary>
      /// Gets the orientation of the scroll bar which changed.
      /// </summary>
      public ScrollBarOrientation Orientation
      {
         get
         {
            return orientation;
         }
      }
   }

   #region Delegate
   /// <summary>
   /// Represents the method that responds to the <see
    cref="ControllableScrollableControl"/>
   /// <see cref="ControllableScrollableControl.Scroll"/> event.
   /// </summary>
   public delegate void ControllableScrollEventHandler(object sender,
    ControllableScrollEventArgs e);
   #endregion

   /// <summary>
   /// This control offers an alternative to the .NET Framework
    ScrollableControl.  
   /// This version is more controllable (changing the Max size does not affect
    the 
   /// value unless the max is made smaller than the value, when the scroll
   /// position is maintained at the bottom of the control) and also 
   /// supports &gt; 16 bit signed scroll ranges.
   /// </summary>
   public class ControllableScrollableControl : System.Windows.Forms.UserControl
   {
      #region Unmanaged Code
      private struct SCROLLINFO
      {
         public int cbSize;
         public int fMask;
         public int nMin;
         public int nMax;
         public int nPage;
         public int nPos;
         public int nTrackPos;
      }
      [DllImport("user32.dll")]
      private static extern int SetScrollInfo (IntPtr hwnd, int n, ref
       SCROLLINFO lpcScrollInfo, bool b);
      [DllImport("user32.dll")]
      private static extern int GetScrollInfo (IntPtr hwnd, int n, ref
       SCROLLINFO lpScrollInfo);
      [DllImport("user32.dll")]
      private static extern int GetScrollPos (IntPtr hWnd, int nBar);
      [DllImport("user32.dll")]
      private static extern int GetScrollRange (IntPtr hWnd, int nBar, ref int
       lpMinPos, ref int lpMaxPos);
      [DllImport("user32.dll")]
      private static extern int SetScrollPos (IntPtr hWnd, int nBar, int nPos,
       int bRedraw);
      [DllImport("user32.dll")]
      private static extern int SetScrollRange (IntPtr hWnd, int nBar, int
       nMinPos, int nMaxPos, int bRedraw);
    
      private const int SB_BOTH = 3;
      private const int SB_BOTTOM = 7;
      private const int SB_CTL = 2;
      private const int SB_ENDSCROLL = 8;
      private const int SB_HORZ = 0;
      private const int SB_LEFT = 6;
      private const int SB_LINEDOWN = 1;
      private const int SB_LINELEFT = 0;
      private const int SB_LINERIGHT = 1;
      private const int SB_LINEUP = 0;
      private const int SB_PAGEDOWN = 3;
      private const int SB_PAGELEFT = 2;
      private const int SB_PAGERIGHT = 3;
      private const int SB_PAGEUP = 2;
      private const int SB_RIGHT = 7;
      private const int SB_THUMBPOSITION = 4;
      private const int SB_THUMBTRACK = 5;
      private const int SB_TOP = 6;
      private const int SB_VERT = 1;

      private const int SIF_RANGE = 0x1;
      private const int SIF_PAGE = 0x2;
      private const int SIF_POS = 0x4;
      private const int SIF_DISABLENOSCROLL = 0x8;
      private const int SIF_TRACKPOS = 0x10;
      private const int SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS |
       SIF_TRACKPOS);

      private const int ESB_DISABLE_BOTH = 0x3;
      private const int ESB_ENABLE_BOTH = 0x0;

      private const int SBS_SIZEGRIP = 0x10;

      [DllImport("user32.dll")]
      private static extern int EnableScrollBar (IntPtr hWnd, int wSBflags, int
       wArrows);
      [DllImport("user32.dll")]
      private static extern int ShowScrollBar (IntPtr hWnd, int wBar, bool
       bShow);

      // Non-client messages:
      private const int WM_NCLBUTTONDOWN = 0xA1;
      private const int WM_NCRBUTTONDOWN = 0xA4;
      private const int WM_NCMBUTTONDOWN = 0xA7;

      // Hit test codes for scroll bars:
      private const int HTHSCROLL = 6;
      private const int HTVSCROLL = 7;

      // Scroll bar messages:
      private const int WM_VSCROLL = 0x115;
      private const int WM_HSCROLL = 0x114;
      private const int WM_MOUSEWHEEL = 0x20A;

      // Mouse wheel stuff:
      private const int WHEEL_DELTA = 120;
      private const int WHEEL_PAGESCROLL = -1;
      private const int SPI_GETWHEELSCROLLLINES = 0x68;
      #endregion

      #region events
      /// <summary>
      /// Raised when the scroll position changed.
      /// </summary>
      public event ControllableScrollEventHandler Scroll;
      /// <summary>
      /// Raised when the user clicks a scroll bar.
      /// </summary>
      public event EventHandler ScrollClick;
      #endregion

      #region Member Variables
      /// <summary> 
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;
      /// <summary>
      /// Object enabling properties of the horizontal scroll bar
      /// to be read/changed.
      /// </summary>
      private ScrollBar horizontal = null;
      /// <summary>
      /// Object enabling properties of the vertical scroll bar
      /// to be read/changed.
      /// </summary>
      private ScrollBar vertical = null;
      /// <summary>
      /// Minimum value of the scroll bars.
      /// </summary>
      private int[] min = {0, 0};
      /// <summary>
      /// Maximum value of the scroll bars.
      /// </summary>
      private int[] max = {100, 100};
      /// <summary>
      /// SmallChange amount of the scroll bars.
      /// </summary>
      private int[] smallChange = {1, 1};
      /// <summary>
      /// LargeChange amount of the scroll bars.
      /// </summary>
      private int[] largeChange = {10, 10};
      /// <summary>
      /// Position of the scroll bars.
      /// </summary>
      private int[] pos = {0, 0};
      /// <summary>
      /// Whether the scroll bars are visible.
      /// </summary>
      private bool[] visible = {false, false};
      /// <summary>
      /// Whether the scroll bars are enabled.
      /// </summary>
      private bool[] enabled = {true, true};
      #endregion

      /// <summary>
      /// Constructs a new instance of the control.
      /// </summary>
      public ControllableScrollableControl()
      {
         // This call is required by the Windows.Forms Form Designer.
         InitializeComponent();

         horizontal = new ScrollBar(this, ScrollBarOrientation.Horizontal);
         vertical = new ScrollBar(this, ScrollBarOrientation.Vertical);
      }

      /// <summary> 
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
         if( disposing )
         {
            if(components != null)
            {
               components.Dispose();
            }
         }
         base.Dispose( disposing );
      }

      #region Component Designer generated code
      /// <summary> 
      /// Required method for Designer support - do not modify 
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         components = new System.ComponentModel.Container();
      }
      #endregion

      #region Public Implementation
      /// <summary>
      /// Gets an object which can be used to get/set the properties
      /// of the horizontal scroll bar.
      /// </summary>
      public ScrollBar HorizontalScrollBar
      {
         get
         {
            return horizontal;
         }
      }

      /// <summary>
      /// Gets an object which can be used to get/set the properties
      /// of the vertical scroll bar.
      /// </summary>
      public ScrollBar VerticalScrollBar
      {
         get
         {
            return vertical;
         }
      }
      #endregion

      #region Internal Implementation
      internal int InternalValue(ScrollBarOrientation orientation)
      {
         if (Handle == IntPtr.Zero)
         {
            return pos[(int) orientation];
         }
         else
         {
            SCROLLINFO si = GetScrollInfo(orientation);
            return si.nPos;
         }
      }
      
      internal bool InternalValue(ScrollBarOrientation orientation, int value)
      {
         bool changed = false;
         if (!Handle.Equals(IntPtr.Zero))
         {
            if (InternalValue(orientation) != value)
            {
               SCROLLINFO si = new SCROLLINFO();
               si.fMask = SIF_POS;
               si.nPos = value;
               SetScrollInfo(orientation, si);
               changed = true;
            }
         }         
         pos[(int) orientation] = value;
         return changed;
      }

      internal int InternalSmallChange(ScrollBarOrientation orientation)
      {
         return smallChange[(int) orientation];
      }
      
      internal void InternalSmallChange(ScrollBarOrientation orientation, int
       value)
      {
         smallChange[(int) orientation] = value;
      }

      internal int InternalLargeChange(ScrollBarOrientation orientation)
      {
         return largeChange[(int) orientation];
      }
      
      internal void InternalLargeChange(ScrollBarOrientation orientation, int
       value)
      {
         if (!Handle.Equals(IntPtr.Zero))
         {
            if (largeChange[(int) orientation] != value)
            {
               SCROLLINFO si = GetScrollInfo(orientation);
               si.nMax += value - si.nPage;
               si.nPage = value;
               si.fMask &= ~SIF_POS;
               SetScrollInfo(orientation, si);
            }
         }         
         largeChange[(int) orientation] = value;
      }

      internal int InternalMin(ScrollBarOrientation orientation)
      {
         return min[(int) orientation];
      }
      
      internal void InternalMin(ScrollBarOrientation orientation, int value)
      {
         if (!Handle.Equals(IntPtr.Zero))
         {
            if (largeChange[(int) orientation] != value)
            {
               SCROLLINFO si = GetScrollInfo(orientation);
               si.nMin = value;
               si.fMask &= ~SIF_POS;
               SetScrollInfo(orientation, si);
            }
         }         
         min[(int) orientation] = value;
      }

      internal int InternalMax(ScrollBarOrientation orientation)
      {
         return max[(int) orientation];
      }
      
      internal void InternalMax(ScrollBarOrientation orientation, int value)
      {
         if (!Handle.Equals(IntPtr.Zero))
         {
            SCROLLINFO si = new SCROLLINFO();
            si.nMax = value + largeChange[(int) orientation] - 1;
            si.nMin = min[(int) orientation];
            si.fMask = SIF_RANGE;
            SetScrollInfo(orientation, si);
         }
         max[(int) orientation] = value;
      }

      internal bool InternalVisible(ScrollBarOrientation orientation)
      {
         return visible[(int) orientation];
      }
      
      internal void InternalVisible(ScrollBarOrientation orientation, bool
       value)
      {
         if (value != visible[(int) orientation])
         {
            if (!Handle.Equals(IntPtr.Zero))
            {
               ShowScrollBar(Handle, (int) orientation, value);
            }
         }
         visible[(int) orientation] = value;
      }

      internal bool InternalEnabled(ScrollBarOrientation bar)
      {         
         return enabled[0];
      }
      
      internal void InternalEnabled(ScrollBarOrientation orientation, bool
       value)
      {
         if (value != enabled[(int) orientation]) 
         {
            if (!Handle.Equals(IntPtr.Zero))
            {
               EnableScrollBar(Handle, ((int) orientation), (value ?
                ESB_ENABLE_BOTH : ESB_DISABLE_BOTH));
            }
            enabled[(int) orientation] = value;
         }
      }
      #endregion

      #region Private Implementation
      private SCROLLINFO GetScrollInfo(ScrollBarOrientation orientation)
      {
         SCROLLINFO si = new SCROLLINFO();
         si.fMask = SIF_ALL;
         si.cbSize = Marshal.SizeOf(si.GetType());
         GetScrollInfo(Handle, (int) orientation, ref si);
         return si;
      }
      
      private void SetScrollInfo(ScrollBarOrientation orientation, SCROLLINFO
       si)
      {
         si.cbSize = Marshal.SizeOf(si.GetType());
         SetScrollInfo(Handle, (int) orientation, ref si, true);
      }
      #endregion

      #region Virtuals and Overrides
      /// <summary>
      /// Raises the <see cref="System.Windows.Forms.Control.HandleCreated"/>
       event and creates
      /// the scroll bars in their initial state.
      /// </summary>
      /// <param name="e">Not used.</param>
      protected override void OnHandleCreated(EventArgs e)
      {
         EnableScrollBar(Handle, SB_HORZ, (enabled[0] ? ESB_ENABLE_BOTH :
          ESB_DISABLE_BOTH));
         
         for (int i = 0; i < 2; i++)
         {
            SCROLLINFO si = new SCROLLINFO();
            si.cbSize = Marshal.SizeOf(si.GetType());
            si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
            si.nMax = max[i] + largeChange[i] - 1;
            si.nMin = min[i];
            si.nPage = largeChange[i];
            si.nPos = pos[i];
            SetScrollInfo((ScrollBarOrientation) i, si);
            ShowScrollBar(Handle, i, visible[i]);
         }
      }

      /// <summary>
      /// Responds to control messages and performs scroll
      /// bar processing.
      /// </summary>
      /// <param name="m">Windows message to process</param>
      protected override void WndProc(ref Message m)
      {
         int nPos = 0;
         ControllableScrollEventArgs e = null;

         switch (m.Msg)
         {
            case WM_MOUSEWHEEL:
               // High order word is the distance the wheel has been rotated,
               // in multiples of WHEEL_DELTA:
               int zDelta = 0;
               if (((int) m.WParam & 0x8000000) == 0x8000000)
               {
                  // Towards the user:
                  zDelta = 0x8000 - ((int) m.WParam & 0x7FFF0000) / 0x10000;
               }
               else
               {
                  zDelta = -((int) m.WParam & 0x7FFF000) / 0x10000;
               }
               int delta = (zDelta / WHEEL_DELTA) * smallChange[SB_VERT] *
                SystemInformation.MouseWheelScrollLines;
               MouseEventArgs me = new MouseEventArgs(MouseButtons.None, 0, 0,
                0, delta);
               OnMouseWheel(me);
               if (me.Delta != 0)
               {
                  if (visible[SB_VERT])
                  {
                     nPos = InternalValue(ScrollBarOrientation.Vertical);
                     nPos += delta;
                     if (nPos >= max[(int) ScrollBarOrientation.Vertical])
                     {
                        nPos = max[(int) ScrollBarOrientation.Vertical];
                     }
                     if (InternalValue(ScrollBarOrientation.Vertical, nPos))
                     {
                        e = new ControllableScrollEventArgs(
                           ScrollBarOrientation.Vertical,
                            ScrollEventType.First,
                            InternalValue(ScrollBarOrientation.Vertical ));
                        OnScroll(e);
                     }
                     m.Result = (IntPtr) 1;
                  }
               }
               break;

            case WM_VSCROLL:
            case WM_HSCROLL:
               base.WndProc(ref m);

               ScrollBarOrientation orientation =
                ScrollBarOrientation.Horizontal;
               if (m.Msg == WM_VSCROLL)
               {
                  orientation = ScrollBarOrientation.Vertical;
               }

               int scrollCode = ((int) m.WParam) & 0xFFFF;

            switch (scrollCode)
            {
               case SB_THUMBTRACK:
                  SCROLLINFO si = GetScrollInfo(orientation);
                  if (InternalValue(orientation, si.nTrackPos))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.ThumbTrack, si.nTrackPos);
                     OnScroll(e);
                  }
                  break;

               case SB_LEFT: // == SB_TOP
                  if (InternalValue(orientation, InternalMin(orientation)))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.First,
                         InternalValue(orientation));
                  }
                  OnScroll(e);
                  break;

               case SB_RIGHT: // == SB_BOTTOM                     
                  if (InternalValue(orientation, InternalMax(orientation)))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.Last,
                         InternalValue(orientation));
                     OnScroll(e);
                  }
                  break;

               case SB_LINELEFT: // == SB_LINEUP
                  nPos = InternalValue(orientation);
                  nPos -= smallChange[(int) orientation];
                  if (nPos < min[(int) orientation])
                  {
                     nPos = min[(int) orientation];
                  }
                  if (InternalValue(orientation, nPos))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.SmallDecrement,
                         InternalValue(orientation));
                     OnScroll(e);
                  }
                  break;

               case SB_LINERIGHT: // == SB_LINEDOWN
                  nPos = InternalValue(orientation);
                  nPos += smallChange[(int) orientation];
                  if (nPos >= max[(int) orientation])
                  {
                     nPos = max[(int) orientation];
                  }
                  if (InternalValue(orientation, nPos))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.SmallIncrement,
                         InternalValue(orientation));
                     OnScroll(e);
                  }
                  break;

               case SB_PAGELEFT: // == SB_PAGEUP
                  nPos = InternalValue(orientation);
                  nPos -= largeChange[(int) orientation];
                  if (nPos < min[(int) orientation])
                  {
                     nPos = min[(int) orientation];
                  }
                  if (InternalValue(orientation, nPos))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.LargeDecrement,
                         InternalValue(orientation));
                     OnScroll(e);
                  }
                  break;

               case SB_PAGERIGHT: // == SB_PAGEDOWN
                  nPos = InternalValue(orientation);
                  nPos += largeChange[(int) orientation];
                  if (nPos >= max[(int) orientation] )
                  {
                     nPos = max[(int) orientation];
                  }
                  if (InternalValue(orientation, nPos))
                  {
                     e = new ControllableScrollEventArgs(
                        orientation, ScrollEventType.LargeIncrement,
                         InternalValue(orientation));
                     OnScroll(e);
                  }
                  break;

               case SB_ENDSCROLL:

                  break;

            }

               break;
            
            case WM_NCLBUTTONDOWN:
            case WM_NCRBUTTONDOWN:
               base.WndProc(ref m);
               OnScrollClick(new EventArgs());
               break;

            default:
               base.WndProc(ref m);
               break;
         }
      }

      /// <summary>
      /// Raises the <see cref="Scroll"/> event.
      /// </summary>
      /// <param name="e"><see cref="ControllableScrollEventArgs"/>
      /// describing the scroll event.</param>
      protected virtual void OnScroll(ControllableScrollEventArgs e)
      {
         if (Scroll != null)
         {
            Scroll(this, e);
         }
      }

      /// <summary>
      /// Raises the <see cref="ScrollClick"/> event.
      /// </summary>
      /// <param name="e">Not used.</param>
      protected virtual void OnScrollClick(EventArgs e)
      {
         if (ScrollClick != null)
         {
            ScrollClick(this, e);
         }
      }
      #endregion
   }

   /// <summary>
   /// Enables the properties of a scroll bar to read/changed.
   /// </summary>
   public class ScrollBar
   {
      /// <summary>
      /// Owning control for this instance of the class.
      /// </summary>
      private ControllableScrollableControl owner = null;
      /// <summary>
      /// Orientation of this scroll bar.
      /// </summary>
      private ScrollBarOrientation orientation =
       ScrollBarOrientation.Horizontal;
      
      /// <summary>
      /// Constructs a new instance of this class.
      /// </summary>
      /// <param name="owner"><see cref="ControllableScrollableControl"/>
      /// which owns this object.</param>
      /// <param name="orientation">Scroll Bar orientation for this
       object.</param>
      internal ScrollBar(ControllableScrollableControl owner,
       ScrollBarOrientation orientation)
      {
         this.owner = owner;
         this.orientation = orientation;
      }

      /// <summary>
      /// Gets/sets whether the scroll bar is visible.
      /// </summary>
      public bool Visible
      {
         get
         {
            return owner.InternalVisible(orientation);
         }
         set
         {
            owner.InternalVisible(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets whether the scroll bar is enabled.
      /// </summary>
      public bool Enabled
      {
         get
         {
            return owner.InternalEnabled(orientation);
         }
         set
         {
            owner.InternalEnabled(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets the large change amount for the scroll bar.
      /// </summary>
      public int LargeChange
      {
         get
         {
            return owner.InternalLargeChange(orientation);
         }
         set
         {
            owner.InternalLargeChange(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets the small change amount for the scroll bar.
      /// </summary>
      public int SmallChange
      {
         get
         {
            return owner.InternalSmallChange(orientation);
         }
         set
         {
            owner.InternalSmallChange(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets the minimum value of the scroll bar.
      /// </summary>
      public int Min
      {
         get
         {
            return owner.InternalMin(orientation);
         }
         set
         {
            owner.InternalMin(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets the maximum value of the scroll bar.
      /// </summary>
      public int Max
      {
         get
         {
            return owner.InternalMax(orientation);
         }
         set
         {
            owner.InternalMax(orientation, value);
         }
      }

      /// <summary>
      /// Gets/sets the current value of the scroll bar.
      /// </summary>
      public int Value
      {
         get
         {
            return owner.InternalValue(orientation);
         }
         set
         {
            owner.InternalValue(orientation, value);
         }
      }

   }
}