private static void OnScrollViewerPreviewMouseWheel(object sender, MouseWheelEventArgs e) { var scrollViewer = sender as ScrollViewer; // We only want to process the PreviewMouseWheel event in the following cases: // The event was not previously handled // The event is a fake event OR the first time we receive an original event // The current ScrollViewer was not already processed if (!e.Handled && !e.Equals(s_currentOriginalEventArg) && (scrollViewer != null) && (s_sequence == null || (!s_sequence.ScrollViewers.Contains(scrollViewer) && s_sequence.FakeEvents.Contains(e)))) { // Create our fake event that will be used to parse all the children ScrollViewers var previewEventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = UIElement.PreviewMouseWheelEvent, Source = e.OriginalSource, }; var originalSource = e.OriginalSource as IInputElement; if (originalSource != null) { if (s_sequence == null) { // First time through the sequence, this only happen the first time we receive the original event s_sequence = new ScrollViewerBehaviorSequence(e, scrollViewer, previewEventArg); s_currentOriginalEventArg = e; } else { s_sequence.FakeEvents.Add(previewEventArg); s_sequence.ScrollViewers.Add(scrollViewer); } // Raise the fake event on the original source, so that it is pushed all the way down the logical tree to all the // ScrollViewers (this is done recursively by each child) originalSource.RaiseEvent(previewEventArg); // Now that all the children ScrollViewers raised their own fake event, we can start going back up the chain if (s_sequence.FakeEvents.Contains(e)) { if (previewEventArg.Handled) { // If one of our children already handled our fake event, handle them all the way up e.Handled = true; } else { // At this point if no one else handled the event in our children, we do our job if (IsScrollSupported(scrollViewer)) { // If this ScrollViewer is able to scroll, handle the fake events all the way up e.Handled = true; } if (IsScrollingOutOfBounds(scrollViewer, e.Delta)) { // If this ScrollViewer has reached its upper or lower bound, push a MouseWheelEvent on a parent // ScrollViewer that can handle it s_sequence.ForceMouseWheelEvent(scrollViewer, e); } } } // Remove the current instances of ScrollViewer and fake event from the sequence s_sequence.FakeEvents.Remove(previewEventArg); s_sequence.ScrollViewers.Remove(scrollViewer); if (s_sequence.FakeEvents.Count == 0) { s_sequence = null; } } } }