The Answer is 42

技術と日常の「答え」を探して

技術と人間のあいだで——問い続けるという尊厳

かつて街を歩く人々は、前を向いて歩いていた。 隣を歩く人と顔を見合わせ、目と目を合わせて言葉を交わした。 しかし今は違う。多くの人々がスマートフォンの画面に目を落とし、手のひらの中にある世界とだけ向き合っている。 その姿に、私はふと問いを覚える——これは本当に「つながり」なのだろうか?

スマホは便利だ。AIとも話せる。調べ物もできる。地図も、予定も、記録も、すべてがそこにある。 私もその恩恵を受け、いまこうしてAIと対話している自分がいる。 けれど同時に、私は感じている。 目の前の人との関わりが、どこか希薄になっている。 言葉が、まなざしが、手触りが、確かさを失いつつある。

■ 技術の進歩と、関係性の後退

現代社会では、「つながる技術」が進化する一方で、人と人との実在的な関係は逆に希薄化しているという逆説がある。 恋愛は効率化され、会話は非同期に置き換えられ、出会いはアプリの中で条件化される。 まなざしよりも通知を、気配よりも反応速度を優先する文化の中で、私たちは関係性の“厚み”を失ってきたのではないか。

この現象は、単なる「便利さの副作用」ではない。 むしろ、私たちの生き方、価値観、世界の見方そのものが、技術によって構造的に再編されていることの表れなのだ。

■ 技術に囲い込まれる世界

ドイツの哲学者マルティン・ハイデガーは、「技術とは単なる道具ではなく、世界の現れ方そのものを規定するものだ」と述べた。 現代技術は、世界を「操作可能な資源」として立ち上げる枠組み——ゲシュテル(囲い込み)を作り出す。 その中で自然は、他者は、そして人間自身すら、「使えるもの」としてしか見えなくなってしまう。

かつて人は、空を見上げて歩いた。 いま、人は手のひらを見つめて歩いている。 世界の地平が、顔から画面へと変わってしまったのだ。

◆ しかし、それでも人は技術を求める

私はスマートフォンを手放せない。 私たちの生活は、技術によって支えられ、豊かになっている。 災害時には情報が命を救い、遠く離れた人とも声を交わせる。

だから、単純に「技術は悪だ」とは言えない。 むしろ、人が技術を求めるのは、人が不完全だからだ。 よりよく生きようとする、その自然な願いのあらわれでもある。

では、どうすればよいのか。 私たちは、技術に溺れることなく、それを用いながらも、人間であり続けることができるのか。

◆ 「問い続ける」という尊厳

私はこの矛盾のなかで、あえて問いを抱え続けたい。 技術に依存しながらも、それを疑い、距離をとろうとすること。 その揺らぎのなかにこそ、人間としての尊厳があるのではないか。

AIは、整った文章を返すことができる。 知識も、論理も、膨大に蓄えている。 けれど、矛盾のなかで苦しみ、悩み、考え続ける力—— それは、いまだAIには真似できない。

人間だけが、「いま、自分は生きている」という実感を持ち、 その生をどう生きるべきかを問い続けることができる。

◆ 技術のなかに生きながら、技術にのまれない生き方へ

だから私は、スマートフォンを手にしたまま、あえてこの問いを継続したい。 この手のひらの中にある世界と、目の前にある「誰か」の存在とを、もう一度つなぎ直すために。

私たちには選択肢がある。 すべてを効率化せず、すべてを便利にせず、 少しだけ不便な対話を選ぶこと。

そのわずかな余白に、もしかしたら、 「人と人とが本当に向き合う」ことの再出発があるのかもしれない。

XAML応用入門 ― DataTemplate・Resource管理・動的UI構築の実践技法

このシリーズの最終回では、XAMLの基礎を超えて、実際の業務や中規模以上のアプリケーションでも通用する応用的な技法を紹介します。

  • データ一覧の表示(ItemsControl, ListBox, DataTemplate
  • リソースの外部化とスケーラブルな管理
  • 状態に応じたUI制御(Trigger, DataTrigger
  • アニメーションとビジュアル演出
  • デバッグとツールの使い方

1. ItemsControlとDataTemplateによるリスト表示

目的:ViewModelのコレクションをUIに一覧表示

<ListBox ItemsSource="{Binding Users}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" FontWeight="Bold" />
                <TextBlock Text=" さん" Foreground="Gray" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
public class MainViewModel : ObservableObject
{
    public ObservableCollection<User> Users { get; } = new()
    {
        new User { Name = "田中" },
        new User { Name = "佐藤" }
    };
}

ポイント:

  • ItemsSourceObservableCollection<T> をバインド
  • データの構造に応じた DataTemplateXAMLで宣言

2. Resourceの外部化とスケーラブルな管理

背景:

アプリ全体でボタンの色やマージンなどを統一したい。XAMLにベタ書きしていると破綻する。

解決法:

ResourceDictionary に共通スタイルを定義し、必要な箇所から参照する。

Styles/Buttons.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Style x:Key="PrimaryButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="#007ACC"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="Padding" Value="8,4"/>
    </Style>
</ResourceDictionary>

App.xaml

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Styles/Buttons.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

利用箇所

<Button Content="登録" Style="{StaticResource PrimaryButtonStyle}" />

3. TriggerとDataTriggerによる状態制御

<Style TargetType="TextBox">
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="LightYellow"/>
        </Trigger>
    </Style.Triggers>
</Style>

DataTrigger を使えば、ViewModelのプロパティに応じてUIを切り替えられます:

<DataTemplate>
    <TextBlock Text="{Binding Status}">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Status}" Value="Error">
                        <Setter Property="Foreground" Value="Red"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</DataTemplate>

4. アニメーションとStoryboardの基本

例:ロード中にプログレスバーの色が点滅する演出

<ProgressBar Name="LoadingBar" Width="200" Height="20" />
<ProgressBar.Triggers>
    <EventTrigger RoutedEvent="Loaded">
        <BeginStoryboard>
            <Storyboard RepeatBehavior="Forever" AutoReverse="True">
                <ColorAnimation Storyboard.TargetName="LoadingBar"
                                Storyboard.TargetProperty="(ProgressBar.Foreground).(SolidColorBrush.Color)"
                                To="LightGreen" Duration="0:0:1" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
</ProgressBar.Triggers>

Storyboard を使えば時間経過による視覚的変化を定義できます。


5. デバッグと開発支援ツールの活用

XAMLに関するよくある悩み:

  • バインディングが効かない
  • スタイルが適用されない
  • テンプレートが壊れている

解決のためのツール:

  • Live Visual Tree(Visual Studio
    → 実行中のUIツリーと DataContext を確認可能

  • Live Property Explorer
    → 実際にバインディングされた値やトリガーの状態を即座に確認可能

  • Snoop(外部ツール)
    WPF専用のインスペクションツール。バインディングエラーやスタイルの適用ミスを見抜ける


6. おわりに ― XAMLは道具、UI設計はアート

ここまで3回にわたって、XAMLの基本から応用までを体系的に扱ってきました。

  • 宣言的UIでロジックと見た目を分離し、
  • バインディングでデータとの接続性を高め、
  • スタイル/テンプレートで見た目の再利用性を実現し、
  • Triggerやアニメーションで状態に応じた表現が可能になりました。

XAMLは道具にすぎません。しかし、その使い方を極めることで、開発体験は“設計”から“表現”へと昇華します。

XAMLスタイルとテンプレート入門 ― WPFアプリのUIを美しく再利用可能に

XAMLに慣れてきたら、次に学びたいのが「スタイル(Style)」と「コントロールテンプレート(ControlTemplate)」です。
これらを理解すると、WPFアプリのUIを美しく、再利用性の高い設計に仕上げることができます。

この記事では、次のような疑問に答えながら、実践的なサンプルを交えて解説します。

  • スタイルってCSSとどう違うの?
  • ボタンの見た目ってどう変えるの?
  • 複数画面で使い回す方法は?

1. スタイルとは? ― プロパティの共通設定を一元管理

XAMLでUIを構築していると、何度も同じプロパティ(幅、マージン、色など)を書くことが増えてきます。
そこで活躍するのが Style です。

基本構文

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="Background" Value="LightBlue"/>
    </Style>
</Window.Resources>

<Button Content="保存"/>
<Button Content="キャンセル"/>

→ この例では、すべての Button に対して一括で見た目を統一できます。


2. スタイルの使い分け ― x:Keyによる個別適用

全てのコントロールに同じStyleが適用されると困る場合もあります。そんな時は x:Key を使って明示的に適用します。

<Window.Resources>
    <Style x:Key="DangerButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="Crimson"/>
        <Setter Property="Foreground" Value="White"/>
    </Style>
</Window.Resources>

<Button Content="削除" Style="{StaticResource DangerButtonStyle}"/>

3. トリガーによる状態変化 ― Hover時やIsEnabledの切替

<Style TargetType="Button">
    <Setter Property="Background" Value="LightGray"/>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="LightGreen"/>
        </Trigger>
    </Style.Triggers>
</Style>

→ マウスオーバー時に背景色を変える例です。


4. ControlTemplateでコントロールの“中身”をまるごと再定義

「見た目」だけでなく、「構造」そのものを変えたい場合は ControlTemplate を使います。

例:丸いボタンにカスタマイズ

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Border Background="{TemplateBinding Background}"
                            CornerRadius="30"
                            BorderBrush="Gray"
                            BorderThickness="2"
                            Padding="10">
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Button Content="丸いボタン"/>

TemplateBinding によって元のプロパティ値を内部に反映できます。


5. DataTemplateとの違いは?

  • ControlTemplate → ボタンやテキストボックスなど「UI部品の見た目」を定義
  • DataTemplate → ListBoxやDataGridなどに表示する「データの見た目」を定義
<ListBox ItemsSource="{Binding Users}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                <TextBlock Text=" さん"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

6. スタイル・テンプレートの外部化と再利用

大規模なアプリになると、スタイルやテンプレートを別ファイル(ResourceDictionary)に分離した方が保守しやすくなります。

外部ファイル定義(e.g. Styles.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
    <Style x:Key="PrimaryButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="Navy"/>
        <Setter Property="Foreground" Value="White"/>
    </Style>
</ResourceDictionary>

App.xaml から読み込む

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Styles/Styles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

7. よくあるつまずきポイント

症状 原因 対策
Styleが反映されない TargetTypeミスマッチ or リソース未読込 名前や参照位置を確認
ControlTemplateでボタンが動かなくなる ContentPresenter がない 忘れずに配置する
トリガーが効かない Property名が誤っている IsMouseOver, IsEnabled など正確に指定

8. まとめ:UIの再利用性と拡張性が飛躍的に向上

XAMLのスタイルとテンプレートを使いこなすことで、WPFアプリのUIは劇的に美しく、効率的になります。
- スタイル:共通化・一元管理 - テンプレート:構造自体のカスタマイズ - 外部化:大規模アプリへの対応


次回予告

次の記事では、「DataTemplateを活用したリスト表示とアイテム選択UIの設計」を予定しています。
ユーザー一覧やログ表示など、「繰り返し表示されるUI」をどうデザインするかに興味がある方は、ぜひご期待ください。

XAML入門 ― UI定義から学ぶWPFの本質

WPFアプリケーションを作る上で、ユーザーインターフェース(UI)の定義には XAML(ザムル) が使われます。
HTMLやXMLに似た構文ながら、WPFの機能と密接に連携しており、「設計とロジックの分離」というMVVMアーキテクチャの実現にも不可欠です。

この記事では、WPFにおけるXAMLの役割、基本構文、レイアウト制御、バインディング、リソース管理、さらにはMVVM連携の基礎までを一気に解説します。


1. なぜXAMLを使うのか?

XAMLは、WPFにおいて「UI設計を視覚的・宣言的に定義できる」手段です。
主なメリットは次の通りです:

  • UIをコードから分離でき、読みやすく保守しやすい
  • ツールと連携しやすいVisual Studioのデザイナ表示)
  • データバインディングやスタイルとの親和性が高い

UI構築をコードで全て書くこともできますが、保守性・再利用性の観点からXAMLは圧倒的に有利です。


2. XAMLの基本構造と文法

ウィンドウの基本構造

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="メインウィンドウ" Height="300" Width="400">
    <Grid>
        <Button Content="クリック" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>
  • xmlnsWPF標準のUI要素(Button、TextBlockなど)を使うための名前空間
  • xmlns:x:x:Name や x:Class などXAML構文上の補助
  • <Grid>:UI要素のレイアウトコンテナ(後述)

属性とプロパティ要素の違い

<Button Content="保存" Width="100" />

<!-- 内容に複雑な要素を含める場合はプロパティ要素記法 -->
<Button Width="120">
    <Button.Content>
        <StackPanel>
            <TextBlock Text="保存" />
            <TextBlock Text="(ローカル)" />
        </StackPanel>
    </Button.Content>
</Button>

3. よく使われるレイアウトパネル

StackPanel(縦や横に積む)

<StackPanel Orientation="Vertical" Margin="10">
    <TextBlock Text="名前" />
    <TextBox Width="200"/>
    <Button Content="登録" />
</StackPanel>

Grid(表形式レイアウト)

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <TextBlock Text="ID:" Grid.Row="0" Grid.Column="0" />
    <TextBox Grid.Row="0" Grid.Column="1" />

    <TextBlock Text="パスワード:" Grid.Row="1" Grid.Column="0" />
    <PasswordBox Grid.Row="1" Grid.Column="1" />
</Grid>

4. データバインディングの基本

XAMLの最大の強みのひとつが データバインディング です。

<TextBox Text="{Binding UserName}" />
<Button Content="ログイン" Command="{Binding LoginCommand}" />

DataContextとは?

WPFでは DataContextバインディングの起点になります。コードビハインドで次のようにViewModelをセットします。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainViewModel();
    }
}

ViewModel は通常、INotifyPropertyChanged を実装し、プロパティ変更をUIに通知します(CommunityToolkit.Mvvm使用で簡潔に)。


5. イベント vs コマンド

従来のイベント処理(XAMLC#メソッド名を書く)

<Button Content="押す" Click="Button_Click" />

→ この方法でも動きますが、MVVMパターンでは避けるべきです。

コマンド(CommandプロパティによるMVVM的記述)

<Button Content="押す" Command="{Binding ClickCommand}" />

→ ViewModel側に ICommand を実装したプロパティが必要です。

CommunityToolkit.Mvvm を使えば次のように簡潔に記述できます:

[RelayCommand]
private void Click() {
    // 処理内容
}

6. スタイルとリソース

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="Margin" Value="5"/>
        <Setter Property="FontWeight" Value="Bold"/>
    </Style>
</Window.Resources>

<Button Content="OK"/>
<Button Content="キャンセル"/>

→ すべてのボタンに共通の見た目が適用されます。

リソースの参照方法には StaticResourceDynamicResource があり、用途によって使い分けます。


7. よくあるエラーと対処法

症状 原因 対処法
赤波線が出る 名前空間や型名のミス x:Classとコードビハインドの整合性を確認
デザイナがクラッシュ Visual Studioの不具合 プロジェクトを一度ビルドして再表示
バインディングが効かない DataContext未設定 or プロパティ通知不足 ViewModel構成を再確認

8. サンプル:ミニログイン画面の全体構成

<StackPanel Margin="20">
    <TextBlock Text="ユーザー名" />
    <TextBox Text="{Binding UserName}" Width="200" />

    <TextBlock Text="パスワード" />
    <PasswordBox x:Name="PasswordInput" Width="200" />

    <Button Content="ログイン" Command="{Binding LoginCommand}" Margin="0,10,0,0"/>
</StackPanel>

ViewModel(CommunityToolkit使用):

public partial class LoginViewModel : ObservableObject
{
    [ObservableProperty]
    private string userName;

    [RelayCommand]
    private void Login()
    {
        // パスワードは PasswordBox から View経由で取得
    }
}

9. おわりに:XAMLが“読める”とUIはもっと楽しくなる

WPFは「ロジックと見た目を切り分けて設計する」思想を徹底できる稀有なフレームワークです。
XAMLに慣れてくると、UIがコードから解き放たれ、開発効率も表現力も格段に向上します。

次回は、「スタイルとテンプレート入門」 に進み、再利用性とデザインの自由度をさらに高めていきましょう。

ロード画面を“遊び場”に──WPFアプリの待ち時間で四目並べミニゲームを実装する

WPF(.NET 8)アプリのロード画面を「ただの待ち時間」から「楽しい体験」に変える方法として、タッチ操作で遊べる四目並べミニゲーム(対コンピュータ)をロード進捗と並列実行する設計を解説します。
ここではCommunityToolkit.Mvvmを用いたMVVM構成(View/ViewModel/Model)の実装例を、サンプルコードとともに提示します。


1. モデル(Model):ゲームロジックのクラス

四目並べのロジック(盤面管理・手の入力・勝敗判定・AI)をモデルに切り出します。

// Models/FourInARowGame.cs
public enum Cell { Empty, Player, Computer }

public class FourInARowGame
{
    public Cell[,] Board { get; } = new Cell[6, 7];
    public bool IsPlayerTurn { get; set; } = true;
    public bool IsGameOver { get; private set; }

    public bool PlaceDisc(int column, Cell cellType)
    {
        for (int row = 5; row >= 0; row--)
        {
            if (Board[row, column] == Cell.Empty)
            {
                Board[row, column] = cellType;
                return true;
            }
        }
        return false;
    }

    public bool CheckWin(Cell cellType)
    {
        // 横・縦・斜め4連続チェック(簡略例)
        int rows = Board.GetLength(0);
        int cols = Board.GetLength(1);
        for (int y = 0; y < rows; y++)
        {
            for (int x = 0; x < cols; x++)
            {
                if (Board[y, x] != cellType) continue;
                // 横
                if (x + 3 < cols &&
                    Enumerable.Range(0, 4).All(i => Board[y, x + i] == cellType))
                    return true;
                // 縦
                if (y + 3 < rows &&
                    Enumerable.Range(0, 4).All(i => Board[y + i, x] == cellType))
                    return true;
                // 斜め右下
                if (x + 3 < cols && y + 3 < rows &&
                    Enumerable.Range(0, 4).All(i => Board[y + i, x + i] == cellType))
                    return true;
                // 斜め左下
                if (x - 3 >= 0 && y + 3 < rows &&
                    Enumerable.Range(0, 4).All(i => Board[y + i, x - i] == cellType))
                    return true;
            }
        }
        return false;
    }

    public int GetRandomComputerMove()
    {
        var validColumns = Enumerable.Range(0, 7).Where(col => Board[0, col] == Cell.Empty).ToList();
        if (validColumns.Count == 0) return -1;
        var rnd = new Random();
        return validColumns[rnd.Next(validColumns.Count)];
    }

    public void Reset()
    {
        for (int y = 0; y < 6; y++)
            for (int x = 0; x < 7; x++)
                Board[y, x] = Cell.Empty;
        IsPlayerTurn = true;
        IsGameOver = false;
    }
}

2. ビューモデル(ViewModel):ゲームとロードの進捗管理

CommunityToolkit.MvvmのObservableObjectRelayCommandを用います。

// ViewModels/LoadingWithGameViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;

public partial class LoadingWithGameViewModel : ObservableObject
{
    private readonly FourInARowGame _game = new();

    public ObservableCollection<Cell> BoardCells { get; } = new ObservableCollection<Cell>(new Cell[6 * 7]);

    [ObservableProperty]
    private bool isLoading = true;

    [ObservableProperty]
    private int loadingProgress = 0;

    [ObservableProperty]
    private string statusMessage = "ロード中... ミニゲームで遊べます";

    [ObservableProperty]
    private bool isGameOver = false;

    public LoadingWithGameViewModel()
    {
        UpdateBoardCells();
        // ロード進捗のダミー進行(実アプリでは実際の進捗で更新)
        Task.Run(async () =>
        {
            for (int i = 1; i <= 100; i++)
            {
                await Task.Delay(40);
                LoadingProgress = i;
                if (LoadingProgress == 100)
                {
                    IsLoading = false;
                    StatusMessage = "ロード完了!";
                    break;
                }
            }
        });
    }

    [RelayCommand]
    private void PlaceDisc(int column)
    {
        if (IsGameOver || IsLoading || !_game.IsPlayerTurn) return;

        if (_game.PlaceDisc(column, Cell.Player))
        {
            UpdateBoardCells();
            if (_game.CheckWin(Cell.Player))
            {
                StatusMessage = "あなたの勝ち!";
                IsGameOver = true;
                return;
            }
            _game.IsPlayerTurn = false;
            ComputerMoveAsync();
        }
    }

    private async void ComputerMoveAsync()
    {
        await Task.Delay(500);
        int col = _game.GetRandomComputerMove();
        if (col >= 0 && _game.PlaceDisc(col, Cell.Computer))
        {
            UpdateBoardCells();
            if (_game.CheckWin(Cell.Computer))
            {
                StatusMessage = "コンピュータの勝ち";
                IsGameOver = true;
            }
        }
        _game.IsPlayerTurn = true;
    }

    [RelayCommand]
    private void ResetGame()
    {
        _game.Reset();
        IsGameOver = false;
        StatusMessage = IsLoading ? "ロード中... ミニゲームで遊べます" : "遊べます";
        UpdateBoardCells();
    }

    private void UpdateBoardCells()
    {
        for (int y = 0; y < 6; y++)
            for (int x = 0; x < 7; x++)
                BoardCells[y * 7 + x] = _game.Board[y, x];
        OnPropertyChanged(nameof(BoardCells));
    }
}
  • BoardCellsはUIバインディング用に一次元化
  • 各列をタッチ/クリックでPlaceDiscCommand.Execute(列番号)呼び出し
  • IsLoading中のみ操作可など制御
  • 進捗バーの進行・終了後の挙動もViewModelで一元管理

3. ビュー(View):UIと操作バインディング

例:XAMLで盤面を動的生成し、進捗バー・状態表示・操作ボタンとバインディング

<Window
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:local="clr-namespace:YourApp.ViewModels"
    DataContext="{local:LoadingWithGameViewModel}">
    <Grid>
        <ProgressBar Minimum="0" Maximum="100" Value="{Binding LoadingProgress}" Height="20" Margin="20" VerticalAlignment="Top"/>
        <TextBlock Text="{Binding StatusMessage}" FontSize="16" FontWeight="Bold" Margin="20,40,20,0" HorizontalAlignment="Center"/>
        <UniformGrid Rows="6" Columns="7" Margin="20,80,20,20">
            <ItemsControl ItemsSource="{Binding BoardCells}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Width="40" Height="40" Margin="2"
                                Command="{Binding DataContext.PlaceDiscCommand, RelativeSource={RelativeSource AncestorType=Window}}"
                                CommandParameter="{Binding (ItemsControl.AlternationIndex), RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContentPresenter}}">
                            <Ellipse Width="36" Height="36"
                                     Fill="{Binding Converter={StaticResource CellToBrushConverter}}"/>
                        </Button>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </UniformGrid>
        <Button Content="もう一度遊ぶ" Command="{Binding ResetGameCommand}" Margin="20" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
    </Grid>
</Window>

ポイント: - CellToBrushConverterはCellの値(Empty/Player/Computer)を色(透明/赤/青など)に変換 - タッチ/マウスどちらでも操作可 - ロード中も操作でき、ロード終了で自動的にゲームが終わる/無効化できる


4. まとめ

  • CommunityToolkit.MvvmでのMVVM分離により、UI・ロジック・状態管理が明快
  • タッチ・マウス両対応の「四目並べ」ミニゲームをロード画面に埋め込むことで、待ち時間を楽しい体験に
  • ゲーム・ロード進捗どちらの状態もViewModelで一元管理し、UIのバインディングで柔軟な制御が可能

参考 - CommunityToolkit.Mvvm ドキュメント - 四目並べ実装のロジック例(Qiita) - WPF タッチイベント入門(Microsoft Learn)