WPF嵌套滚动视图-将控制权交还给父滚动视图

7
这是我的控制树的样子:
<window>
 <scrollviewer>
  <expander>
   <scrollviewer>
    <grid>
    </grid>
   </scrollviewer>
  </expander>
  <expander>
   <scrollviewer>
    <grid>
    </grid>
   </scrollviewer>
  </expander>
 </scrollviewer>
</window>

使用鼠标滚轮,控件会自动从父级滚动视图传递到子级滚动视图,但是当我滚动到子级滚动视图的末尾时,控件不会返回到父级滚动视图。如何解决这个问题?
展开器、网格和滚动视图都是动态生成的。

同时请查看 这个问题 的答案。 - piedar
2个回答

11

我在我的应用程序中遇到了类似的问题。我通过一个依赖属性来纠正它,该属性将捕获并传递事件给其父级。这可以应用于任何具有滚动条的控件。但是对于我来说,我不需要验证是否在滚动到末尾时发送到其父级。您只需在 OnValueChanged 方法中添加一个验证,即滚动条是否在顶部或底部,以将其发送到其父级。

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);
   }
}

我建议使用可视化树父级 VisualTreeHelper.GetParent(scrollview) as UIElement; 而不是逻辑树父级 scrollview.Parent as UIElement;,但是我必须承认我记不清楚哪个逻辑父级结构曾经失败过。 - grek40
1
我在我的另一个问题的答案中实现了Janick建议的内容,检查是否滚动到顶部/底部。 - Vimes
1
@Vimes,你应该将它作为答案添加。它完美地回答了问题,而且比我的好得多。 - Janick Isabelle

2
受Janick和其他人的回答启发,我实现了一个功能,在内部滚动视图(包括来自ListView、ListBox、DataGrid等)滚动到顶部/底部时,滚动祖先ScrollViewers。代码在我的这个答案中,这个问题非常相似。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接