回答編集履歴

2

見直しキャンペーン中

2023/08/14 10:10

投稿

TN8001
TN8001

スコア9350

test CHANGED
@@ -295,8 +295,8 @@
295
295
  }
296
296
  }
297
297
  ```
298
-
299
298
  「WinForms的発想」のほうを一時しのぎのつもりで書きましたが、かえって難しいしトータル行数も大差ない気がします^^;
299
+ ![アプリ画像](https://ddjkaamml8q8x.cloudfront.net/questions/2023-08-14/40d47835-a91b-44e4-b2da-a8ac5e0062f7.png)
300
300
 
301
301
  ---
302
302
 

1

見直しキャンペーン中

2023/07/27 16:16

投稿

TN8001
TN8001

スコア9350

test CHANGED
@@ -1,605 +1,303 @@
1
1
  > コード作成者がすでに不在で既存部分も理解できていないため1から学習を始めるのですが、本件はすぐに対応が必要なため一時的にしのぎたいです。
2
2
 
3
-
4
-
5
3
  本当に何もかもわからない状態だとすると、なかなか厳しいと思いますね。
6
-
7
4
  少なくともテスト用のミニマムなプロジェクトを作って、試されたほうがいいと思います(提示コードはなんだかさっぱりわかりません)
8
5
 
9
-
10
-
11
6
  `DataGrid`の仮想化を切っても使える程度の少量の件数であれば、WinForms的な発想もありえます。
12
-
13
7
  具体的には`CellEditEnding`で`cell.Background = Brushes.Yellow`のような(あくまでイメージです。実際はこれではダメです)
14
8
 
15
-
16
-
17
- ```xaml
9
+ ```xml
18
-
19
10
  <DataGrid
20
-
21
11
  EnableColumnVirtualization="False"
22
-
23
- EnableRowVirtualization="False"
12
+ EnableRowVirtualization="False">
24
-
25
13
  ```
26
-
27
14
  こうしてみてとてもじゃないが使いものにならない(読み込み速度やスクロール・カラムサイズ変更等)なら、このアプローチはすっぱり諦めたほうがいいでしょう(手元では1000件程度でもかなり厳しい)
28
15
 
29
-
30
-
31
16
  WPF的には98mateさんの調査通り、おそらく`DataTrigger`でやるのが真っ当なのでしょう。
32
17
 
33
-
34
-
35
18
  > →(疑問)DataTriggerっていつ反映される?
36
19
 
37
-
38
-
39
20
  やってみたらすぐわかるのでは?(WPFでは`INotifyPropertyChanged`は欠かせないので、必ず押さえておいてください)
40
21
 
41
-
42
-
43
22
  > →(疑問)これだと背景を変えるセル(座標)が不明?参考サイトだと元々バインドされたプロパティが変化したときの例。
44
23
 
45
-
46
-
47
24
  発想が逆です。セルごとに状態が必要ならば、セル個数分プロパティが必要です。
48
25
 
49
-
50
-
51
- ```xaml
26
+ ```xml
52
-
53
27
  <Window
54
-
55
28
  x:Class="Questions346520.MainWindow"
56
-
57
29
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
58
-
59
30
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
60
-
61
31
  Width="800"
62
-
63
32
  Height="450">
64
-
65
33
  <Window.Resources>
66
-
67
34
  <Style x:Key="dirtyStyle" BasedOn="{StaticResource {x:Type DataGridCell}}" TargetType="DataGridCell">
68
-
69
35
  <Setter Property="Background" Value="Yellow" />
70
-
71
36
  </Style>
72
-
73
37
  </Window.Resources>
74
-
75
38
  <Grid>
76
-
77
39
  <Grid.ColumnDefinitions>
78
-
79
40
  <ColumnDefinition />
80
-
81
41
  <ColumnDefinition />
82
-
83
42
  </Grid.ColumnDefinitions>
84
43
 
85
-
86
-
87
44
  <GroupBox Header="WinForms的発想">
88
-
89
45
  <DockPanel>
90
-
91
46
  <Button Click="Button1_Click" Content="更新" DockPanel.Dock="Top" />
92
-
93
47
  <DataGrid
94
-
95
48
  x:Name="dataGrid1"
96
-
97
49
  AutoGenerateColumns="False"
98
-
99
50
  BeginningEdit="DataGrid1_BeginningEdit"
100
-
101
51
  CellEditEnding="DataGrid1_CellEditEnding"
102
-
103
52
  EnableColumnVirtualization="False"
104
-
105
53
  EnableRowVirtualization="False"
106
-
107
54
  ItemsSource="{Binding Records1}"
108
-
109
55
  SelectionUnit="CellOrRowHeader">
110
-
111
56
  <DataGrid.Columns>
112
-
113
57
  <DataGridTextColumn Binding="{Binding No}" Header="No" IsReadOnly="True" />
114
-
115
58
  <DataGridTextColumn Binding="{Binding UshortValue}" Header="ushort" />
116
-
117
59
  <DataGridTextColumn Binding="{Binding StringValue}" Header="string" />
118
-
119
60
  </DataGrid.Columns>
120
-
121
61
  </DataGrid>
122
-
123
62
  </DockPanel>
124
-
125
63
  </GroupBox>
126
64
 
127
-
128
-
129
65
  <GroupBox Grid.Column="1" Header="DataTrigger">
130
-
131
66
  <DockPanel>
132
-
133
67
  <Button Click="Button2_Click" Content="更新" DockPanel.Dock="Top" />
134
-
135
68
  <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Records2}" SelectionUnit="CellOrRowHeader">
136
-
137
69
  <DataGrid.Columns>
138
-
139
70
  <DataGridTextColumn Binding="{Binding No}" Header="No" IsReadOnly="True" />
140
-
141
71
  <DataGridTextColumn Binding="{Binding UshortValue}" Header="ushort">
142
-
143
72
  <DataGridTextColumn.CellStyle>
144
-
145
73
  <Style TargetType="DataGridCell">
146
-
147
74
  <Style.Triggers>
148
-
149
75
  <DataTrigger Binding="{Binding IsDirtyUshortValue}" Value="True">
150
-
151
76
  <Setter Property="Background" Value="Yellow" />
152
-
153
77
  </DataTrigger>
154
-
155
78
  </Style.Triggers>
156
-
157
79
  </Style>
158
-
159
80
  </DataGridTextColumn.CellStyle>
160
-
161
81
  </DataGridTextColumn>
162
-
163
82
  <DataGridTextColumn Binding="{Binding StringValue}" Header="string">
164
-
165
83
  <DataGridTextColumn.CellStyle>
166
-
167
84
  <Style TargetType="DataGridCell">
168
-
169
85
  <Style.Triggers>
170
-
171
86
  <DataTrigger Binding="{Binding IsDirtyStringValue}" Value="True">
172
-
173
87
  <Setter Property="Background" Value="Yellow" />
174
-
175
88
  </DataTrigger>
176
-
177
89
  </Style.Triggers>
178
-
179
90
  </Style>
180
-
181
91
  </DataGridTextColumn.CellStyle>
182
-
183
92
  </DataGridTextColumn>
184
-
185
93
  </DataGrid.Columns>
186
-
187
94
  </DataGrid>
188
-
189
95
  </DockPanel>
190
-
191
96
  </GroupBox>
192
-
193
97
  </Grid>
194
-
195
98
  </Window>
196
-
197
99
  ```
198
100
 
199
-
200
-
201
- ```C#
101
+ ```cs
202
-
203
102
  using System;
204
-
205
103
  using System.Collections.Generic;
206
-
207
104
  using System.ComponentModel;
208
-
209
105
  using System.Linq;
210
-
211
106
  using System.Runtime.CompilerServices;
212
-
213
107
  using System.Windows;
214
-
215
108
  using System.Windows.Controls;
216
-
217
109
  using System.Windows.Media;
218
110
 
219
-
220
-
221
111
  namespace Questions346520
222
-
223
112
  {
224
-
225
113
  public partial class MainWindow : Window
226
-
227
114
  {
228
-
229
115
  public List<Record1> Records1 { get; } = new List<Record1>();
230
-
231
116
  public List<Record2> Records2 { get; } = new List<Record2>();
232
117
 
233
-
234
-
235
118
  private bool isDirty1;
236
-
237
119
  private string oldValue;
238
-
239
120
  private Style defaultStyle;
240
-
241
121
  private Style dirtyStyle;
242
122
 
243
-
244
-
245
123
  public MainWindow()
246
-
247
- {
124
+ {
248
-
249
125
  InitializeComponent();
250
-
251
126
  DataContext = this;
252
127
 
253
-
254
-
255
128
  defaultStyle = FindResource(typeof(DataGridCell)) as Style;
256
-
257
129
  dirtyStyle = FindResource("dirtyStyle") as Style;
258
130
 
259
-
260
-
261
131
  for (var i = 0; i < 1000; i++)
262
-
263
- {
132
+ {
264
-
265
133
  Records1.Add(new Record1()
266
-
267
134
  {
268
-
269
135
  No = i,
270
-
271
136
  UshortValue = (ushort)(i * 10),
272
-
273
137
  StringValue = (i * 100).ToString(),
274
-
275
138
  });
276
-
277
139
  // ↑↓片方ずつコメント化してパフォーマンスチェック
278
-
279
140
  Records2.Add(new Record2()
280
-
281
141
  {
282
-
283
142
  No = i,
284
-
285
143
  UshortValue = (ushort)(i * 10),
286
-
287
144
  StringValue = (i * 100).ToString(),
288
-
289
145
  IsDirtyUshortValue = false,
290
-
291
146
  IsDirtyStringValue = false,
292
-
293
147
  });
294
-
295
- }
148
+ }
296
-
297
- }
149
+ }
298
-
299
-
300
150
 
301
151
  private void DataGrid1_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
302
-
303
- {
152
+ {
304
-
305
153
  if (e.EditingEventArgs.Source is TextBlock textBlock)
306
-
307
154
  oldValue = textBlock.Text;
308
155
 
309
-
310
-
311
156
  if (e.EditingEventArgs.Source is DataGridCell cell)
312
-
313
157
  oldValue = (cell.Content as TextBlock).Text;
314
-
315
- }
158
+ }
316
-
317
-
318
159
 
319
160
  private void DataGrid1_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
320
-
321
- {
161
+ {
322
-
323
162
  if (e.EditAction != DataGridEditAction.Commit)
324
-
325
- {
163
+ {
326
-
327
164
  oldValue = null;
328
-
329
165
  return;
330
-
331
- }
166
+ }
332
-
333
-
334
167
 
335
168
  var textbox = e.EditingElement as TextBox;
336
-
337
169
  var newValue = textbox.Text;
338
-
339
170
  if (oldValue == newValue)
340
-
341
- {
171
+ {
342
-
343
172
  oldValue = null;
344
-
345
173
  return;
346
-
347
- }
174
+ }
348
-
349
-
350
175
 
351
176
  var cell = textbox.Parent as DataGridCell;
352
-
353
177
  cell.Style = dirtyStyle;
354
-
355
178
  isDirty1 = true;
356
-
357
- }
179
+ }
358
-
359
-
360
180
 
361
181
  private void Button1_Click(object sender, RoutedEventArgs e)
362
-
363
- {
182
+ {
364
-
365
183
  if (!isDirty1) return;
366
184
 
367
-
368
-
369
185
  foreach (var cell in dataGrid1.Descendants<DataGridCell>())
370
-
371
186
  cell.Style = defaultStyle;
372
187
 
373
-
374
-
375
188
  isDirty1 = false;
376
-
377
- }
189
+ }
378
-
379
-
380
190
 
381
191
  private void Button2_Click(object sender, RoutedEventArgs e)
382
-
383
- {
192
+ {
384
-
385
193
  if (!Record2.IsDirty2) return;
386
194
 
387
-
388
-
389
195
  foreach (var record2 in Records2)
390
-
391
- {
196
+ {
392
-
393
197
  record2.IsDirtyUshortValue = false;
394
-
395
198
  record2.IsDirtyStringValue = false;
396
-
397
- }
199
+ }
398
-
399
200
  Record2.IsDirty2 = false;
400
-
401
- }
201
+ }
402
-
403
-
404
-
405
202
 
406
203
 
407
204
  public class Record1
408
-
409
- {
205
+ {
410
-
411
206
  public int No { get; set; }
412
-
413
207
  public ushort UshortValue { get; set; }
414
-
415
208
  public string StringValue { get; set; }
416
-
417
- }
209
+ }
418
-
419
-
420
210
 
421
211
  public class Record2 : Observable
422
-
423
- {
212
+ {
424
-
425
213
  // ダサいがこうするしかないか?
426
-
427
214
  public static bool IsDirty2;
428
215
 
429
-
430
-
431
216
  public int No { get; set; }
432
217
 
433
-
434
-
435
218
  public ushort UshortValue
436
-
437
- {
219
+ {
438
-
439
220
  get => _UshortValue;
440
-
441
221
  set
442
-
443
222
  {
444
-
445
223
  if (_UshortValue != value)
446
-
447
224
  {
448
-
449
225
  _UshortValue = value;
450
-
451
226
  IsDirtyUshortValue = true;
452
-
453
227
  }
454
-
455
228
  }
456
-
457
- }
229
+ }
458
-
459
230
  private ushort _UshortValue;
460
231
 
461
-
462
-
463
232
  // ValueはPropertyChangedを発砲する必要はないが、↑は面倒なので使っちゃうw
464
-
465
233
  public string StringValue
466
-
467
- {
234
+ {
468
-
469
235
  get => _StringValue;
470
-
471
236
  set { if (SetProperty(ref _StringValue, value)) IsDirtyStringValue = true; }
472
-
473
- }
237
+ }
474
-
475
238
  private string _StringValue;
476
239
 
477
-
478
-
479
240
  // こちらはコードから変更されるため、PropertyChangedを発砲しないとViewに反映されない
480
-
481
241
  public bool IsDirtyUshortValue
482
-
483
- {
242
+ {
484
-
485
243
  get => _IsDirtyUshortValue;
486
-
487
244
  set { if (SetProperty(ref _IsDirtyUshortValue, value)) IsDirty2 = true; }
488
-
489
- }
245
+ }
490
-
491
246
  private bool _IsDirtyUshortValue;
492
247
 
493
-
494
-
495
248
  public bool IsDirtyStringValue
496
-
497
- {
249
+ {
498
-
499
250
  get => _IsDirtyStringValue;
500
-
501
251
  set { if (SetProperty(ref _IsDirtyStringValue, value)) IsDirty2 = true; }
502
-
503
- }
252
+ }
504
-
505
253
  private bool _IsDirtyStringValue;
506
-
507
- }
254
+ }
508
-
509
255
  }
510
256
 
511
-
512
-
513
257
  // [VisualTreeの子孫要素を取得する - xin9le.net](https://blog.xin9le.net/entry/2013/10/29/222336)
514
-
515
258
  static class DependencyObjectExtensions
516
-
517
259
  {
518
-
519
260
  public static IEnumerable<DependencyObject> Children(this DependencyObject obj)
520
-
521
- {
261
+ {
522
-
523
262
  if (obj == null) throw new ArgumentNullException(nameof(obj));
524
-
525
263
  var count = VisualTreeHelper.GetChildrenCount(obj);
526
-
527
264
  if (count == 0) yield break;
528
-
529
265
  for (var i = 0; i < count; i++)
530
-
531
- {
266
+ {
532
-
533
267
  var child = VisualTreeHelper.GetChild(obj, i);
534
-
535
268
  if (child != null) yield return child;
536
-
537
- }
269
+ }
538
-
539
- }
270
+ }
540
-
541
271
  public static IEnumerable<DependencyObject> Descendants(this DependencyObject obj)
542
-
543
- {
272
+ {
544
-
545
273
  if (obj == null) throw new ArgumentNullException(nameof(obj));
546
-
547
274
  foreach (var child in obj.Children())
548
-
549
- {
275
+ {
550
-
551
276
  yield return child;
552
-
553
277
  foreach (var grandChild in child.Descendants())
554
-
555
278
  yield return grandChild;
556
-
557
- }
279
+ }
558
-
559
- }
280
+ }
560
-
561
281
  public static IEnumerable<T> Descendants<T>(this DependencyObject obj) where T : DependencyObject => obj.Descendants().OfType<T>();
562
-
563
282
  }
564
283
 
565
-
566
-
567
284
  public class Observable : INotifyPropertyChanged
568
-
569
285
  {
570
-
571
286
  public event PropertyChangedEventHandler PropertyChanged;
572
-
573
287
  protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
574
-
575
- {
288
+ {
576
-
577
289
  if (Equals(storage, value)) return false;
578
-
579
290
  storage = value;
580
-
581
291
  OnPropertyChanged(propertyName);
582
-
583
292
  return true;
584
-
585
- }
293
+ }
586
-
587
294
  protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
588
-
589
295
  }
590
-
591
296
  }
592
-
593
297
  ```
594
298
 
595
-
596
-
597
299
  「WinForms的発想」のほうを一時しのぎのつもりで書きましたが、かえって難しいしトータル行数も大差ない気がします^^;
598
300
 
599
-
600
-
601
301
  ---
602
302
 
603
-
604
-
605
303
  行ごとの判定でよければ`DataTable`をソースにすれば、簡単にできそうな気はします(おそらく「それじゃダメ」と言われそうなので特に調べていませんが^^;