Search code examples
wpfscrollviewer

WPF Nested Scrollviewers - giving control back to parent scollviewer


This is what my control tree looks like:

<window>
 <scrollviewer>
  <expander>
   <scrollviewer>
    <grid>
    </grid>
   </scrollviewer>
  </expander>
  <expander>
   <scrollviewer>
    <grid>
    </grid>
   </scrollviewer>
  </expander>
 </scrollviewer>
</window>

Using the mouse wheel, the control automatically passes from parent to child scrollviewer, but when I scroll to the end of the child scrollviewer the control doesn't pass back to the parent scorllviewer. How do I achieve this?

The expander, grid and the scrollviewers are dynamically generated.


Solution

  • I get a similar trouble in my application. I correct it by a depency property that will catch and pass the event too his parent. This can be applied to any control that have a scroll in it. But for me, i didn't need to validate if it was at the end of the scroll to send to his parent. You will just have to add, in the OnValueChanged method, a validation for if the scroll is at the end or at the top to send to his parent.

    using System.Windows.Controls;
    
    public static class SendMouseWheelToParent
    {
       public static readonly DependencyProperty ScrollProperty
         = DependencyProperty.RegisterAttached("IsSendingMouseWheelEventToParent",
            typeof(bool),
            typeof(SendMouseWheelToParent),
            new FrameworkPropertyMetadata(OnValueChanged));
    
       /// <summary>
       /// Gets the IsSendingMouseWheelEventToParent for a given <see cref="TextBox"/>.
       /// </summary>
       /// <param name="control">
       /// The <see cref="TextBox"/> whose IsSendingMouseWheelEventToParent is to be retrieved.
       /// </param>
       /// <returns>
       /// The IsSendingMouseWheelEventToParent, or <see langword="null"/>
       /// if no IsSendingMouseWheelEventToParent has been set.
       /// </returns>
       public static bool? GetIsSendingMouseWheelEventToParent(Control control)
       {
           if (control == null)
               throw new ArgumentNullException("");
    
           return control.GetValue(ScrollProperty) as bool?;
       }
    
       /// <summary>
       /// Sets the IsSendingMouseWheelEventToParent for a given <see cref="TextBox"/>.
       /// </summary>
       /// <param name="control">
       /// The <see cref="TextBox"/> whose IsSendingMouseWheelEventToParent is to be set.
       /// </param>
       /// <param name="IsSendingMouseWheelEventToParent">
       /// The IsSendingMouseWheelEventToParent to set, or <see langword="null"/>
       /// to remove any existing IsSendingMouseWheelEventToParent from <paramref name="control"/>.
       /// </param>
       public static void SetIsSendingMouseWheelEventToParent(Control control, bool? sendToParent)
       {
           if (control == null)
               throw new ArgumentNullException("");
    
           control.SetValue(ScrollProperty, sendToParent);
       }
    
       private static void OnValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
       {
           var scrollViewer = dependencyObject as Control;
           bool? IsSendingMouseWheelEventToParent = e.NewValue as bool?;
           scrollViewer.PreviewMouseWheel -= scrollViewer_PreviewMouseWheel;
    
           if (IsSendingMouseWheelEventToParent != null && IsSendingMouseWheelEventToParent != false)
           {
             scrollViewer.SetValue(ScrollProperty, IsSendingMouseWheelEventToParent);
             scrollViewer.PreviewMouseWheel += scrollViewer_PreviewMouseWheel;
           }
       }
    
    
       private static void scrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
       {
           var scrollview = sender as Control;
    
           var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
           eventArg.RoutedEvent = UIElement.MouseWheelEvent;
           eventArg.Source = sender;
           var parent = scrollview.Parent as UIElement;
           parent.RaiseEvent(eventArg);
       }
    }