2016-12-28 4 views
10

DataGridには多くの項目があり、プログラムではSelectedItemにスクロールする必要があります。私はのStackOverflowとGoogleで検索しましたし、次のように解決策があるのscrollIntoViewようだ:選択した項目にフォーカスがあるまで上または下のDataGridをスクロールWPF DataGridをスクロールして、選択した項目を上に表示します

grid.ScrollIntoView(grid.SelectedItem) 

を。ただし、選択したアイテムに関連する現在のスクロール位置によっては、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の最後までスクロールするとうまくいくが、その後は選択した項目にスクロールしても必ずしも機能しないということだ。

この問題を解決するにはどうすればよいですか、またはトップポジションの特定のレコードにスクロールするための代替戦略が他にありますか?

答えて

3

this other questionに受け入れられた答えは、このようなグリッドの最初または最後の可視行を取得する別の方法を示しています。 行の索引を見つけ出し、直接スクロールしたり、最初に表示される行が一致するまで行ごとにスクロールしたりできます。

5

このようなニーズのために直接データグリッドで作業するのではなく、コレクションビューで作業してみてください。

ここでは、可能な場合は常に最初の選択項目として目的の項目が表示されます。そうでない場合は、スクロールビューアーが最後までスクロールされ、ターゲット項目がその位置で選択されます。

キーポイントは次のとおりです。

  • 使用CollectionViewビジネス側とXAMLコントロール(IsSynchronizedWithCurrentItem=true)上の現在の項目の同期を有​​効に
  • 延期「本物の」ターゲットスクロール「を選択できるようにするためにここで、最後の項目」visualy(優先度の低いDispatcher.BeginInvokeを使用して)実行される

は、ビジネスロジックである(これはVBのC#からの自動convertionある)

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> 
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> 
関連する問題