Ответ 1
Вы были на правильном пути, просто попробуйте работать с представлением коллекции вместо того, чтобы работать непосредственно с datagrid для таких потребностей.
Вот рабочий пример, где желаемый элемент всегда отображается как первый выбранный элемент, если это возможно, иначе прокрутка прокрутки до конца и целевого элемента выбирается в его положении.
Ключевыми моментами являются:
- Используйте CollectionView на стороне бизнеса и включите синхронизацию текущего элемента в элементе управления XAML (
IsSynchronizedWithCurrentItem=true
) - Отложите "реальную" целевую прокрутку, чтобы разрешить визуализацию "Выбрать последний элемент" (используя
Dispatcher.BeginInvoke
с низким приоритетом)
Вот бизнес-логика (это автоматическое преобразование из С# в 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>