web-dev-qa-db-ja.com

WPFデータグリッドで自動スクロールする方法

私はバカだと思います。私は15分間検索したところ、データグリッドをスクロールするためのいくつかの異なる解決策を見つけましたが、どれも私にはうまくいかないようです。

.NET 3.5およびWPF Toolkit DataGridでWPFを使用しています。監視可能なコレクションが変更されると、グリッドが更新され、完全に機能します。現在、私のDataGridは通常のグリッド内にあり、DataGridが大きくなりすぎるとスクロールバーが表示されます。また大丈夫...

そして今、1.00000.000 $の質問が来ます:

データグリッドを最後の行までスクロールするにはどうすればよいですか?有る:

  • autoScrollプロパティなし
  • currentRowSelectedインデックスなし
  • currentCell、ただしCurrentCell = AllCells.Lastに使用できるコレクションはありません

何か案は?私は本当に愚かで、この質問がとても難しいのは奇妙に思えます。何が欠けていますか?

35

;)

if (mainDataGrid.Items.Count > 0)
{
    var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator;
    if (border != null)
    {
        var scroll = border.Child as ScrollViewer;
        if (scroll != null) scroll.ScrollToEnd();
    }
}

あなたはdatagridメソッドを使うべきです

datagrid.ScrollIntoView(itemInRow);

または

datagrid.ScrollIntoView(itemInRow, column);

この方法では、スクロールビューアなどを見つけるのにごちゃごちゃすることがありません.

49
Aran Mulholland

グリッドの自動スクロールの添付プロパティを書きました:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;

public static class DataGridBehavior
{
    public static readonly DependencyProperty AutoscrollProperty = DependencyProperty.RegisterAttached(
        "Autoscroll", typeof(bool), typeof(DataGridBehavior), new PropertyMetadata(default(bool), AutoscrollChangedCallback));

    private static readonly Dictionary<DataGrid, NotifyCollectionChangedEventHandler> handlersDict = new Dictionary<DataGrid, NotifyCollectionChangedEventHandler>();

    private static void AutoscrollChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        var dataGrid = dependencyObject as DataGrid;
        if (dataGrid == null)
        {
            throw new InvalidOperationException("Dependency object is not DataGrid.");
        }

        if ((bool)args.NewValue)
        {
            Subscribe(dataGrid);
            dataGrid.Unloaded += DataGridOnUnloaded;
            dataGrid.Loaded += DataGridOnLoaded;
        }
        else
        {
            Unsubscribe(dataGrid);
            dataGrid.Unloaded -= DataGridOnUnloaded;
            dataGrid.Loaded -= DataGridOnLoaded;
        }
    }

    private static void Subscribe(DataGrid dataGrid)
    {
        var handler = new NotifyCollectionChangedEventHandler((sender, eventArgs) => ScrollToEnd(dataGrid));
        handlersDict.Add(dataGrid, handler);
        ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged += handler;
        ScrollToEnd(dataGrid);
    }

    private static void Unsubscribe(DataGrid dataGrid)
    {
        NotifyCollectionChangedEventHandler handler;
        handlersDict.TryGetValue(dataGrid, out handler);
        if (handler == null)
        {
            return;
        }
        ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged -= handler;
        handlersDict.Remove(dataGrid);
    }

    private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        var dataGrid = (DataGrid)sender;
        if (GetAutoscroll(dataGrid))
        {
            Subscribe(dataGrid);
        }
    }

    private static void DataGridOnUnloaded(object sender, RoutedEventArgs routedEventArgs)
    {
        var dataGrid = (DataGrid)sender;
        if (GetAutoscroll(dataGrid))
        {
            Unsubscribe(dataGrid);
        }
    }

    private static void ScrollToEnd(DataGrid datagrid)
    {
        if (datagrid.Items.Count == 0)
        {
            return;
        }
        datagrid.ScrollIntoView(datagrid.Items[datagrid.Items.Count - 1]);
    }

    public static void SetAutoscroll(DependencyObject element, bool value)
    {
        element.SetValue(AutoscrollProperty, value);
    }

    public static bool GetAutoscroll(DependencyObject element)
    {
        return (bool)element.GetValue(AutoscrollProperty);
    }
}

使用法:

    <DataGrid c:DataGridBehavior.Autoscroll="{Binding AutoScroll}"/>
18

AutoScroll to the Last要素が追加された場合:

YourDataGrid.ScrollIntoView(YourDataGrid.Items.GetItemAt(YourDataGrid.Items.Count-1));

このヘルプがあります:)

8
Anas

私はこれが遅い答えであることを知っていますが、周りを検索している人々のためだけに、DataGridの一番下までスクロールする最も簡単な方法を見つけました。 DataContextChangedイベントにこれを入れます:

myDataGrid.ScrollIntoView(CollectionView.NewItemPlaceholder);

簡単でしょ?

これが機能する理由です。すべてのデータグリッドで、DataGridの下部に、バインドされているリストに新しいアイテムを追加できる場所があります。あれは CollectionView.NewItemPlaceholder、そしてDataGridにはそのうちの1つだけが存在します。だから、それにスクロールするだけです。

7
James Esh
listbox.Add(foo);
listbox.SelectedIndex = count - 1;
listbox.ScrollIntoView(listbox.SelectedItem);
listbox.SelectedIndex = -1;
6
azze

大きなデータの場合datagrid.ScrollIntoView(itemInRow、column);うまく機能しない場合は、以下の1つのみを使用する必要があります。

if (mainDataGrid.Items.Count > 0) 
        { 
            var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator; 
            if (border != null) 
            { 
                var scroll = border.Child as ScrollViewer; 
                if (scroll != null) scroll.ScrollToEnd(); 
            } 
        } 
3
TRS Rao

これを行う最も簡単な方法は、ScrollViewer.ScrollChanged添付イベントから ScrollIntoView メソッドを呼び出すことです。これは、XAMLで次のように設定できます。

<DataGrid
...
ScrollViewer.ScrollChanged="control_ScrollChanged">

ScrollChangedEventArgs オブジェクトには、レイアウトとスクロール位置(範囲、オフセット、ビューポート)の計算に役立つさまざまなプロパティがあります。デフォルトのDataGrid仮想化設定を使用する場合、これらは通常、行/列の数で測定されることに注意してください。

ユーザーがスクロールバーを動かしてグリッドの上位のアイテムを表示しない限り、新しいアイテムがDataGridに追加されるときに一番下のアイテムを表示したままにする実装例を次に示します。

    private void control_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        // If the entire contents fit on the screen, ignore this event
        if (e.ExtentHeight < e.ViewportHeight)
            return;

        // If no items are available to display, ignore this event
        if (this.Items.Count <= 0)
            return;

        // If the ExtentHeight and ViewportHeight haven't changed, ignore this event
        if (e.ExtentHeightChange == 0.0 && e.ViewportHeightChange == 0.0)
            return;

        // If we were close to the bottom when a new item appeared,
        // scroll the new item into view.  We pick a threshold of 5
        // items since issues were seen when resizing the window with
        // smaller threshold values.
        var oldExtentHeight = e.ExtentHeight - e.ExtentHeightChange;
        var oldVerticalOffset = e.VerticalOffset - e.VerticalChange;
        var oldViewportHeight = e.ViewportHeight - e.ViewportHeightChange;
        if (oldVerticalOffset + oldViewportHeight + 5 >= oldExtentHeight)
            this.ScrollIntoView(this.Items[this.Items.Count - 1]);
    }
3
Matt

実は...

WPFでDataContextを実行することについてコレクションビューについて学習していたときも、同じ問題がありました。

私も、プロダクションビルダー専用の抵抗膜方式タッチスクリーンに配置する必要があったため、ボタンを使用してDataGridを上下に移動するためにプログラムでWPFプログラムをまとめる必要があるという問題に直面しました。マウスやキーボードを使用する必要はありません。

しかし、この例は、この投稿で前述したようにScrollIntoViewメソッドを使用して私にとってうまくいきました:

    private void OnMoveUp(object sender, RoutedEventArgs e)
    {
        ICollectionView myCollectView = CollectionViewSource.GetDefaultView(Orders);
        if (myCollectView.CurrentPosition > 0)
            myCollectView.MoveCurrentToPrevious();

        if (myCollectView.CurrentItem != null)
            theDataGrid.ScrollIntoView(myCollectView.CurrentItem);
    }

    private void OnMoveDown(object sender, RoutedEventArgs e)
    {
        ICollectionView  myCollectView = CollectionViewSource.GetDefaultView(Orders);
        if (myCollectView.CurrentPosition < Orders.Count)
            myCollectView.MoveCurrentToNext();

        if (myCollectView.CurrentItem !=null)
            theDataGrid.ScrollIntoView(myCollectView.CurrentItem);
    }

OrdersはList<T>コレクション

xAMLの場合:

    <StackPanel Grid.Row="1"
        Orientation="Horizontal">
            <Button Click="OnMoveUp">
                <Image Source="Up.jpg" />
            </Button>
            <Button Click="OnMoveDown">
                <Image Source="Down.jpg" />
              </Button>
    </StackPanel>

    <DataGrid Grid.Row="2"
              x:Name="theDataGrid"
              ItemSource="{Binding Orders}"
              ScrollViewer.CanContentScroll="True"
              ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,0,5">

    << code >>


    </DataGrid>

前のアドバイスに従い、スタックパネルではなくDataGridだけを保持してください。 DataGridの行定義(この場合は3行目)で、高さを150に設定すると、スクロールバーが機能します。

1
Steve Brother

ここに別の優れたソリューションがあります。

public sealed class CustomDataGrid : DataGrid
{
    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);
    }
    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
        if (this.Items.Count > 0) this.ScrollIntoView(this.Items[this.Items.Count - 1]);
    }
}
1
James M

必要なのは、DataGridのScrollViewerオブジェクトへの参照を取得することです。次に、VerticalOffsetプロパティを操作して、一番下までスクロールできます。

アプリにさらにフレアを追加するには、スクロールにスプラインアニメーションを追加して、すべてがアプリケーションの他の部分と同じように見えるようにします。

0
Justin Niessner

次のコードは私にとってはうまくいきます。

Private Sub DataGrid1_LoadingRow(sender As Object, e As DataGridRowEventArgs) Handles DataGrid1.LoadingRow
    DataGrid1.ScrollIntoView(DataGrid1.Items.GetItemAt(DataGrid1.Items.Count - 1))
End Sub
0
user10601591

[〜#〜] mvvm [〜#〜]パターンを使用している場合は、この記事と他の記事を組み合わせて使用​​できます。 http:// www .codeproject.com/KB/WPF/AccessControlsInViewModel.aspx

付属のプロパティを使用して、ViewModelクラスのコントロールにアクセスするという考え方です。これを行ったら、データグリッドがnullではなく、アイテムが含まれていることを確認する必要があります。

if ((mainDataGrid != null) && (mainDataGrid.Items.Count > 0)){
//Same snippet
}
0
PILuaces

自動スクロールを行うMVVMの方法を探している場合は、自動スクロール動作を使用できます。ビヘイビアーは選択したアイテムまでスクロールし、System.Windows.Interactivity.dllへの参照を追加するだけです。

public class ScrollIntoViewBehavior : Behavior<DataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectionChanged += new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
    }

    void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (sender is DataGrid)
        {
            DataGrid grid = (sender as DataGrid);
            if (grid?.SelectedItem != null)
            {
                grid.Dispatcher.InvokeAsync(() =>
                {
                    grid.UpdateLayout();
                    grid.ScrollIntoView(grid.SelectedItem, null);
                });
            }
        }
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.SelectionChanged -=
            new SelectionChangedEventHandler(AssociatedObject_SelectionChanged);
    }
}

XAML

<DataGrid>
    <i:Interaction.Behaviors>
        <local:ScrollIntoViewBehavior/>
    </i:Interaction.Behaviors>
</DataGrid>
0
Sasha Yakobchuk

Datagrid.datacontextにdataviewを使用した場合、これを使用できます。

private void dgvRecords_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    var dv = dgvRecords.DataContext as DataView;
    if (dv.Count > 0)
    {
        var drv = dv[dv.Count - 1] as DataRowView;
        dgvRecords.ScrollIntoView(drv);
    }
}
0
Peter Tran

スクロールバーでdatagridviewを使用する場合は、この手法を使用する必要があります。他のテクニックを試していたからです。しかし、それらは正しく動作しません...

var border = VisualTreeHelper.GetChild(mainDataGrid, 0) as Decorator;
if(border != null)
{   var scroll = border.Child as ScrollViewer;
    if (scroll != null) scroll.ScrollToEnd(); 
}
0
Rutul Patel

WPF DataGrid自動スクロール

マウスボタンがボタンコントロール上で押されている限り、自動スクロール。

XAML

<Button x:Name="XBTNPageDown" Height="50" MouseLeftButtonDown="XBTNPageDown_MouseLeftButtonDown"  MouseUp="XBTNPageDown_MouseUp">Page Down</Button>

コード

    private bool pagedown = false;
    private DispatcherTimer pageDownTimer = new DispatcherTimer();

    private void XBTNPageDown_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        pagedown = true;
        pageDownTimer.Interval = new TimeSpan(0, 0, 0, 0, 30);
        pageDownTimer.Start();
        pageDownTimer.Tick += (o, ea) =>
        {
            if (pagedown)
            {
                var sv = XDG.FindVisualChild<ScrollViewer>();
                sv.PageDown();
                pageDownTimer.Start();
            }
            else
            {
                pageDownTimer.Stop();
            }
        };
    }

    private void XBTNPageDown_MouseUp(object sender, MouseButtonEventArgs e)
    {
        pagedown = false;
    }

これは拡張メソッドです

選択した静的クラスに配置し、上記のコードへの参照を追加します。

   public static T FindVisualChild<T>(this DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    return (T)child;
                }

                T childItem = FindVisualChild<T>(child);
                if (childItem != null) return childItem;
            }
        }
        return null;
    }

注:プロパティsvは、繰り返しの作業を避けるために移動できます。

誰でもこれを行うRX方法がありますか?

0
John Peters