滾動WPF DataGrid以顯示頂部的選定項目




vb.net scrollviewer (2)

你在正確的軌道上,只是嘗試使用集合視圖,而不是直接在這種需求的數據網格上工作。

這是一個工作示例,如果可能,所需的項目始終顯示為第一個選定的項目,否則將滾動查看器滾動到結尾,並在其位置選擇目標項目。

關鍵是:

  • 在業務端使用CollectionView並在XAML控件上啟用當前項目同步( IsSynchronizedWithCurrentItem=true
  • 推遲“真正的”目標滾動,以允許“選擇最後一項”被視為執行(通過使用低優先級的Dispatcher.BeginInvoke

這裡是業務邏輯(這是從C#到VB的自動轉換)

Public Class Foo

    Public Property FooNumber As Integer
        Get
        End Get
        Set
        End Set
    End Property
End Class

Public Class MainWindow
    Inherits Window
    Implements INotifyPropertyChanged

    Private _myCollectionView As ICollectionView

    Public Sub New()
        MyBase.New
        DataContext = Me
        InitializeComponent
        MyCollection = New ObservableCollection(Of Foo)
        MyCollectionView = CollectionViewSource.GetDefaultView(MyCollection)
        Dim i As Integer = 0
        Do While (i < 50)
            MyCollection.Add(New Foo)
            i = (i + 1)
        Loop

    End Sub

    Public Property MyCollectionView As ICollectionView
        Get
            Return Me._myCollectionView
        End Get
        Set
            Me._myCollectionView = value
            Me.OnPropertyChanged("MyCollectionView")
        End Set
    End Property

    Private Property MyCollection As ObservableCollection(Of Foo)
        Get
        End Get
        Set
        End Set
    End Property

    Private Sub ButtonBase_OnClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim targetNum As Integer = Convert.ToInt32(targetScroll.Text)
        Dim targetObj As Foo = Me.MyCollection.FirstOrDefault(() => {  }, (r.FooNumber = targetNum))

        'THIS IS WHERE THE MAGIC HAPPENS
        If (Not (targetObj) Is Nothing) Then
            'Move to the collection view to the last item
            Me.MyCollectionView.MoveCurrentToLast
            'Bring this last item into the view
            Dim current = Me.MyCollectionView.CurrentItem
            itemsContainer.ScrollIntoView(current)
            'This is the trick : Invoking the real target item select with a low priority allows previous visual change (scroll to the last item) to be executed
            Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, New Action(() => {  }, Me.ScrollToTarget(targetObj)))
        End If

    End Sub

    Private Sub ScrollToTarget(ByVal targetObj As Foo)
        Me.MyCollectionView.MoveCurrentTo(targetObj)
        itemsContainer.ScrollIntoView(targetObj)
    End Sub

    Public Event PropertyChanged As PropertyChangedEventHandler

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        If (Not (PropertyChanged) Is Nothing) Then
            PropertyChanged?.Invoke(Me, New PropertyChangedEventArgs(propertyName))
        End If

    End Sub
End Class

這是xaml

 <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <DataGrid x:Name="itemsContainer" ItemsSource="{Binding MyCollectionView}" IsSynchronizedWithCurrentItem="True"  Margin="2" AutoGenerateColumns="False" >
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding FooNumber}"></DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

    <StackPanel Grid.Column="1">
        <TextBox x:Name="targetScroll" Text="2" Margin="2"></TextBox>
        <Button Content="Scroll To item" Click="ButtonBase_OnClick" Margin="2"></Button>
    </StackPanel>
</Grid>

我有一個DataGrid與許多項目,我需要以編程方式滾動到SelectedItem 。 我搜索了StackOverflow和Google,似乎解決方案是ScrollIntoView,如下所示:

grid.ScrollIntoView(grid.SelectedItem)

向上或向下滾動DataGrid,直到所選項目處於焦點狀態。 但是,根據相對於所選項目的當前滾動位置,所選項目最終可能是DataGrid的ScrollViewer中最後一個可見項目。 我希望所選的項目將成為ScrollViewer中的第一個可見項目(假設DataGrid中有足夠的行來允許這個項目)。 所以我試過這個:

'FindVisualChild is a custom extension method that searches in the visual tree and returns 
'the first element of the specified type
Dim sv = grid.FindVisualChild(Of ScrollViewer)
If sv IsNot Nothing Then sv.ScrollToEnd()
grid.ScrollIntoView(grid.SelectedItem)

首先我滾動到DataGrid的結尾,然後才滾動到SelectedItem,此時SelectedItem顯示在DataGrid的頂部。

我的問題是,滾動到DataGrid的結尾運作良好,但隨後滾動到選定的項目並不總是工作。

我該如何解決這個問題,或者是否有其他的策略來滾動到頂部的特定記錄?


我用下面的代碼解決了這個問題:

public partial class MainWindow:Window
{
    private ObservableCollection<Product> products=new ObservableCollection<Product> ();

    public MainWindow()
    {
        InitializeComponent ();

        for (int i = 0;i < 50;i++)
        {
            Product p=new Product { Name="Product "+i.ToString () };
            products.Add (p);
        }

        lstProduct.ItemsSource=products;
    }

    private void lstProduct_SelectionChanged(object sender,SelectionChangedEventArgs e)
    {
        products.Move (lstProduct.SelectedIndex,0);
        lstProduct.ScrollIntoView (lstProduct.SelectedItem);
    }
}

public class Product
{
    public string Name { get; set; }
}


<Grid>
    <ListBox Name="lstProduct" Margin="20" DisplayMemberPath="Name" SelectionChanged="lstProduct_SelectionChanged" />
</Grid>