前提・実現したいこと
C#とWPFにてアプリを作成しております。
対象データごとに詳細データを確認、追加、編集出来るページがあるのですが、詳細データListView部分のスクロールのコントロールが上手くいかない部分があります。
対象データを変更するごとに詳細データの横スクロールを初期値(1番左)へ戻したいです。
発生している問題
詳細データを保持している対象データを表示する
↓
詳細データの横スクロールを1番右へ移動させる
↓
表示されている詳細データを削除する
↓
詳細データを保持していない対象データを表示する
↓
詳細データを追加するとスクロールが右位置のまま表示される
遷移先の対象データが詳細データを保持していればスクロールは初期値(1番左)へ戻っている
といった状態です。
該当のソースコード
C#側で対象データが変わる際に通るようScrollToHomeは実装しています。
補足情報
VisualStudio2013で実装。
調査を進めてデータがないとスクロールコントロールが出来ないように思った。
データがない場合でもスクロールをコントロール出来るよう実装したいです。
どなたかご教示お願いいたします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答1件
0
ベストアンサー
詳細データを追加するとスクロールが右位置のまま表示される
うまく日本語で説明できませんが、ListView
はItemsSource
をまたがってスクロール位置を保存しているということですよね?
なぜかアイテムが空の時に限って、水平スクロールバーも出ないし左端に寄って表示する。
そこでアイテムが追加されると、保存された位置にスクロールされる。
これ自体はちょっと変ですが標準の動作ですね。
C#側で対象データが変わる際に通るようScroll ToHomeは実装しています。
これのことでしょうか?
ScrollViewer.ScrollToHome Method (System.Windows.Controls) | Microsoft Docs
このちょっと変な動作をキャンセルするために、選択が変わるたびにScrollToHome
しているが「アイテムが空の時だけは効かない」と。
私もよくわかりませんが、これに該当するということなんでしょう。
This method does not induce any scrolling behavior if ScrollInfo is null.
単純にやるならアイテムの追加時にもScrollToHome
したらどうですか?
(確かに「位置を保存しないオプション」があってもいいですね)
今の理解での検証コード(全然違うという場合はもっと詳しく説明してください)
xml
1<Window 2 x:Class="Questions336796.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="800" 6 Height="450"> 7 <Grid> 8 <Grid.ColumnDefinitions> 9 <ColumnDefinition /> 10 <ColumnDefinition Width="Auto" /> 11 <ColumnDefinition /> 12 </Grid.ColumnDefinitions> 13 <ListBox 14 x:Name="listBox" 15 DisplayMemberPath="Name" 16 ItemsSource="{Binding Items}" 17 SelectionChanged="ListBox_SelectionChanged" /> 18 <StackPanel Grid.Column="1"> 19 <Button Click="AddButton_Click" Content="詳細追加" /> 20 <Button Click="ClearButton_Click" Content="詳細全削除" /> 21 <CheckBox 22 x:Name="checkBox1" 23 Content="ScrollToHome" 24 IsChecked="True" /> 25 <CheckBox 26 x:Name="checkBox2" 27 Content="Virtualizing" 28 IsChecked="True" /> 29 </StackPanel> 30 <ListView 31 x:Name="listView" 32 Grid.Column="2" 33 ItemsSource="{Binding SelectedItem.Details, ElementName=listBox}" 34 VirtualizingPanel.IsVirtualizing="{Binding IsChecked, ElementName=checkBox2}"> 35 <ListView.View> 36 <GridView> 37 <GridViewColumn 38 Width="200" 39 DisplayMemberBinding="{Binding Detail1}" 40 Header="詳細1" /> 41 <GridViewColumn 42 Width="200" 43 DisplayMemberBinding="{Binding Detail2}" 44 Header="詳細2" /> 45 <GridViewColumn 46 Width="200" 47 DisplayMemberBinding="{Binding Detail3}" 48 Header="詳細3" /> 49 </GridView> 50 </ListView.View> 51 </ListView> 52 </Grid> 53</Window>
cs
1using System; 2using System.Collections.Generic; 3using System.Collections.ObjectModel; 4using System.Linq; 5using System.Windows; 6using System.Windows.Controls; 7using System.Windows.Media; 8 9namespace Questions336796 10{ 11 public partial class MainWindow : Window 12 { 13 public ObservableCollection<Item> Items { get; } 14 15 public MainWindow() 16 { 17 InitializeComponent(); 18 19 Items = new ObservableCollection<Item> 20 { 21 new Item { Name = "Item1", }, 22 new Item { Name = "Item2", }, 23 new Item { Name = "Item3", }, 24 }; 25 Items[0].Details.Add(new Detail()); 26 Items[2].Details.Add(new Detail()); 27 28 DataContext = this; 29 } 30 31 private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 32 { 33 if (checkBox1.IsChecked == true) 34 { 35 var sv = listView.Descendants<ScrollViewer>().First(); 36 sv.ScrollToHome(); 37 } 38 } 39 40 private void AddButton_Click(object sender, RoutedEventArgs e) 41 { 42 if (listBox.SelectedItem is Item item) 43 item.Details.Add(new Detail()); 44 45 if (checkBox1.IsChecked == true) 46 { 47 var sv = listView.Descendants<ScrollViewer>().First(); 48 sv.ScrollToHome(); 49 } 50 } 51 52 private void ClearButton_Click(object sender, RoutedEventArgs e) 53 { 54 if (listBox.SelectedItem is Item item) 55 item.Details.Clear(); 56 } 57 } 58 59 public class Item 60 { 61 public string Name { get; set; } 62 public ObservableCollection<Detail> Details { get; } = new ObservableCollection<Detail>(); 63 } 64 65 public class Detail 66 { 67 public string Detail1 { get; set; } = "Detail1"; 68 public string Detail2 { get; set; } = "Detail2"; 69 public string Detail3 { get; set; } = "Detail3"; 70 } 71 72 // [VisualTreeの子孫要素を取得する - xin9le.net](https://blog.xin9le.net/entry/2013/10/29/222336) 73 public static class DependencyObjectExtensions 74 { 75 //--- 子要素を取得 76 public static IEnumerable<DependencyObject> Children(this DependencyObject obj) 77 { 78 if (obj == null) throw new ArgumentNullException(nameof(obj)); 79 80 var count = VisualTreeHelper.GetChildrenCount(obj); 81 if (count == 0) yield break; 82 83 for (var i = 0; i < count; i++) 84 { 85 var child = VisualTreeHelper.GetChild(obj, i); 86 if (child != null) yield return child; 87 } 88 } 89 90 //--- 子孫要素を取得 91 public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj) 92 { 93 if (obj == null) throw new ArgumentNullException(nameof(obj)); 94 95 foreach (var child in obj.Children()) 96 { 97 yield return child; 98 foreach (var grandChild in child.Descendants()) 99 yield return grandChild; 100 } 101 } 102 103 //--- 特定の型の子要素を取得 104 public static IEnumerable<T> Children<T>(this DependencyObject obj) 105 where T : DependencyObject => obj.Children().OfType<T>(); 106 107 //--- 特定の型の子孫要素を取得 108 public static IEnumerable<T> Descendants<T>(this DependencyObject obj) 109 where T : DependencyObject => obj.Descendants().OfType<T>(); 110 } 111}
仮想化を切っても(回答コードで両方のチェックを外す)似たような動きになりましたが、これはちょっとイヤですかねぇ。
投稿2021/05/06 09:25
編集2023/07/26 16:25総合スコア10022
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。