You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取WPF DataGrid中可见行的索引?用于十六进制编辑器分块加载

WPF DataGrid 获取可见行索引实现十六进制编辑器按需加载

一、获取可见行的起始/结束索引

要实现按需加载,首先得准确获取当前DataGrid中可见行的索引范围。可以通过遍历可视化树找到DataGrid的ScrollViewer,再结合行的位置判断是否在可见区域内:

public (int StartIndex, int EndIndex) GetVisibleRowRange(DataGrid dataGrid)
{
    var scrollViewer = FindVisualChild<ScrollViewer>(dataGrid);
    if (scrollViewer == null)
        return (-1, -1);

    double viewportTop = scrollViewer.VerticalOffset;
    double viewportBottom = viewportTop + scrollViewer.ViewportHeight;

    int startIdx = int.MaxValue;
    int endIdx = int.MinValue;

    for (int i = 0; i < dataGrid.Items.Count; i++)
    {
        var row = dataGrid.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
        if (row == null) continue;

        // 计算行在ScrollViewer坐标系中的位置
        Point rowTopPoint = row.TransformToAncestor(scrollViewer).Transform(new Point(0, 0));
        double rowTop = rowTopPoint.Y;
        double rowBottom = rowTop + row.ActualHeight;

        // 判断行是否与可见视口有交集
        if (rowBottom > viewportTop && rowTop < viewportBottom)
        {
            if (i < startIdx) startIdx = i;
            if (i > endIdx) endIdx = i;
        }
    }

    return (startIdx == int.MaxValue ? -1 : startIdx, endIdx == int.MinValue ? -1 : endIdx);
}

private T FindVisualChild<T>(DependencyObject parent) where T : Visual
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        Visual child = (Visual)VisualTreeHelper.GetChild(parent, i);
        if (child is T target)
            return target;

        T nestedChild = FindVisualChild<T>(child);
        if (nestedChild != null)
            return nestedChild;
    }
    return null;
}

二、绑定自定义滚动条与加载逻辑

  1. 初始化滚动条参数:根据文件总大小计算总行数,设置滚动条的最大值和步长:
// 假设每行显示16字节
int bytesPerRow = 16;
long totalFileSize = new FileInfo(filePath).Length;
int totalRows = (int)Math.Ceiling((double)totalFileSize / bytesPerRow);

customVerticalScrollBar.Maximum = totalRows - visibleRowCount; // 避免滚动到超出文件范围
customVerticalScrollBar.LargeChange = visibleRowCount; // 一页行数
customVerticalScrollBar.SmallChange = 1; // 单行滚动
  1. 处理滚动事件:根据滚动位置计算需要加载的文件块,建议预留缓冲区域(比如前后各多加载半页),避免滚动时频繁加载:
private void CustomVerticalScrollBar_Scroll(object sender, ScrollEventArgs e)
{
    var scrollBar = sender as ScrollBar;
    if (scrollBar == null) return;

    int targetStartRow = (int)scrollBar.Value;
    // 计算加载范围:前后各加半页作为缓冲
    int buffer = visibleRowCount / 2;
    int loadStart = Math.Max(0, targetStartRow - buffer);
    int loadEnd = Math.Min(totalRows - 1, targetStartRow + visibleRowCount + buffer);

    // 调用文件读取方法加载对应块
    LoadHexDataBlock(loadStart, loadEnd, bytesPerRow);

    // 让DataGrid滚动到目标起始行
    dataGrid.ScrollIntoView(dataGrid.Items[targetStartRow]);
}
  1. 同步DataGrid与滚动条位置:当窗口大小变化或DataGrid布局更新时,同步滚动条位置避免错位:
private void DataGrid_LayoutUpdated(object sender, EventArgs e)
{
    var (startIdx, _) = GetVisibleRowRange(dataGrid);
    if (startIdx != -1 && !customVerticalScrollBar.IsMouseCaptured)
    {
        customVerticalScrollBar.Value = startIdx;
    }
}

三、优化DataGrid虚拟化配置

确保DataGrid启用虚拟化,提升渲染效率:

<DataGrid x:Name="hexDataGrid"
          VirtualizingStackPanel.IsVirtualizing="True"
          VirtualizingStackPanel.VirtualizationMode="Recycling"
          ScrollViewer.VerticalScrollBarVisibility="Hidden"
          CanUserScrollRows="False">
    <!-- 列定义 -->
</DataGrid>

内容的提问来源于stack exchange,提问作者Tsaras

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:25:38