本文へスキップ
BecomeCoder

C# MVVMコース · 第7章 View ― WPFへ接続する · レッスン29

計算プロパティの表示 ― 残タスク数

ブラウザで完結

導入

第6章で作ったRemainingCount=>の計算プロパティ)を、実際に画面へ表示してみましょう。計算プロパティは特別な設定なしに、TextBlockから普通のプロパティと同じように{Binding}できます。

説明

classDiagram
    class TaskListViewModel {
        +Tasks : ObservableCollection~TaskDto~
        +RemainingCount : int «=> Tasks.Count(t => !t.IsCompleted)»
    }
    class TextBlock {
        Text="{Binding RemainingCount}"
    }
    TextBlock ..> TaskListViewModel : 表示のたびに再評価
<Window Title="タスク一覧" Width="360" Height="420">
  <StackPanel Margin="16">
    <TextBlock Text="残りタスク: " FontWeight="Bold" />
    <TextBlock Text="{Binding RemainingCount}" FontSize="20" Foreground="Teal" />
    <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 int RemainingCount => Tasks.Count(t => !t.IsCompleted);

    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()
    );
}
  • RemainingCountは保存された値ではなく=>の計算プロパティ。Tasksが変わるたびに、画面が再描画されるタイミングで式が再評価される
  • TextBlockText="{Binding RemainingCount}"と書くだけで、普通のプロパティと同じように計算プロパティを表示できる
  • このランナーの制約上、追加したタスクのIsCompletedは常にfalseのままなので(要素ごとの書き換え手段がないため)、実質的にRemainingCountTasks.Countと同じ値になる。式の形自体は第6章の本物のロジックと同じであることに注目してほしい

やってみよう

  1. 一覧が空の状態で「残りタスク: 0」と表示されることを確認する
  2. タスクを追加するたびに数字が増えることを確認する
  3. 「先頭を完了」や「すべて削除」で一覧を減らすと、数字も連動して減ることを確認する

確認ポイント

  • RemainingCountTasksの増減に連動して自動的に再計算されること
  • 計算プロパティもTextBlockから普通の{Binding}で表示できること

まとめ

  • 計算プロパティ(=>)は保存せず、参照されるたびに再計算される
  • Viewからは、状態プロパティも計算プロパティも同じ{Binding}で扱える
  • ここまででTaskListViewModelの主要な機能(一覧・追加・完了/削除・件数表示)がすべてWPFの画面につながった

次章: DIコンテナ・Composition Root・ヘルパークラスの設計指針など、周辺の道具立てを固めます。

実際に動かしてみよう

上がXAML(View)、真ん中がViewModelのC#風コード、右がプレビューです。ViewModelのプロパティやRelayCommandを書き換えると、プレビュー側の一覧・入力・ボタンの有効/無効にその場で反映されます(本物のC#コンパイルではなく、単純なプロパティ・計算プロパティ・コマンドの実行/CanExecuteを再現した学習用エンジンです。複雑なLINQや独自クラスのメソッド呼び出しなどは対象外です)。本文のコードを書き換えて、Viewとの連動を確かめながら進めましょう。

MVVM (XAML + ViewModel) プレビュー

書いた XAML(View) と ViewModel(C#風) をその場で連携させて動かす学習用プレビューを読み込みます(本物のWPF/MVVMではなく、単純な自動プロパティ・計算プロパティ(=>)・ICommand(RelayCommandのexecute/CanExecute)・{Binding}の双方向バインディングを再現したものです。独自クラスのメソッド呼び出し・複雑なLINQ・非同期処理・DI・CommandParameterは実行されません)。
スクロールして表示された時点でも自動で読み込まれます。