回答編集履歴

1

見直しキャンペーン中

2023/07/26 16:05

投稿

TN8001
TN8001

スコア9862

test CHANGED
@@ -1,541 +1,271 @@
1
- `WPF``MVVM`するには、`WinForms`的な考え方は捨ててください。
1
+ WPFでMVVMするには、WinForms的な考え方は捨ててください。
2
-
3
- `View`のコントロールにどう入れるかではなく、表示したいデータ(`Model``ViewModel`)をバインドし、結果的にテンプレートが当たって表示されます。
2
+ コントロールにどう入れるかではなく、表示したいデータ(Model≒ViewModel)をバインドし、結果的にテンプレートが当たって表示されます。
4
-
5
-
6
3
 
7
4
  > カレンダーの日付部分にあたる箇所
8
5
 
9
-
10
-
11
6
  カレンダーを作っているわけですね。
12
-
13
7
  日付は1~31あってそれを表示したいのであれば、その数字のコレクション(例えば`List<int>`)を作ってバインドします。
14
-
15
8
  コレクションですから、`ItemsControl`や`ListBox`にバインドすることになります。
16
9
 
17
-
18
-
19
10
  `Grid`にこだわりがあるなら↓こういった手法があります。
20
-
21
11
  [Gridへのアイテムのバインド(WPF編) | 泥庭](https://yone64.wordpress.com/2013/05/29/grid%E3%81%B8%E3%81%AE%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%81%AE%E3%83%90%E3%82%A4%E3%83%B3%E3%83%89%EF%BC%88wpf%E7%B7%A8%EF%BC%89/)
22
12
 
23
-
24
-
25
13
  しかしカレンダーは`UniformGrid`のほうが向いていますので、回答はそちらを採用しました。
26
14
 
27
-
28
-
29
15
  [ItemsControl 攻略 ~ 外観のカスタマイズ | grabacr.nét](http://grabacr.net/archives/1240)
30
-
31
16
  こちらをじっくり読んで、各テンプレート等がどういう役割か把握しておいてください(ブックマーク推奨)
32
17
 
33
-
34
-
35
- ```xaml
18
+ ```xml
36
-
37
19
  <Window
38
-
39
20
  x:Class="Questions333874.Views.MainWindow"
40
-
41
21
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
42
-
43
22
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
44
-
45
23
  xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
46
-
47
24
  xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
48
-
49
25
  xmlns:m="clr-namespace:Questions333874.Models"
50
-
51
26
  xmlns:vm="clr-namespace:Questions333874.ViewModels"
52
-
53
27
  Width="650" Height="550">
54
28
 
55
-
56
-
57
29
  <Window.Resources>
58
-
59
30
  <DataTemplate DataType="{x:Type m:Schedule}">
60
-
61
31
  <TextBlock Text="{Binding Text}" />
62
-
63
32
  </DataTemplate>
64
-
65
33
  </Window.Resources>
66
34
 
67
-
68
-
69
35
  <Window.DataContext>
70
-
71
36
  <vm:MainWindowViewModel />
72
-
73
37
  </Window.DataContext>
74
38
 
75
-
76
-
77
39
  <behaviors:Interaction.Triggers>
78
-
79
40
  <behaviors:EventTrigger EventName="ContentRendered">
80
-
81
41
  <l:LivetCallMethodAction MethodName="Initialize" MethodTarget="{Binding}" />
82
-
83
42
  </behaviors:EventTrigger>
84
-
85
43
  </behaviors:Interaction.Triggers>
86
44
 
87
-
88
-
89
45
  <DockPanel>
90
-
91
46
  <DockPanel Margin="10" DockPanel.Dock="Bottom">
92
-
93
47
  <Button Command="{Binding AddCommand}" Content="Add" DockPanel.Dock="Right" IsDefault="True" />
94
-
95
48
  <TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
96
-
97
49
  </DockPanel>
98
50
 
99
-
100
-
101
51
  <GroupBox Margin="10" Padding="10">
102
-
103
52
  <GroupBox.Header>
104
-
105
53
  <StackPanel Orientation="Horizontal">
106
-
107
54
  <TextBlock VerticalAlignment="Center" Text="{Binding HeaderMonth}" />
108
-
109
55
  <Button Command="{Binding PreviousCommand}" Content="<" />
110
-
111
56
  <Button Command="{Binding NextCommand}" Content=">" />
112
-
113
57
  </StackPanel>
114
-
115
58
  </GroupBox.Header>
116
59
 
117
-
118
-
119
60
  <ListBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" ItemsSource="{Binding Items}" SelectedItem="{Binding Selected}">
120
-
121
61
  <ListBox.Template>
122
-
123
62
  <ControlTemplate TargetType="ListBox">
124
-
125
63
  <Border BorderBrush="Black" BorderThickness="1">
126
-
127
64
  <DockPanel>
128
-
129
65
  <Border BorderBrush="Black" BorderThickness="0,0,0,1" DockPanel.Dock="Top">
130
-
131
66
  <UniformGrid Columns="7">
132
-
133
67
  <UniformGrid.Resources>
134
-
135
68
  <Style TargetType="Label">
136
-
137
69
  <Setter Property="Margin" Value="1,0,0,0" />
138
-
139
70
  <Setter Property="Padding" Value="0" />
140
-
141
71
  <Setter Property="HorizontalContentAlignment" Value="Center" />
142
-
143
72
  <Setter Property="Background" Value="#B0E0E6" />
144
-
145
73
  </Style>
146
-
147
74
  </UniformGrid.Resources>
148
-
149
75
  <Label Margin="0" Content="日" Foreground="Red" />
150
-
151
76
  <Label Content="月" />
152
-
153
77
  <Label Content="火" />
154
-
155
78
  <Label Content="水" />
156
-
157
79
  <Label Content="木" />
158
-
159
80
  <Label Content="金" />
160
-
161
81
  <Label Content="土" Foreground="Blue" />
162
-
163
82
  </UniformGrid>
164
-
165
83
  </Border>
166
-
167
84
  <ItemsPresenter />
168
-
169
85
  </DockPanel>
170
-
171
86
  </Border>
172
-
173
87
  </ControlTemplate>
174
-
175
88
  </ListBox.Template>
176
89
 
177
-
178
-
179
90
  <ListBox.ItemsPanel>
180
-
181
91
  <ItemsPanelTemplate>
182
-
183
92
  <UniformGrid Columns="7" FirstColumn="{Binding FirstDayOffset}" />
184
-
185
93
  </ItemsPanelTemplate>
186
-
187
94
  </ListBox.ItemsPanel>
188
95
 
189
-
190
-
191
96
  <ListBox.ItemTemplate>
192
-
193
97
  <DataTemplate>
194
-
195
98
  <DockPanel>
196
-
197
99
  <TextBlock HorizontalAlignment="Center" DockPanel.Dock="Top" Foreground="{Binding Color}" Text="{Binding Day}" />
198
-
199
100
  <ItemsControl ItemsSource="{Binding Schedules}" />
200
-
201
101
  </DockPanel>
202
-
203
102
  </DataTemplate>
204
-
205
103
  </ListBox.ItemTemplate>
206
-
207
104
  </ListBox>
208
-
209
105
  </GroupBox>
210
-
211
106
  </DockPanel>
212
-
213
107
  </Window>
214
-
215
108
  ```
216
109
 
217
-
218
-
219
- ```C#
110
+ ```cs
220
-
221
111
  using Livet;
222
-
223
112
  using Livet.Commands;
224
-
225
113
  using Questions333874.Models;
226
-
227
114
  using System;
228
-
229
115
  using System.Collections.Generic;
230
-
231
116
  using System.Collections.ObjectModel;
232
-
233
117
  using System.Linq;
234
-
235
118
  using System.Windows.Media;
236
119
 
237
-
238
-
239
120
  namespace Questions333874.ViewModels
240
-
241
121
  {
242
-
243
122
  public class DayViewModel //: ViewModel
244
-
245
123
  {
246
-
247
124
  public DateTime DateTime { get; set; }
248
-
249
125
  public int Day => DateTime.Day;
250
-
251
126
  public Brush Color => DateTime.DayOfWeek == DayOfWeek.Sunday ? Brushes.Red
252
-
253
127
  : DateTime.DayOfWeek == DayOfWeek.Saturday ? Brushes.Blue
254
-
255
128
  : Brushes.Black;
256
-
257
129
  public ObservableCollection<Schedule> Schedules { get; } = new ObservableCollection<Schedule>();
258
-
259
130
  }
260
131
 
261
-
262
-
263
132
  public class MainWindowViewModel : ViewModel
264
-
265
133
  {
266
-
267
134
  public string HeaderMonth => current.ToString("yyyy/MM");
268
135
 
269
-
270
-
271
136
  private int firstDayOffset;
272
-
273
137
  public int FirstDayOffset { get => firstDayOffset; set => RaisePropertyChangedIfSet(ref firstDayOffset, value); }
274
138
 
275
-
276
-
277
139
  private DayViewModel selected;
278
-
279
140
  public DayViewModel Selected
280
-
281
- {
141
+ {
282
-
283
142
  get => selected;
284
-
285
143
  set
286
-
287
- {
144
+ {
288
-
289
145
  if (RaisePropertyChangedIfSet(ref selected, value) && selected != null)
290
-
291
146
  current = selected.DateTime.Date;
292
-
293
147
  }
294
-
295
- }
148
+ }
296
-
297
-
298
149
 
299
150
  private List<DayViewModel> items;
300
-
301
151
  public List<DayViewModel> Items { get => items; set => RaisePropertyChangedIfSet(ref items, value); }
302
152
 
303
-
304
-
305
153
  private string text;
306
-
307
154
  public string Text
308
-
309
- {
155
+ {
310
-
311
156
  get => text;
312
-
313
157
  set
314
-
315
- {
158
+ {
316
-
317
159
  if (RaisePropertyChangedIfSet(ref text, value))
318
-
319
160
  AddCommand.RaiseCanExecuteChanged();
320
-
321
161
  }
322
-
323
- }
162
+ }
324
-
325
-
326
-
327
163
 
328
164
 
329
165
  private ViewModelCommand previousCommand;
330
-
331
166
  public ViewModelCommand PreviousCommand => previousCommand ??= new ViewModelCommand(Previous);
332
-
333
167
  private void Previous()
334
-
335
- {
168
+ {
336
-
337
169
  current = current.AddMonths(-1);
338
-
339
170
  Update();
340
-
341
- }
171
+ }
342
-
343
-
344
172
 
345
173
  private ViewModelCommand nextCommand;
346
-
347
174
  public ViewModelCommand NextCommand => nextCommand ??= new ViewModelCommand(Next);
348
-
349
175
  private void Next()
350
-
351
- {
176
+ {
352
-
353
177
  current = current.AddMonths(1);
354
-
355
178
  Update();
356
-
357
- }
179
+ }
358
-
359
-
360
180
 
361
181
  private ViewModelCommand addCommand;
362
-
363
182
  public ViewModelCommand AddCommand => addCommand ??= new ViewModelCommand(Add, () => !string.IsNullOrEmpty(Text));
364
-
365
183
  private void Add()
366
-
367
- {
184
+ {
368
-
369
185
  var s = new Schedule { DateTime = selected.DateTime.Date, Text = Text, };
370
-
371
186
  schedules.Add(s);
372
-
373
187
  selected.Schedules.Add(s);
374
-
375
188
  Text = null;
376
-
377
- }
189
+ }
378
-
379
-
380
190
 
381
191
  private List<Schedule> schedules = new List<Schedule>();
382
-
383
192
  private DateTime current = DateTime.Now.Date;
384
193
 
385
-
386
-
387
194
  // 月変更時 Items再生成 雑w
388
-
389
195
  private void Update()
390
-
391
- {
196
+ {
392
-
393
197
  // 選択月 日数
394
-
395
198
  var days = DateTime.DaysInMonth(current.Year, current.Month);
396
199
 
397
-
398
-
399
200
  // 日数分DayViewModel作成
400
-
401
201
  Items = Enumerable.Range(1, days).Select(x =>
402
-
403
- {
202
+ {
404
-
405
203
  var d = new DateTime(current.Year, current.Month, x);
406
-
407
204
  return new DayViewModel { DateTime = d, };
408
-
409
205
  }).ToList();
410
206
 
411
-
412
-
413
207
  // DayViewModelにScheduleを突っ込む
414
-
415
208
  foreach (var d in Items)
416
-
417
209
  foreach (var s in schedules.Where(x => x.DateTime.Date == d.DateTime.Date))
418
-
419
210
  d.Schedules.Add(s);
420
211
 
421
-
422
-
423
212
  firstDayOffset = (int)new DateTime(current.Year, current.Month, 1).DayOfWeek;
424
-
425
213
  selected = Items.Single(x => x.DateTime.Date == current.Date);
426
-
427
214
  current = selected.DateTime.Date;
428
215
 
429
-
430
-
431
216
  // 更新
432
-
433
217
  RaisePropertyChanged(nameof(FirstDayOffset));
434
-
435
218
  RaisePropertyChanged(nameof(HeaderMonth));
436
-
437
219
  RaisePropertyChanged(nameof(Items));
438
-
439
220
  RaisePropertyChanged(nameof(Selected));
440
-
441
- }
221
+ }
442
-
443
-
444
222
 
445
223
  public void Initialize()
446
-
447
- {
224
+ {
448
-
449
225
  // 見本Scheduleデータ作成
450
-
451
226
  var r = new Random();
452
-
453
227
  var n = DateTime.Now.Date;
454
-
455
228
  schedules = Enumerable.Range(0, 10).Select(_ =>
456
-
457
- {
229
+ {
458
-
459
230
  var d = n.AddDays(r.Next(-30, 30));
460
-
461
231
  return new Schedule { DateTime = d, Text = d.ToString("MM/dd の予定"), };
462
-
463
232
  }).ToList();
464
233
 
465
-
466
-
467
234
  Update();
468
-
469
- }
235
+ }
470
-
471
236
  }
472
-
473
237
  }
474
-
475
238
  ```
476
239
 
477
-
478
-
479
- ```C#
240
+ ```cs
480
-
481
241
  using Livet;
482
-
483
242
  using System;
484
-
485
243
  using System.Diagnostics;
486
244
 
487
245
 
488
-
489
-
490
-
491
246
  namespace Questions333874.Models
492
-
493
247
  {
494
-
495
248
  [DebuggerDisplay("{DateTime} {Text}")]
496
-
497
249
  public class Schedule : NotificationObject
498
-
499
250
  {
500
-
501
251
  private DateTime dateTime;
502
-
503
252
  public DateTime DateTime { get => dateTime; set => RaisePropertyChangedIfSet(ref dateTime, value); }
504
253
 
505
-
506
-
507
254
  private string text;
508
-
509
255
  public string Text { get => text; set => RaisePropertyChangedIfSet(ref text, value); }
510
-
511
256
  }
512
-
513
257
  }
514
-
515
258
  ```
516
259
 
517
-
518
-
519
260
  ![アプリ画像](143582f9facbd7b768725544b5280d23.png)
520
261
 
521
-
522
-
523
262
  ---
524
263
 
525
-
526
-
527
- `MVVM`寄せで回答しましたが、私だったらこの方式は使いませんね。
264
+ MVVM寄せで回答しましたが、私だったらこの方式は使いませんね。
528
-
529
-
530
-
265
+
531
- カレンダー表示というのは、`View`の都合ですよね?(縦に並ぼうが横に並ぼうが、`Model`は知ったこっちゃない)
266
+ カレンダー表示というのは、Viewの都合ですよね?(縦に並ぼうが横に並ぼうが、Modelは知ったこっちゃない)
532
-
533
267
  カスタムコントロール(あるいはユーザーコントロール)を作って、`List<Schedule>`のようなものをバインドすれば勝手に表示するように作りますかね。
534
268
 
535
-
536
-
537
269
  その際はバリバリにコードビハインドということになります(例えばこのような [wpf/Calendar.cs at main · dotnet/wpf](https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/Calendar.cs))
538
270
 
539
-
540
-
541
- `MVVM`がパワーワードすぎて、なかなか本質が見えないのが難しく感じる原因でしょうか。
271
+ MVVMがパワーワードすぎて、なかなか本質が見えないのが難しく感じる原因でしょうか。