回答編集履歴

1

見直しキャンペーン中

2023/07/26 16:25

投稿

TN8001
TN8001

スコア9326

test CHANGED
@@ -1,393 +1,197 @@
1
1
  > 詳細データを追加するとスクロールが右位置のまま表示される
2
-
3
-
4
2
 
5
3
  うまく日本語で説明できませんが、`ListView`は`ItemsSource`をまたがってスクロール位置を保存しているということですよね?
6
4
 
7
-
8
-
9
5
  なぜかアイテムが空の時に限って、水平スクロールバーも出ないし左端に寄って表示する。
10
-
11
6
  そこでアイテムが追加されると、保存された位置にスクロールされる。
12
-
13
7
  これ自体はちょっと変ですが標準の動作ですね。
14
-
15
-
16
8
 
17
9
  > C#側で対象データが変わる際に通るようScroll ToHomeは実装しています。
18
10
 
19
-
20
-
21
11
  これのことでしょうか?
22
-
23
12
  [ScrollViewer.ScrollToHome Method (System.Windows.Controls) | Microsoft Docs](https://docs.microsoft.com/en-us/dotnet/api/system.windows.controls.scrollviewer.scrolltohome)
24
-
25
-
26
13
 
27
14
  このちょっと変な動作をキャンセルするために、選択が変わるたびに`ScrollToHome`しているが「アイテムが空の時だけは効かない」と。
28
15
 
29
-
30
-
31
16
  私もよくわかりませんが、これに該当するということなんでしょう。
32
-
33
17
  > This method does not induce any scrolling behavior if ScrollInfo is null.
34
18
 
35
-
36
-
37
19
  単純にやるならアイテムの追加時にも`ScrollToHome`したらどうですか?
38
-
39
20
  (確かに「位置を保存しないオプション」があってもいいですね)
40
-
41
-
42
21
 
43
22
  ---
44
23
 
45
-
46
-
47
24
  今の理解での検証コード(全然違うという場合はもっと詳しく説明してください)
48
-
49
- ```xaml
25
+ ```xml
50
-
51
26
  <Window
52
-
53
27
  x:Class="Questions336796.MainWindow"
54
-
55
28
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
56
-
57
29
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
58
-
59
30
  Width="800"
60
-
61
31
  Height="450">
62
-
63
32
  <Grid>
64
-
65
33
  <Grid.ColumnDefinitions>
66
-
67
34
  <ColumnDefinition />
68
-
69
35
  <ColumnDefinition Width="Auto" />
70
-
71
36
  <ColumnDefinition />
72
-
73
37
  </Grid.ColumnDefinitions>
74
-
75
38
  <ListBox
76
-
77
39
  x:Name="listBox"
78
-
79
40
  DisplayMemberPath="Name"
80
-
81
41
  ItemsSource="{Binding Items}"
82
-
83
42
  SelectionChanged="ListBox_SelectionChanged" />
84
-
85
43
  <StackPanel Grid.Column="1">
86
-
87
44
  <Button Click="AddButton_Click" Content="詳細追加" />
88
-
89
45
  <Button Click="ClearButton_Click" Content="詳細全削除" />
90
-
91
46
  <CheckBox
92
-
93
47
  x:Name="checkBox1"
94
-
95
48
  Content="ScrollToHome"
96
-
97
49
  IsChecked="True" />
98
-
99
50
  <CheckBox
100
-
101
51
  x:Name="checkBox2"
102
-
103
52
  Content="Virtualizing"
104
-
105
53
  IsChecked="True" />
106
-
107
54
  </StackPanel>
108
-
109
55
  <ListView
110
-
111
56
  x:Name="listView"
112
-
113
57
  Grid.Column="2"
114
-
115
58
  ItemsSource="{Binding SelectedItem.Details, ElementName=listBox}"
116
-
117
59
  VirtualizingPanel.IsVirtualizing="{Binding IsChecked, ElementName=checkBox2}">
118
-
119
60
  <ListView.View>
120
-
121
61
  <GridView>
122
-
123
62
  <GridViewColumn
124
-
125
63
  Width="200"
126
-
127
64
  DisplayMemberBinding="{Binding Detail1}"
128
-
129
65
  Header="詳細1" />
130
-
131
66
  <GridViewColumn
132
-
133
67
  Width="200"
134
-
135
68
  DisplayMemberBinding="{Binding Detail2}"
136
-
137
69
  Header="詳細2" />
138
-
139
70
  <GridViewColumn
140
-
141
71
  Width="200"
142
-
143
72
  DisplayMemberBinding="{Binding Detail3}"
144
-
145
73
  Header="詳細3" />
146
-
147
74
  </GridView>
148
-
149
75
  </ListView.View>
150
-
151
76
  </ListView>
152
-
153
77
  </Grid>
154
-
155
78
  </Window>
156
-
157
79
  ```
158
80
 
159
-
160
-
161
- ```C#
81
+ ```cs
162
-
163
82
  using System;
164
-
165
83
  using System.Collections.Generic;
166
-
167
84
  using System.Collections.ObjectModel;
168
-
169
85
  using System.Linq;
170
-
171
86
  using System.Windows;
172
-
173
87
  using System.Windows.Controls;
174
-
175
88
  using System.Windows.Media;
176
89
 
177
-
178
-
179
90
  namespace Questions336796
180
-
181
91
  {
182
-
183
92
  public partial class MainWindow : Window
184
-
185
93
  {
186
-
187
94
  public ObservableCollection<Item> Items { get; }
188
95
 
189
-
190
-
191
96
  public MainWindow()
192
-
193
97
  {
194
-
195
98
  InitializeComponent();
196
99
 
197
-
198
-
199
100
  Items = new ObservableCollection<Item>
200
-
201
101
  {
202
-
203
102
  new Item { Name = "Item1", },
204
-
205
103
  new Item { Name = "Item2", },
206
-
207
104
  new Item { Name = "Item3", },
208
-
209
105
  };
210
-
211
106
  Items[0].Details.Add(new Detail());
212
-
213
107
  Items[2].Details.Add(new Detail());
214
108
 
215
-
216
-
217
109
  DataContext = this;
218
-
219
110
  }
220
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
+ }
221
120
 
222
-
223
- private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
121
+ private void AddButton_Click(object sender, RoutedEventArgs e)
224
-
225
122
  {
123
+ if (listBox.SelectedItem is Item item)
124
+ item.Details.Add(new Detail());
226
125
 
227
126
  if (checkBox1.IsChecked == true)
228
-
229
127
  {
230
-
231
128
  var sv = listView.Descendants<ScrollViewer>().First();
232
-
233
129
  sv.ScrollToHome();
234
-
235
130
  }
236
-
237
131
  }
238
132
 
133
+ private void ClearButton_Click(object sender, RoutedEventArgs e)
134
+ {
135
+ if (listBox.SelectedItem is Item item)
136
+ item.Details.Clear();
137
+ }
138
+ }
239
139
 
140
+ public class Item
141
+ {
142
+ public string Name { get; set; }
143
+ public ObservableCollection<Detail> Details { get; } = new ObservableCollection<Detail>();
144
+ }
240
145
 
146
+ public class Detail
147
+ {
241
- private void AddButton_Click(object sender, RoutedEventArgs e)
148
+ public string Detail1 { get; set; } = "Detail1";
149
+ public string Detail2 { get; set; } = "Detail2";
150
+ public string Detail3 { get; set; } = "Detail3";
151
+ }
242
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)
243
158
  {
159
+ if (obj == null) throw new ArgumentNullException(nameof(obj));
244
160
 
161
+ var count = VisualTreeHelper.GetChildrenCount(obj);
245
- if (listBox.SelectedItem is Item item)
162
+ if (count == 0) yield break;
246
163
 
247
- item.Details.Add(new Detail());
248
-
249
-
250
-
251
- if (checkBox1.IsChecked == true)
164
+ for (var i = 0; i < count; i++)
252
-
253
165
  {
254
-
255
- var sv = listView.Descendants<ScrollViewer>().First();
166
+ var child = VisualTreeHelper.GetChild(obj, i);
256
-
257
- sv.ScrollToHome();
167
+ if (child != null) yield return child;
258
-
259
168
  }
260
-
261
169
  }
262
170
 
171
+ //--- 子孫要素を取得
172
+ public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
173
+ {
174
+ if (obj == null) throw new ArgumentNullException(nameof(obj));
263
175
 
264
-
265
- private void ClearButton_Click(object sender, RoutedEventArgs e)
176
+ foreach (var child in obj.Children())
266
-
267
- {
177
+ {
268
-
178
+ yield return child;
269
- if (listBox.SelectedItem is Item item)
179
+ foreach (var grandChild in child.Descendants())
270
-
271
- item.Details.Clear();
180
+ yield return grandChild;
272
-
181
+ }
273
182
  }
274
183
 
275
- }
276
-
277
-
278
-
279
- public class Item
280
-
281
- {
282
-
283
- public string Name { get; set; }
284
-
285
- public ObservableCollection<Detail> Details { get; } = new ObservableCollection<Detail>();
286
-
287
- }
288
-
289
-
290
-
291
- public class Detail
292
-
293
- {
294
-
295
- public string Detail1 { get; set; } = "Detail1";
296
-
297
- public string Detail2 { get; set; } = "Detail2";
298
-
299
- public string Detail3 { get; set; } = "Detail3";
300
-
301
- }
302
-
303
-
304
-
305
- // [VisualTreeの子孫要素を取得する - xin9le.net](https://blog.xin9le.net/entry/2013/10/29/222336)
306
-
307
- public static class DependencyObjectExtensions
308
-
309
- {
310
-
311
- //--- 子要素を取得
312
-
313
- public static IEnumerable<DependencyObject> Children(this DependencyObject obj)
314
-
315
- {
316
-
317
- if (obj == null) throw new ArgumentNullException(nameof(obj));
318
-
319
-
320
-
321
- var count = VisualTreeHelper.GetChildrenCount(obj);
322
-
323
- if (count == 0) yield break;
324
-
325
-
326
-
327
- for (var i = 0; i < count; i++)
328
-
329
- {
330
-
331
- var child = VisualTreeHelper.GetChild(obj, i);
332
-
333
- if (child != null) yield return child;
334
-
335
- }
336
-
337
- }
338
-
339
-
340
-
341
- //--- 子孫要素を取得
342
-
343
- public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
344
-
345
- {
346
-
347
- if (obj == null) throw new ArgumentNullException(nameof(obj));
348
-
349
-
350
-
351
- foreach (var child in obj.Children())
352
-
353
- {
354
-
355
- yield return child;
356
-
357
- foreach (var grandChild in child.Descendants())
358
-
359
- yield return grandChild;
360
-
361
- }
362
-
363
- }
364
-
365
-
366
-
367
184
  //--- 特定の型の子要素を取得
368
-
369
185
  public static IEnumerable<T> Children<T>(this DependencyObject obj)
370
-
371
186
  where T : DependencyObject => obj.Children().OfType<T>();
372
187
 
373
-
374
-
375
188
  //--- 特定の型の子孫要素を取得
376
-
377
189
  public static IEnumerable<T> Descendants<T>(this DependencyObject obj)
378
-
379
190
  where T : DependencyObject => obj.Descendants().OfType<T>();
380
-
381
191
  }
382
-
383
192
  }
384
-
385
193
  ```
386
-
387
-
388
194
 
389
195
  ---
390
196
 
391
-
392
-
393
197
  仮想化を切っても(回答コードで両方のチェックを外す)似たような動きになりましたが、これはちょっとイヤですかねぇ。