teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

見直しキャンペーン中

2023/07/26 16:25

投稿

TN8001
TN8001

スコア10111

answer CHANGED
@@ -1,197 +1,197 @@
1
- > 詳細データを追加するとスクロールが右位置のまま表示される
2
-
3
- うまく日本語で説明できませんが、`ListView`は`ItemsSource`をまたがってスクロール位置を保存しているということですよね?
4
-
5
- なぜかアイテムが空の時に限って、水平スクロールバーも出ないし左端に寄って表示する。
6
- そこでアイテムが追加されると、保存された位置にスクロールされる。
7
- これ自体はちょっと変ですが標準の動作ですね。
8
-
9
- > C#側で対象データが変わる際に通るようScroll ToHomeは実装しています。
10
-
11
- これのことでしょうか?
12
- [ScrollViewer.ScrollToHome Method (System.Windows.Controls) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.scrollviewer.scrolltohome)
13
-
14
- このちょっと変な動作をキャンセルするために、選択が変わるたびに`ScrollToHome`しているが「アイテムが空の時だけは効かない」と。
15
-
16
- 私もよくわかりませんが、これに該当するということなんでしょう。
17
- > This method does not induce any scrolling behavior if ScrollInfo is null.
18
-
19
- 単純にやるならアイテムの追加時にも`ScrollToHome`したらどうですか?
20
- (確かに「位置を保存しないオプション」があってもいいですね)
21
-
22
- ---
23
-
24
- 今の理解での検証コード(全然違うという場合はもっと詳しく説明してください)
25
- ```xaml
26
- <Window
27
- x:Class="Questions336796.MainWindow"
28
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
29
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
30
- Width="800"
31
- Height="450">
32
- <Grid>
33
- <Grid.ColumnDefinitions>
34
- <ColumnDefinition />
35
- <ColumnDefinition Width="Auto" />
36
- <ColumnDefinition />
37
- </Grid.ColumnDefinitions>
38
- <ListBox
39
- x:Name="listBox"
40
- DisplayMemberPath="Name"
41
- ItemsSource="{Binding Items}"
42
- SelectionChanged="ListBox_SelectionChanged" />
43
- <StackPanel Grid.Column="1">
44
- <Button Click="AddButton_Click" Content="詳細追加" />
45
- <Button Click="ClearButton_Click" Content="詳細全削除" />
46
- <CheckBox
47
- x:Name="checkBox1"
48
- Content="ScrollToHome"
49
- IsChecked="True" />
50
- <CheckBox
51
- x:Name="checkBox2"
52
- Content="Virtualizing"
53
- IsChecked="True" />
54
- </StackPanel>
55
- <ListView
56
- x:Name="listView"
57
- Grid.Column="2"
58
- ItemsSource="{Binding SelectedItem.Details, ElementName=listBox}"
59
- VirtualizingPanel.IsVirtualizing="{Binding IsChecked, ElementName=checkBox2}">
60
- <ListView.View>
61
- <GridView>
62
- <GridViewColumn
63
- Width="200"
64
- DisplayMemberBinding="{Binding Detail1}"
65
- Header="詳細1" />
66
- <GridViewColumn
67
- Width="200"
68
- DisplayMemberBinding="{Binding Detail2}"
69
- Header="詳細2" />
70
- <GridViewColumn
71
- Width="200"
72
- DisplayMemberBinding="{Binding Detail3}"
73
- Header="詳細3" />
74
- </GridView>
75
- </ListView.View>
76
- </ListView>
77
- </Grid>
78
- </Window>
79
- ```
80
-
81
- ```C#
82
- using System;
83
- using System.Collections.Generic;
84
- using System.Collections.ObjectModel;
85
- using System.Linq;
86
- using System.Windows;
87
- using System.Windows.Controls;
88
- using System.Windows.Media;
89
-
90
- namespace Questions336796
91
- {
92
- public partial class MainWindow : Window
93
- {
94
- public ObservableCollection<Item> Items { get; }
95
-
96
- public MainWindow()
97
- {
98
- InitializeComponent();
99
-
100
- Items = new ObservableCollection<Item>
101
- {
102
- new Item { Name = "Item1", },
103
- new Item { Name = "Item2", },
104
- new Item { Name = "Item3", },
105
- };
106
- Items[0].Details.Add(new Detail());
107
- Items[2].Details.Add(new Detail());
108
-
109
- DataContext = this;
110
- }
111
-
112
- private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
113
- {
114
- if (checkBox1.IsChecked == true)
115
- {
116
- var sv = listView.Descendants<ScrollViewer>().First();
117
- sv.ScrollToHome();
118
- }
119
- }
120
-
121
- private void AddButton_Click(object sender, RoutedEventArgs e)
122
- {
123
- if (listBox.SelectedItem is Item item)
124
- item.Details.Add(new Detail());
125
-
126
- if (checkBox1.IsChecked == true)
127
- {
128
- var sv = listView.Descendants<ScrollViewer>().First();
129
- sv.ScrollToHome();
130
- }
131
- }
132
-
133
- private void ClearButton_Click(object sender, RoutedEventArgs e)
134
- {
135
- if (listBox.SelectedItem is Item item)
136
- item.Details.Clear();
137
- }
138
- }
139
-
140
- public class Item
141
- {
142
- public string Name { get; set; }
143
- public ObservableCollection<Detail> Details { get; } = new ObservableCollection<Detail>();
144
- }
145
-
146
- public class Detail
147
- {
148
- public string Detail1 { get; set; } = "Detail1";
149
- public string Detail2 { get; set; } = "Detail2";
150
- public string Detail3 { get; set; } = "Detail3";
151
- }
152
-
153
- // [VisualTreeの子孫要素を取得する - xin9le.net](https://blog.xin9le.net/entry/2013/10/29/222336)
154
- public static class DependencyObjectExtensions
155
- {
156
- //--- 子要素を取得
157
- public static IEnumerable<DependencyObject> Children(this DependencyObject obj)
158
- {
159
- if (obj == null) throw new ArgumentNullException(nameof(obj));
160
-
161
- var count = VisualTreeHelper.GetChildrenCount(obj);
162
- if (count == 0) yield break;
163
-
164
- for (var i = 0; i < count; i++)
165
- {
166
- var child = VisualTreeHelper.GetChild(obj, i);
167
- if (child != null) yield return child;
168
- }
169
- }
170
-
171
- //--- 子孫要素を取得
172
- public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
173
- {
174
- if (obj == null) throw new ArgumentNullException(nameof(obj));
175
-
176
- foreach (var child in obj.Children())
177
- {
178
- yield return child;
179
- foreach (var grandChild in child.Descendants())
180
- yield return grandChild;
181
- }
182
- }
183
-
184
- //--- 特定の型の子要素を取得
185
- public static IEnumerable<T> Children<T>(this DependencyObject obj)
186
- where T : DependencyObject => obj.Children().OfType<T>();
187
-
188
- //--- 特定の型の子孫要素を取得
189
- public static IEnumerable<T> Descendants<T>(this DependencyObject obj)
190
- where T : DependencyObject => obj.Descendants().OfType<T>();
191
- }
192
- }
193
- ```
194
-
195
- ---
196
-
1
+ > 詳細データを追加するとスクロールが右位置のまま表示される
2
+
3
+ うまく日本語で説明できませんが、`ListView`は`ItemsSource`をまたがってスクロール位置を保存しているということですよね?
4
+
5
+ なぜかアイテムが空の時に限って、水平スクロールバーも出ないし左端に寄って表示する。
6
+ そこでアイテムが追加されると、保存された位置にスクロールされる。
7
+ これ自体はちょっと変ですが標準の動作ですね。
8
+
9
+ > C#側で対象データが変わる際に通るようScroll ToHomeは実装しています。
10
+
11
+ これのことでしょうか?
12
+ [ScrollViewer.ScrollToHome Method (System.Windows.Controls) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.scrollviewer.scrolltohome)
13
+
14
+ このちょっと変な動作をキャンセルするために、選択が変わるたびに`ScrollToHome`しているが「アイテムが空の時だけは効かない」と。
15
+
16
+ 私もよくわかりませんが、これに該当するということなんでしょう。
17
+ > This method does not induce any scrolling behavior if ScrollInfo is null.
18
+
19
+ 単純にやるならアイテムの追加時にも`ScrollToHome`したらどうですか?
20
+ (確かに「位置を保存しないオプション」があってもいいですね)
21
+
22
+ ---
23
+
24
+ 今の理解での検証コード(全然違うという場合はもっと詳しく説明してください)
25
+ ```xml
26
+ <Window
27
+ x:Class="Questions336796.MainWindow"
28
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
29
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
30
+ Width="800"
31
+ Height="450">
32
+ <Grid>
33
+ <Grid.ColumnDefinitions>
34
+ <ColumnDefinition />
35
+ <ColumnDefinition Width="Auto" />
36
+ <ColumnDefinition />
37
+ </Grid.ColumnDefinitions>
38
+ <ListBox
39
+ x:Name="listBox"
40
+ DisplayMemberPath="Name"
41
+ ItemsSource="{Binding Items}"
42
+ SelectionChanged="ListBox_SelectionChanged" />
43
+ <StackPanel Grid.Column="1">
44
+ <Button Click="AddButton_Click" Content="詳細追加" />
45
+ <Button Click="ClearButton_Click" Content="詳細全削除" />
46
+ <CheckBox
47
+ x:Name="checkBox1"
48
+ Content="ScrollToHome"
49
+ IsChecked="True" />
50
+ <CheckBox
51
+ x:Name="checkBox2"
52
+ Content="Virtualizing"
53
+ IsChecked="True" />
54
+ </StackPanel>
55
+ <ListView
56
+ x:Name="listView"
57
+ Grid.Column="2"
58
+ ItemsSource="{Binding SelectedItem.Details, ElementName=listBox}"
59
+ VirtualizingPanel.IsVirtualizing="{Binding IsChecked, ElementName=checkBox2}">
60
+ <ListView.View>
61
+ <GridView>
62
+ <GridViewColumn
63
+ Width="200"
64
+ DisplayMemberBinding="{Binding Detail1}"
65
+ Header="詳細1" />
66
+ <GridViewColumn
67
+ Width="200"
68
+ DisplayMemberBinding="{Binding Detail2}"
69
+ Header="詳細2" />
70
+ <GridViewColumn
71
+ Width="200"
72
+ DisplayMemberBinding="{Binding Detail3}"
73
+ Header="詳細3" />
74
+ </GridView>
75
+ </ListView.View>
76
+ </ListView>
77
+ </Grid>
78
+ </Window>
79
+ ```
80
+
81
+ ```cs
82
+ using System;
83
+ using System.Collections.Generic;
84
+ using System.Collections.ObjectModel;
85
+ using System.Linq;
86
+ using System.Windows;
87
+ using System.Windows.Controls;
88
+ using System.Windows.Media;
89
+
90
+ namespace Questions336796
91
+ {
92
+ public partial class MainWindow : Window
93
+ {
94
+ public ObservableCollection<Item> Items { get; }
95
+
96
+ public MainWindow()
97
+ {
98
+ InitializeComponent();
99
+
100
+ Items = new ObservableCollection<Item>
101
+ {
102
+ new Item { Name = "Item1", },
103
+ new Item { Name = "Item2", },
104
+ new Item { Name = "Item3", },
105
+ };
106
+ Items[0].Details.Add(new Detail());
107
+ Items[2].Details.Add(new Detail());
108
+
109
+ DataContext = this;
110
+ }
111
+
112
+ private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
113
+ {
114
+ if (checkBox1.IsChecked == true)
115
+ {
116
+ var sv = listView.Descendants<ScrollViewer>().First();
117
+ sv.ScrollToHome();
118
+ }
119
+ }
120
+
121
+ private void AddButton_Click(object sender, RoutedEventArgs e)
122
+ {
123
+ if (listBox.SelectedItem is Item item)
124
+ item.Details.Add(new Detail());
125
+
126
+ if (checkBox1.IsChecked == true)
127
+ {
128
+ var sv = listView.Descendants<ScrollViewer>().First();
129
+ sv.ScrollToHome();
130
+ }
131
+ }
132
+
133
+ private void ClearButton_Click(object sender, RoutedEventArgs e)
134
+ {
135
+ if (listBox.SelectedItem is Item item)
136
+ item.Details.Clear();
137
+ }
138
+ }
139
+
140
+ public class Item
141
+ {
142
+ public string Name { get; set; }
143
+ public ObservableCollection<Detail> Details { get; } = new ObservableCollection<Detail>();
144
+ }
145
+
146
+ public class Detail
147
+ {
148
+ public string Detail1 { get; set; } = "Detail1";
149
+ public string Detail2 { get; set; } = "Detail2";
150
+ public string Detail3 { get; set; } = "Detail3";
151
+ }
152
+
153
+ // [VisualTreeの子孫要素を取得する - xin9le.net](https://blog.xin9le.net/entry/2013/10/29/222336)
154
+ public static class DependencyObjectExtensions
155
+ {
156
+ //--- 子要素を取得
157
+ public static IEnumerable<DependencyObject> Children(this DependencyObject obj)
158
+ {
159
+ if (obj == null) throw new ArgumentNullException(nameof(obj));
160
+
161
+ var count = VisualTreeHelper.GetChildrenCount(obj);
162
+ if (count == 0) yield break;
163
+
164
+ for (var i = 0; i < count; i++)
165
+ {
166
+ var child = VisualTreeHelper.GetChild(obj, i);
167
+ if (child != null) yield return child;
168
+ }
169
+ }
170
+
171
+ //--- 子孫要素を取得
172
+ public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
173
+ {
174
+ if (obj == null) throw new ArgumentNullException(nameof(obj));
175
+
176
+ foreach (var child in obj.Children())
177
+ {
178
+ yield return child;
179
+ foreach (var grandChild in child.Descendants())
180
+ yield return grandChild;
181
+ }
182
+ }
183
+
184
+ //--- 特定の型の子要素を取得
185
+ public static IEnumerable<T> Children<T>(this DependencyObject obj)
186
+ where T : DependencyObject => obj.Children().OfType<T>();
187
+
188
+ //--- 特定の型の子孫要素を取得
189
+ public static IEnumerable<T> Descendants<T>(this DependencyObject obj)
190
+ where T : DependencyObject => obj.Descendants().OfType<T>();
191
+ }
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
197
  仮想化を切っても(回答コードで両方のチェックを外す)似たような動きになりましたが、これはちょっとイヤですかねぇ。