WPF: DataGridのスクロールバーが右に張り付く問題

WPFのDataGridで、バインドしているItemsSourceをシチュエーションによって差し替えるアプリケーションを作っている時、Horizontal方向のスクロールバーが右端によってしまう現象に遭遇しました。

なんとなくItemsSourceを切り替えるのがお行儀良くないからなのかな?とは思いますが、使いづらいのでなんとなく逃げてみようと思います。

現象

色々試してみると、ItemsSourceにバインドする型がDataTableで、そこそこスクロールバーを動かした状態でItemsSourceのインスタンスを切り替えるとスクロールバーが右端に寄ってしまうようです。

DataTableではなくObservableCollectionの場合は、インスタンスを切り替えた場合のみ同様の現象が発生しました。ですが、そんな使い方はしないと思うのであまり問題にはならないでしょう。

対策(逃げ)

現象が起きるのはItemsSourceが変化するときなので、そこでゴニョゴニョすれば良さそうです。
ItemsSourceChangedとかあればいいんですが、よくわからないのでDataContextのプロパティ変更のハンドラでスクロールバーの位置をセットします。

こんな感じのコードをDataContextを持つクラスに追加します。(DataGridのDataContextChangedでもいいかも)
コードビハインドに何か書くのが嫌な場合はBehaviorで解決するといいと思います。

if (DataContext is System.ComponentModel.INotifyPropertyChanged vm)
{
    vm.PropertyChanged += (_, e) =>
    {
        if (e.PropertyName?.Equals("DataTable") != true)
        {
            return;
        }
        if (VisualTreeHelper.GetChildrenCount(DataGrid) <= 0)
        {
            return;
        }
        if (VisualTreeHelper.GetChild(DataGrid, 0) is Decorator border)
        {
            var scrollViewer = border.Child as ScrollViewer;
            scrollViewer?.ScrollToHorizontalOffset(scrollViewer!.HorizontalOffset);
        }
    };
}

スクロールバーはVisualTreeHelperを利用して取得できます。
ScrollToHorizontalOffset()というメソッドで横方向の位置を設定すると右端に張り付くことは無くなります。

大抵の場合、変更前後でテーブルの幅が変化するので完全に同じ位置をキープできませんが、右に張りついちゃうよりはいいかな?という感じですかね。

とりあえずはこれで対応したいと思います。

ソースコード

github.com