我有一个包含许多项的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的末尾效果很好,但随后滚动到所选项目并不总是有效。
如何解决此问题,或者是否有其他替代策略可以滚动到顶部位置的特定记录?
答案 0 :(得分:5)
你是在正确的轨道上,只是尝试使用集合视图而不是直接在数据网格上工作以满足这种需求。
这是一个工作示例,如果可能,所需项目始终显示为第一个选定项目,否则滚动查看器将滚动到结尾,并在其位置选择目标项目。
关键点是:
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>
答案 1 :(得分:3)
this other question的已接受答案显示了获取此类网格的第一个/最后一个可见行的不同方法。 您可以找到行的索引并直接在那里滚动或逐行向下滚动,直到第一个可见行匹配。
答案 2 :(得分:3)
我用以下代码解决了这个问题:
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>