WPF ScrollViewer 内嵌ScrollViewer时,内部到顶(底)把滚动动作传到外部(WPF笔记)

WPF ScrollViewer内嵌套ScrollViewer或DataGrid、ListBox等包含滚动条的控件,鼠标置于内部

ScrollViewer时滚轮动作无法传递到外部ScrollViewer,该方法实现内部ScrollViewer到底或到顶后,滚轮动作传递至外部,让外部ScrollViewer滚动

1.1 ScrollViewer嵌套
  • MainWindow.xaml.cs添加隧道事件
    private void ChildScroll_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (e.Delta > 0 && ChildScroll.VerticalOffset == 0)
        {
            // 向上滚动并且已经到顶部
            OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
            e.Handled = true;
        }
        else if (e.Delta < 0 && ChildScroll.VerticalOffset >= ChildScroll.ExtentHeight - ChildScroll.ViewportHeight)
        {
            // 向下滚动并且已经到底部
            OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
            e.Handled = true;
        }
    }
  •  MainWindow.xaml
<ScrollViewer
    x:Name="OuterScrollViewer"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <ScrollViewer
         x:Name="ChildScroll"
         HorizontalScrollBarVisibility="Auto"
         PreviewMouseWheel="ChildScroll_PreviewMouseWheel"
         VerticalScrollBarVisibility="Auto">
        <!-- 其他内容 -->
    </ScrollViewer>
</ScrollViewer>
1.2  ScrollViewer内DataGrid、ListBox
  • MainWindow.xaml.cs
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    ListBoxScrollViewer = GetScrollViewer(Listbox1);
    DataGridScrollViewer = GetScrollViewer(DG);
}
private ScrollViewer? ListBoxScrollViewer;
 private void Listbox1_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
 {
     if (ListBoxScrollViewer == null)
     {
         return;
     }
     if (e.Delta > 0 && ListBoxScrollViewer.VerticalOffset == 0)
     {
         // 向上滚动并且已经到顶部
         OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
         e.Handled = true;
     }
     else if (e.Delta < 0 && ListBoxScrollViewer.VerticalOffset >= ListBoxScrollViewer.ExtentHeight - ListBoxScrollViewer.ViewportHeight)
     {
         // 向下滚动并且已经到底部
         OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
         e.Handled = true;
     }
 }

 private ScrollViewer? DataGridScrollViewer;
 private void DG_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
 {
     if (DataGridScrollViewer == null)
     {
         return ;
     }
     if (e.Delta > 0 && DataGridScrollViewer.VerticalOffset == 0)
     {
         // 向上滚动并且已经到顶部
         OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
         e.Handled = true;
     }
     else if (e.Delta < 0 && DataGridScrollViewer.VerticalOffset >= DataGridScrollViewer.ExtentHeight - DataGridScrollViewer.ViewportHeight)
     {
         // 向下滚动并且已经到底部
         OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
         e.Handled = true;
     }
 }

/// <summary>
/// 获取控件内部ScrollViewer
/// </summary>
/// <param name="depObj"></param>
/// <returns></returns>
 private ScrollViewer? GetScrollViewer(DependencyObject depObj)
 {
     if (depObj is ScrollViewer) return depObj as ScrollViewer;

     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
     {
         var child = VisualTreeHelper.GetChild(depObj, i);
         var result = GetScrollViewer(child);
         if (result != null) return result;
     }
     return null;
 }
  • MainWindow.xaml
<ScrollViewer
    x:Name="OuterScrollViewer"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <DataGrid
    	x:Name="DG"
    	PreviewMouseWheel="DG_PreviewMouseWheel" />
    <ListBox
    	x:Name="Listbox1"
    	PreviewMouseWheel="Listbox1_PreviewMouseWheel">
</ListBox>
</ScrollViewer>
 2、MVVM
  • 添加行为ScrollViewerMouseWheelBehavior.cs
using Microsoft.Xaml.Behaviors;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows;

namespace WpfApp1.Lib
{
    public class ScrollViewerMouseWheelBehavior : Behavior<ScrollViewer>
    {
        public static readonly DependencyProperty OuterScrollViewerProperty =
       DependencyProperty.Register(
           nameof(OuterScrollViewer),
           typeof(ScrollViewer),
           typeof(ScrollViewerMouseWheelBehavior));
        public ScrollViewer OuterScrollViewer
        {
            get { return (ScrollViewer)GetValue(OuterScrollViewerProperty); }
            set { SetValue(OuterScrollViewerProperty, value); }
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.PreviewMouseWheel += OnPreviewMouseWheel;
        }

        protected override void OnDetaching()
        {
            AssociatedObject.PreviewMouseWheel -= OnPreviewMouseWheel;
            base.OnDetaching();
        }

        private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (OuterScrollViewer == null) return;

            var scrollViewer = (ScrollViewer)sender;

            if (e.Delta > 0 && scrollViewer.VerticalOffset == 0)
            {
                // 向上滚动并且已经到顶部
                OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
                e.Handled = true;
            }
            else if (e.Delta < 0 && scrollViewer.VerticalOffset >= scrollViewer.ExtentHeight - scrollViewer.ViewportHeight)
            {
                // 向下滚动并且已经到底部
                OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
                e.Handled = true;
            }
        }
    }
}
  • 添加ListBoxScrollViewerMouseWheelBehavior.cs(DataGrid同)
using Microsoft.Xaml.Behaviors;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;

namespace WpfApp1.Lib
{

    public partial class ListBoxScrollViewerMouseWheelBehavior : Behavior<ListBox>
    {
        public static readonly DependencyProperty OuterScrollViewerProperty =
        DependencyProperty.Register(
            nameof(OuterScrollViewer),
            typeof(ScrollViewer),
            typeof(ListBoxScrollViewerMouseWheelBehavior));
        public ScrollViewer OuterScrollViewer
        {
            get { return (ScrollViewer)GetValue(OuterScrollViewerProperty); }
            set { SetValue(OuterScrollViewerProperty, value); }
        }

        private ScrollViewer _innerScrollViewer;

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Loaded += OnDataGridLoaded;
        }

        private void OnDataGridLoaded(object sender, RoutedEventArgs e)
        {
            // 获取 DataGrid 内部的 ScrollViewer
            _innerScrollViewer = GetScrollViewer(AssociatedObject);
            if (_innerScrollViewer != null)
            {
                _innerScrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
            }
        }

        protected override void OnDetaching()
        {
            if (_innerScrollViewer != null)
            {
                _innerScrollViewer.PreviewMouseWheel -= OnPreviewMouseWheel;
            }
            AssociatedObject.Loaded -= OnDataGridLoaded;
            base.OnDetaching();
        }

        private ScrollViewer GetScrollViewer(DependencyObject depObj)
        {
            if (depObj is ScrollViewer) return depObj as ScrollViewer;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);
                var result = GetScrollViewer(child);
                if (result != null) return result;
            }
            return null;
        }

        private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (OuterScrollViewer == null || _innerScrollViewer == null) return;

            if (e.Delta > 0 && _innerScrollViewer.VerticalOffset == 0)
            {
                // 向上滚动并且已经到顶部
                OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
                e.Handled = true;
            }
            else if (e.Delta < 0 && _innerScrollViewer.VerticalOffset >= _innerScrollViewer.ExtentHeight - _innerScrollViewer.ViewportHeight)
            {
                // 向下滚动并且已经到底部
                OuterScrollViewer.ScrollToVerticalOffset(OuterScrollViewer.VerticalOffset - e.Delta);
                e.Handled = true;
            }
        }
    }

}
  • MainWindow.xaml
<!-- 引入命名空间 --> 
xmlns:scroll="clr-namespace:WpfApp1.Lib"


<ScrollViewer
    x:Name="OuterScrollViewer"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">
    <ScrollViewer
        HorizontalScrollBarVisibility="Auto"
        VerticalScrollBarVisibility="Auto">
        <i:Interaction.Behaviors >
            <scroll:ScrollViewerMouseWheelBehavior 
               OuterScrollViewer="{Binding ElementName=OuterScrollViewer}" />
        </i:Interaction.Behaviors>
    </ScrollViewer>
    <DataGrid>
        <i:Interaction.Behaviors>
            <scroll:DataGridScrollViewerMouseWheelBehavior 
               OuterScrollViewer="{Binding ElementName=OuterScrollViewer}" />
        </i:Interaction.Behaviors>
    </DataGrid>
    <ListBox>
        <i:Interaction.Behaviors>
            <scroll:ListBoxScrollViewerMouseWheelBehavior 
                OuterScrollViewer="{Binding ElementName=OuterScrollViewer}" />
        </i:Interaction.Behaviors>
    </ListBox>
</ScrollViewer>

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值