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