WPF CollectionView/DataGrid 获取第一个可见项的索引

4

有没有一种方法可以获取用户看到的第一个可见项?

在WinForms的DatagridView中,我们有FirstDisplayedScrollingRowIndex。WPF的变体有等效物吗?

我在ViewModel中使用了一个CollectionView,它绑定到XAML中的DataGrid。

仅为清楚起见:不想获取选定行的索引,因为我已经能够这样做了...

示例
我的ObservableCollection中有20个项目,但由于大小限制,我的Datagrid只能显示13个项目。 之前,用户选择了Item2,然后向下滚动了一点,所以Item5-17可见。 我怎样才能获得Item5的索引?

XAML

<Style x:Key="DatagridStyle" TargetType="DataGrid">
        <Setter Property="AutoGenerateColumns" Value="False"/>
        <Setter Property="Background" Value="{StaticResource ColorDatagridBackground}"/>
        <Setter Property="IsReadOnly" Value="True"/>
        <Setter Property="CanUserAddRows" Value="False"/>
        <Setter Property="CanUserDeleteRows" Value="False"/>
        <Setter Property="CanUserResizeColumns" Value="False"/>
        <Setter Property="CanUserReorderColumns" Value="True"/>
        <Setter Property="CanUserSortColumns" Value="True"/>
        <Setter Property="ColumnHeaderHeight" Value="25"/>
        <Setter Property="Margin" Value="0,5,0,5"/>
        <Setter Property="ItemsSource" Value="{Binding ItemCollection}"/>
</Style>
<DataGrid DockPanel.Dock="Top" 
              Style="{StaticResource DatagridStyle}"
              util:DataGridColumnsBehavior.BindableColumns="{Binding DatagridColumns, UpdateSourceTrigger=PropertyChanged}"
              IsSynchronizedWithCurrentItem="True"
              EnableRowVirtualization="True">
        <i:Interaction.Behaviors>
            <util:DataGridScrollBehaviour />
        </i:Interaction.Behaviors>
</DataGrid>

ViewModel

private ObservableCollection<DataGridColumn> _datagridColumns;
private CollectionView _itemCollection;
private CollectionViewSource _itemCollectionSource;
public ObservableCollection<DataGridColumn> DatagridColumns
    {
        get => _datagridColumns;
        set
        {
            _datagridColumns = value;
            RaisePropertyChanged();
        }
    }
    public CollectionView ItemCollection
    {
        get => _itemCollection;
        set
        {
            _itemCollection = value;
            RaisePropertyChanged();
        }
    }
    public CollectionViewSource ItemCollectionSource
    {
        get => _itemCollectionSource;
        set
        {
            _itemCollectionSource = value;
            RaisePropertyChanged();
        }
    }


    _datagridColumns = MainViewModel.GetColumns(MainViewModel.AppMode.Match);

    _itemCollectionSource = new CollectionViewSource();
    ItemCollectionSource.Source = _vml.Main.ItemList;
    _itemCollection = (CollectionView)ItemCollectionSource.View;

期望的结果: 如果我的视图区域是...
- 在顶部,并且由于当前排序我在顶部添加一个项目,那么我希望我的视图区域仍然停留在顶部,这样我就可以看到我的新项目
- 在底部,并且由于当前排序我在底部添加一个项目,那么我希望我的视图区域移动到“新”的底部,这样我就可以看到我的新项目
- 位于任何中间位置,我希望继续查看相同数量的项目

我可以通过我的CollectionView访问SortOrder,但为了确定我的视图区域需要移动到哪里,我确实需要知道我的视图区域当前所在的位置(顶部、中间、底部)。


取决于你使用的是什么?GridViewListViewDataGrid中?一些XAML会很有用。你想要可见元素的特定原因吗?总是可以使用触发器。 - XAMlMAX
根据当前可见区域,我想决定是否以及如何自动滚动。 - Evaniar
你实际上想要实现什么目标,需要知道这个索引值吗? - Peregrine
这并没有告诉我关于你的需求。你是想滚动到那个项目,因为它与第一个项目有某种联系,还是因为你想实现一些智能滚动(这会反噬你)? - XAMlMAX
它只是用于确定我的视图区域当前所在的位置,以便我可以决定需要应用哪种类型的“智能滚动”。 - Evaniar
你有找到这方面的任何信息吗?我也有类似的需求,我想要在一个被包含在滚动视图内的ItemsControl中显示顶部项。这似乎是在安卓中可用的东西,但是我找不到在WPF中跟踪滚动到视图中的项的任何信息。 - Paul Gibson
2个回答

2

对于我的问题(一个带有项控件的滚动视图),我发现解决方案是捕获滚动更改事件并使用它。由于DataGrid具有滚动视图,我认为您可以做同样的事情,或者在其他最合适的地方使用相同的逻辑而不是滚动更改。因此,在窗口代码后台中:

    private void dgScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        int i = 0;
        DataGrid dg = (DataGrid)sender;
        foreach (ObservableFlatObservations o in dg.Items)
        {
            UIElement v = (UIElement)dg.ItemContainerGenerator.ContainerFromItem(o);
            GeneralTransform childTransform = v.TransformToAncestor((ScrollViewer)sender);
            Rect rectangle = childTransform.TransformBounds(new Rect(new Point(0, 0), v.RenderSize));
            Rect result = Rect.Intersect(new Rect(new Point(0, 0), ((ScrollViewer)sender).RenderSize), rectangle);
            if (result != Rect.Empty)
            {
                //This one is visible so do some stuff
                return;// i is the index of this item
            }
            i++;
        }
    }

在数据网格的XAML定义中:
<DataGrid x:Name="MainDataGrid" AutoGenerateColumns="False" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
          ItemsSource="{Binding oFObs.View}"
          SelectedItem="{Binding CurrentObs, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
          IsReadOnly="True"
          Grid.Row="1" Grid.ColumnSpan="3"
          ScrollViewer.ScrollChanged="dgScrollChanged">

我还没有用我的数据表格测试过这个方法,但是它对我的原始案例很有效。请注意,我的数据元素名称未映射到您的问题,但我认为您可以修改此内容以使其与您拥有的内容配合使用。

实际尝试后,结果可能更简单。在事件处理程序中,仅网格中可见的行将从ContainerFromItem调用返回值,因为数据表格虚拟化了内容。因此,我认为如果您像这样做,您可以找到数据表格中的第一个可见索引:

private void dgScrollChanged(object sender, ScrollChangedEventArgs e)
{
    int i = 0;
    DataGrid dg = (DataGrid)sender;
    foreach (GetFlatObservationsResult o in dg.ItemsSource)
    {
        DataGridRow v = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(o);
        if(v!=null)
        {
            //i is the index of the first visable row
            //do some stuff
            return;
        }
        i++;
    }
}

-1

您可以在集合标记中添加 SelectedIndex="{Binding SelIndex}" 属性。

其中 SelIndex 将是绑定到该属性的 ViewModel 文件中的属性。


正如问题中已经说明的那样:我不想要所选行的索引。我想要获取第一个可见行的索引。 - Evaniar

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