Advertisement
Guest User

Untitled

a guest
Oct 28th, 2014
1,071
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.00 KB | None | 0 0
  1. internal sealed class ScrollViewerBehavior
  2.     {
  3.         #region Constants
  4.  
  5.         public static readonly DependencyProperty FixScrollingProperty =
  6.             DependencyProperty.RegisterAttached
  7.             ("FixScrolling", typeof(bool), typeof(ScrollViewerBehavior),
  8.             new FrameworkPropertyMetadata(false, OnFixScrollingPropertyChanged));
  9.  
  10.         #endregion
  11.  
  12.         #region Fields
  13.  
  14.         /// <summary>
  15.         /// Field used to keep track of the original MouseWheelEvent that started the current sequence
  16.         /// </summary>
  17.         private static MouseWheelEventArgs s_currentOriginalEventArg;
  18.  
  19.         /// <summary>
  20.         /// Field used to keep all the information related to the current sequence of MouseWheelEvents
  21.         /// </summary>
  22.         private static ScrollViewerBehaviorSequence s_sequence;
  23.  
  24.         #endregion
  25.  
  26.         #region Event Handlers
  27.  
  28.         public static void OnFixScrollingPropertyChanged(object sender, DependencyPropertyChangedEventArgs e)
  29.         {
  30.             var scrollViewer = sender as ScrollViewer;
  31.             if (scrollViewer != null)
  32.             {
  33.                 var fixScrolling = (bool)e.NewValue;
  34.                 if (fixScrolling)
  35.                 {
  36.                     scrollViewer.PreviewMouseWheel += OnScrollViewerPreviewMouseWheel;
  37.                 }
  38.                 else
  39.                 {
  40.                     scrollViewer.PreviewMouseWheel -= OnScrollViewerPreviewMouseWheel;
  41.                 }
  42.             }
  43.             else
  44.             {
  45.                 throw new ArgumentException("The dependency property can only be attached to a ScrollViewer", "sender");
  46.             }
  47.         }
  48.  
  49.         private static void OnScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs e)
  50.         {
  51.             var scrollViewer = sender as ScrollViewer;
  52.  
  53.             // We only want to process the PreviewMouseWheel event in the following cases:
  54.             // The event was not previously handled
  55.             // The event is a fake event OR the first time we receive an original event
  56.             // The current ScrollViewer was not already processed
  57.             if (!e.Handled && !e.Equals(s_currentOriginalEventArg) && (scrollViewer != null) &&
  58.                 (s_sequence == null || (!s_sequence.ScrollViewers.Contains(scrollViewer) && s_sequence.FakeEvents.Contains(e))))
  59.             {
  60.                 // Create our fake event that will be used to parse all the children ScrollViewers
  61.                 var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
  62.                 {
  63.                     RoutedEvent = UIElement.PreviewMouseWheelEvent,
  64.                     Source = e.OriginalSource,
  65.                 };
  66.  
  67.                 var originalSource = e.OriginalSource as IInputElement;
  68.                 if (originalSource != null)
  69.                 {
  70.                     if (s_sequence == null)
  71.                     {
  72.                         // First time through the sequence, this only happen the first time we receive the original event
  73.                         s_sequence = new ScrollViewerBehaviorSequence(e, scrollViewer, previewEventArg);
  74.                         s_currentOriginalEventArg = e;
  75.                     }
  76.                     else
  77.                     {
  78.                         s_sequence.FakeEvents.Add(previewEventArg);
  79.                         s_sequence.ScrollViewers.Add(scrollViewer);
  80.                     }
  81.  
  82.                     // Raise the fake event on the original source, so that it is pushed all the way down the logical tree to all the
  83.                     // ScrollViewers (this is done recursively by each child)
  84.                     originalSource.RaiseEvent(previewEventArg);
  85.  
  86.                     // Now that all the children ScrollViewers raised their own fake event, we can start going back up the chain
  87.                     if (s_sequence.FakeEvents.Contains(e))
  88.                     {
  89.                         if (previewEventArg.Handled)
  90.                         {
  91.                             // If one of our children already handled our fake event, handle them all the way up
  92.                             e.Handled = true;
  93.                         }
  94.                         else
  95.                         {
  96.                             // At this point if no one else handled the event in our children, we do our job
  97.                             if (IsScrollSupported(scrollViewer))
  98.                             {
  99.                                 // If this ScrollViewer is able to scroll, handle the fake events all the way up
  100.                                 e.Handled = true;
  101.                             }
  102.  
  103.                             if (IsScrollingOutOfBounds(scrollViewer, e.Delta))
  104.                             {
  105.                                 // If this ScrollViewer has reached its upper or lower bound, push a MouseWheelEvent on a parent
  106.                                 // ScrollViewer that can handle it
  107.                                 s_sequence.ForceMouseWheelEvent(scrollViewer, e);
  108.                             }
  109.                         }
  110.                     }
  111.  
  112.                     // Remove the current instances of ScrollViewer and fake event from the sequence
  113.                     s_sequence.FakeEvents.Remove(previewEventArg);
  114.                     s_sequence.ScrollViewers.Remove(scrollViewer);
  115.                     if (s_sequence.FakeEvents.Count == 0)
  116.                     {
  117.                         s_sequence = null;
  118.                     }
  119.                 }
  120.             }
  121.         }
  122.  
  123.         #endregion
  124.  
  125.         #region Public Methods
  126.  
  127.         public static bool GetFixScrolling(DependencyObject obj)
  128.         {
  129.             return (bool)obj.GetValue(FixScrollingProperty);
  130.         }
  131.  
  132.         public static void SetFixScrolling(DependencyObject obj, bool value)
  133.         {
  134.             obj.SetValue(FixScrollingProperty, value);
  135.         }
  136.  
  137.         #endregion
  138.  
  139.         #region Private Methods
  140.  
  141.         /// <summary>
  142.         /// Checks if the provided ScrollViewer can scroll, meaning if it supports scrolling and if the scrolling request is not
  143.         /// out of bounds
  144.         /// </summary>
  145.         internal static bool CanScroll(ScrollViewer scrollViewer, int delta)
  146.         {
  147.             if (!IsScrollSupported(scrollViewer) || IsScrollingOutOfBounds(scrollViewer, delta))
  148.             {
  149.                 return false;
  150.             }
  151.  
  152.             return true;
  153.         }
  154.  
  155.         /// <summary>
  156.         /// Checks if the delta scroll request is out of bound for the provided ScrollViewer, meaning if we are attempting to scroll
  157.         /// down when the scrollbar is all the way down, or up when it's all the way up.
  158.         /// </summary>
  159.         internal static bool IsScrollingOutOfBounds(ScrollViewer scrollViewer, int delta)
  160.         {
  161.             return ((delta > 0) && (scrollViewer.VerticalOffset <= 0)) ||
  162.                    ((delta <= 0) && (scrollViewer.VerticalOffset >= scrollViewer.ScrollableHeight));
  163.         }
  164.  
  165.         /// <summary>
  166.         /// Checks if the provided ScrollViewer supports scrolling, meaning if it has an actual scrollbar that can be used
  167.         /// </summary>
  168.         internal static bool IsScrollSupported(ScrollViewer scrollViewer)
  169.         {
  170.             return (scrollViewer.ScrollableHeight > 0) && scrollViewer.IsEnabled;
  171.         }
  172.  
  173.         #endregion
  174.     }
  175.  
  176.     internal sealed class ScrollViewerBehaviorSequence
  177.     {
  178.         #region Properties
  179.  
  180.         /// <summary>
  181.         /// The list of all the fake events raised in the sequence
  182.         /// </summary>
  183.         public List<MouseWheelEventArgs> FakeEvents { get; private set; }
  184.  
  185.         /// <summary>
  186.         /// The real original MouseWheelEvent that started the sequence
  187.         /// </summary>
  188.         public MouseWheelEventArgs OriginalEvent { get; private set; }
  189.  
  190.         /// <summary>
  191.         /// The list of all the ScrollViewers involved in the sequence
  192.         /// </summary>
  193.         public List<ScrollViewer> ScrollViewers { get; private set; }
  194.  
  195.         #endregion
  196.  
  197.         #region Constructors
  198.  
  199.         public ScrollViewerBehaviorSequence(MouseWheelEventArgs originalEvent, ScrollViewer scrollViewer, MouseWheelEventArgs fakeEvent)
  200.         {
  201.             OriginalEvent = originalEvent;
  202.             FakeEvents = new List<MouseWheelEventArgs> { fakeEvent };
  203.             ScrollViewers = new List<ScrollViewer> { scrollViewer };
  204.         }
  205.  
  206.         #endregion
  207.  
  208.         #region Public Methods
  209.  
  210.         /// <summary>
  211.         /// Handles all the events of the sequence, including the original one
  212.         /// </summary>
  213.         public void HandleEvents()
  214.         {
  215.             foreach (var fakeEvent in FakeEvents)
  216.             {
  217.                 fakeEvent.Handled = true;
  218.             }
  219.  
  220.             OriginalEvent.Handled = true;
  221.         }
  222.  
  223.         public void ForceMouseWheelEvent(ScrollViewer scrollViewer, MouseWheelEventArgs e)
  224.         {
  225.             // Since the current ScrollViewer isn't able to scroll, push the event to the first parent who can
  226.             int index = ScrollViewers.IndexOf(scrollViewer);
  227.             if (index > -1)
  228.             {
  229.                 for (int i = index - 1; i >= 0; --i)
  230.                 {
  231.                     var parentScrollViewer = ScrollViewers[i];
  232.                     if (ScrollViewerBehavior.CanScroll(parentScrollViewer, e.Delta))
  233.                     {
  234.                         e.Handled = true;
  235.  
  236.                         // Since we will trigger our own MouseWheelEvent on the right ScrollViewer,
  237.                         // handle all the chain of preview events (fake and real)
  238.                         HandleEvents();
  239.  
  240.                         var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
  241.                         eventArg.RoutedEvent = UIElement.MouseWheelEvent;
  242.                         eventArg.Source = scrollViewer;
  243.                         parentScrollViewer.RaiseEvent(eventArg);
  244.                         break;
  245.                     }
  246.                 }
  247.             }
  248.         }
  249.  
  250.         #endregion
  251.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement