導入
一覧が空のときに「削除」ボタンが押せてしまうのはおかしな話です。第6章で学んだCanExecuteを使い、一覧に何かあるときだけ操作ボタンを有効にしてみましょう。
実務との違い(重要): 本来は一覧の行ごとに「この行を完了にする」「この行を削除する」ボタンを置き、
CommandParameterで対象の行をCommandに渡します。今回使っている<mvvm-runner>は教材用のサブセットのためCommandParameterに対応していません。そこでこのレッスンでは、「先頭の1件を完了にする」「すべて削除する」という単純な操作で、CanExecuteによるボタンの活性/非活性制御そのものを体験します。行ごとの操作を本物のWPFで試したい場合はC# WPFコースのDataGrid回を参照してください。
説明
flowchart TB
T["Tasks(一覧)"] -->|"Tasks.Any()"| CE{"canExecute"}
CE -->|"true(1件以上)"| ON["ボタンが押せる"]
CE -->|"false(0件)"| OFF["ボタンがグレーアウト"]
<Window Title="タスク一覧" Width="360" Height="360">
<StackPanel Margin="16">
<TextBlock Text="タスク一覧" FontWeight="Bold" FontSize="16" />
<TextBox Text="{Binding NewTaskTitle, Mode=TwoWay}" Margin="0,10" />
<Button Content="追加" Command="{Binding AddCommand}" Width="80" />
<ListBox ItemsSource="{Binding Tasks}" DisplayMemberPath="Title" Height="120" Margin="0,10" />
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
<Button Content="先頭を完了" Command="{Binding CompleteCommand}" Width="100" Margin="0,0,8,0" />
<Button Content="すべて削除" Command="{Binding DeleteAllCommand}" Width="100" />
</StackPanel>
</StackPanel>
</Window>
public record TaskDto(string Title, bool IsCompleted);
public class TaskListViewModel
{
public string NewTaskTitle { get; set; } = "";
public ObservableCollection<TaskDto> Tasks { get; set; } = new();
public ICommand AddCommand => new RelayCommand(
() => { Tasks.Add(new TaskDto(NewTaskTitle, false)); NewTaskTitle = ""; },
() => !string.IsNullOrWhiteSpace(NewTaskTitle)
);
public ICommand CompleteCommand => new RelayCommand(
() => { Tasks.RemoveAt(0); },
() => Tasks.Any()
);
public ICommand DeleteAllCommand => new RelayCommand(
() => { Tasks.Clear(); },
() => Tasks.Any()
);
}
CompleteCommand・DeleteAllCommandはどちらもcanExecuteに() => Tasks.Any()を渡している。**一覧が空のときはfalse**になり、ボタンが自動的にグレーアウトするTasks.RemoveAt(0)は先頭の1件を取り除く、Tasks.Clear()はすべて取り除く。どちらもObservableCollectionなので実行のたびに一覧の表示も自動で更新されるTaskDtoのIsCompletedはこのランナーの制約上、要素ごとに書き換える手段がないため、このレッスンでは「完了にする」を「一覧から取り除く」(=第5章のGetActiveTasksが完了済みを除外するのと同じ考え方)として簡略化している
やってみよう
- 一覧が空の状態では「先頭を完了」「すべて削除」がどちらも押せないことを確認する
- いくつかタスクを追加する
- 「先頭を完了」を押すたびに、一番上の項目が消えることを確認する
- 「すべて削除」を押すと、一覧が空になり、両方のボタンが再びグレーアウトすることを確認する
確認ポイント
- 一覧が空のとき、「先頭を完了」「すべて削除」の両方が押せないこと
- 1件以上あるとき、両方が押せるようになること
- 「先頭を完了」で先頭の1件だけが消え、「すべて削除」で全件消えること
まとめ
canExecuteは「今、実行してよい条件」を表す。Tasks.Any()のように一覧の状態を条件にできる- 条件を満たさない間、Viewは自動でボタンを非活性にする(
IsEnabledを自分で書かない) - 実務では行ごとの対象指定(
CommandParameter)が必要になる場面があるが、このランナーでは非対応。考え方(CanExecuteでの活性制御)自体は同じ
次回: RemainingCountのような計算プロパティを画面に表示します。