C# 如何在ScrollView中以正确的方式滚动DataGrid?

C# 如何在ScrollView中以正确的方式滚动DataGrid?,c#,wpf,C#,Wpf,我认为这是一种非常普遍的情况,但我找不到我需要的解决方案 我的情况是,我有一个DataGrid内置ScrollViewerDataGrid有这样的参数 MinHeight="350" MaxHeight="350" 因此,这意味着我有一个dataGrid,在ScrollViewer中具有恒定的高度。而DataGrid可以包含(例如)300个项目,因此它有一个滚动条,ScrollViewer也有一个滚动条 我需要的是当用户将鼠标放在DataGrid上并向下滚动时,DataGrid中的内容向上滚动

我认为这是一种非常普遍的情况,但我找不到我需要的解决方案

我的情况是,我有一个
DataGrid
内置
ScrollViewer
DataGrid
有这样的参数

MinHeight="350"
MaxHeight="350"
因此,这意味着我有一个
dataGrid
,在
ScrollViewer
中具有恒定的高度。而
DataGrid
可以包含(例如)300个项目,因此它有一个滚动条,
ScrollViewer
也有一个滚动条

我需要的是当用户将鼠标放在
DataGrid
上并向下滚动时,
DataGrid
中的内容向上滚动到最后一项,如果是最后一项,这意味着现在我们应该将滚动事件转到
ScrollViewer
并连续滚动整个页面

我找到了一些这样的解决方案

  • 我检查了一个答案,但它们不适合我的问题。因为我得到的是-当用户将鼠标放在
    DataGrid
    上并尝试滚动时,所以
    DataGrid
    从未获得滚动事件,只是
    ScrollViewer
    获得滚动事件

    如何修复它?

    XAML
    
    一个不起任何作用的按钮
    
    代码隐藏
    使用System.Collections.Generic;
    使用System.Collections.ObjectModel;
    使用系统诊断;
    使用System.Windows;
    使用System.Windows.Controls;
    使用System.Windows.Input;
    使用System.Windows.Media;
    名称空间so_wpf_test_1
    {
    /// 
    ///MainWindow.xaml的交互逻辑
    /// 
    公共部分类主窗口:窗口
    {
    公共主窗口()
    {
    初始化组件();
    var items=新的ObservableCollection();
    对于(int i=1;i 0&&scroll.VerticalOffset==0)
    {
    sv.ScrollToVerticalOffset(sv.VerticalOffset-e.Delta);
    }
    其他的
    {
    //无特殊情况:如果是这种情况,滚动dg(WPF会自动执行此操作)
    }
    int-idx=0;
    //如果向下滚动
    如果(e.Delta<0)
    {
    //看到草图了吗
    idx=dictLastVisible[m_dg]+1;
    WriteLine($”从{(m_dg==dg?1:2)}到{idx});
    }
    //如果向上滚动
    否则,如果(e.Delta>0)
    {
    //看到草图了吗
    idx=可见[m_dg]-1;
    Debug.WriteLine($“FROM{(m_dg==dg?1:2)}UP{idx}”);
    }
    //如果索引不表示任何内容
    如果(idx!=0)
    {
    //将索引转换为基于0的
    --idx;
    //将该行滚动到视图中
    m_dg.ScrollIntoView(m_dg.Items[idx]);
    }
    }
    /// 
    ///资料来源:https://stackoverflow.com/a/41136328/258462
    /// 
    /// 
    /// 
    公共静态ScrollViewer GetScrollViewer(UIElement元素)
    {
    if(element==null)返回null;
    ScrollViewer retour=null;
    for(int i=0;i
    可能有助于素描

    如果代码没有运行或有bug,或者注释不够清晰,请发表评论。如果您有此要求,我希望您能够为动态数量的
    DataGrid
    s扩展代码

    <Window x:Class="so_wpf_test_1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:so_wpf_test_1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Border Padding="10" Background="Yellow">
            <ScrollViewer x:Name="sv">
                <StackPanel Orientation="Vertical">
                    <DataGrid x:Name="dg" MinHeight="350" MaxHeight="350" ScrollViewer.ScrollChanged="dg_ScrollChanged">
                    </DataGrid>
    
                    <Button>A button that doesn't do anything</Button>
    
                    <DataGrid x:Name="dg2" MinHeight="350" MaxHeight="350"  ScrollViewer.ScrollChanged="dg_ScrollChanged">
                    </DataGrid>
                </StackPanel>
            </ScrollViewer>
        </Border>
    </Window>
    
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace so_wpf_test_1
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                var items = new ObservableCollection<Item>();
                for (int i = 1; i <= 50; ++i)
                {
                    items.Add(new Item($"test {i}", $"abcd {i}", $"dcba {i}"));
                }
                dg.ItemsSource = items;
                dg2.ItemsSource = items;
    
                dg.PreviewMouseWheel += Dg_PreviewMouseWheel;
                dg2.PreviewMouseWheel += Dg_PreviewMouseWheel;
            }
    
            private void Dg_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
            {
                var m_dg = sender as DataGrid;
                var scroll = GetScrollViewer(m_dg);
    
                // if scrolling to bottom and beyond...
                if (e.Delta < 0 && scroll.VerticalOffset == scroll.ScrollableHeight)
                {
                    sv.ScrollToVerticalOffset(sv.VerticalOffset - e.Delta);
                }
                // if scrolling to top and beyond...
                else if (e.Delta > 0 && scroll.VerticalOffset == 0)
                {
                    sv.ScrollToVerticalOffset(sv.VerticalOffset - e.Delta);
                }
                else
                {
                    // nothing special: scroll the dg if it is the case (WPF does this automatically)
                }
    
                int idx = 0;
    
                // if scrolling down
                if (e.Delta < 0)
                {
                    // see the sketch
                    idx = dictLastVisible[m_dg] + 1;
    
                    Debug.WriteLine($"FROM {(m_dg == dg ? 1 : 2)} DOWN {idx}");
                }
                // if scrolling up
                else if (e.Delta > 0)
                {
                    // see the sketch
                    idx = dictFirstVisible[m_dg] - 1;
    
                    Debug.WriteLine($"FROM {(m_dg == dg ? 1 : 2)} UP {idx}");
                }
    
                // if the index does not represent nothing
                if (idx != 0)
                {
                    // transform index to be 0-based
                    --idx;
    
                    // scroll that row into view
                    m_dg.ScrollIntoView(m_dg.Items[idx]);
                }
            }
    
            /// <summary>
            /// Source: https://stackoverflow.com/a/41136328/258462
            /// </summary>
            /// <param name="element"></param>
            /// <returns></returns>
            public static ScrollViewer GetScrollViewer(UIElement element)
            {
                if (element == null) return null;
    
                ScrollViewer retour = null;
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element) && retour == null; i++)
                {
                    if (VisualTreeHelper.GetChild(element, i) is ScrollViewer)
                    {
                        retour = (ScrollViewer)(VisualTreeHelper.GetChild(element, i));
                    }
                    else
                    {
                        retour = GetScrollViewer(VisualTreeHelper.GetChild(element, i) as UIElement);
                    }
                }
                return retour;
            }
    
            Dictionary<DataGrid, int> dictLastVisible = new Dictionary<DataGrid, int>();
            Dictionary<DataGrid, int> dictFirstVisible = new Dictionary<DataGrid, int>();
    
            private void dg_ScrollChanged(object sender, ScrollChangedEventArgs e)
            {
                var dg = (DataGrid)sender;
    
                int idxb = -1, idxe = -1; // b = beginning, e = end; both invalid initially
    
                // from the first row towards the last row
                for (int i = 0; i < dg.Items.Count; i++)
                {
                    var v = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.Items[i]);
    
                    if (v != null)
                    {
                        idxb = i + 1; // compute the beginning row in the viewport
                        break;
                    }
                }
    
                // from the beginning row towards the last row
                for (int i = idxb + 1; i < dg.Items.Count; i++)
                {
                    var v = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.Items[i]);
    
                    if (v == null)
                    {
                        idxe = i - 1 + 1; // compute the end row in the viewport
                        break;
                    }
                }
    
                // store the two indices in two dictionaries
                dictFirstVisible[dg] = idxb;
                dictLastVisible[dg] = idxe;
            }
        }
    
        public class Item
        {
            public string A { get; set; }
            public string B { get; set; }
            public string C { get; set; }
    
            public Item(string a, string b, string c)
            {
                A = a;
                B = b;
                C = c;
            }
        }
    }