ChatGPTにXAMLを書いてもらった

## 元ネタ様
 [ChatGPTを利用して画面モックを爆速で作成する](https://acro-engineer.hatenablog.com/entry/2023/10/13/150000)
上記事はChat GPT-4を使用し、HTMLとBootstrapを使用した画面モックアップをChatGPTに作ってもらおう、というもの。
30秒足らずで、Bootstrapによるレイアウトまで組まれた、立派なモックアップが完成するという。
**ならXAMLもできるんじゃねーの！**

## [Bing Chat](https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx)
みんなご存知、**Bing**。
Edgeの標準検索エンジン。つまり誰にも使われてこなかったMicrosoftの検索エンジン。

そんなBingが2023年7月ごろ（うろ覚え）個人、および法人向けに発表したAIチャット。それが、**[Bing Chat](https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx)**。
なんと、**GPT-4が無料で使えてしまうのだ**。OMG。
GPT-4は、普通に使うと月額3000円くらいかかるので、そう考えると大盤振る舞いすぎて逆にこわい。

#### 追記 2024-01-22
現在は[Copilot](https://copilot.microsoft.com/)というサービスになっています。

## ものは試し
というわけで、早速BingChatのGPT-4くんに画面を作っていただきたいと思います。
プロンプトはこんな具合。対照実験のため、大筋元ネタ様のプロンプトと同じ条件としました。
```
次の画面構成の案を、WPFにおけるXAML形式で出力してください。

- 書籍の登録画面の登録フォーム 
- 書籍のタイトル、出版社、カテゴリ、登録日、備考を持つ 
- カテゴリはプルダウンからの選択 
- 最後にキャンセルボタン、登録ボタンがある
```

回答はいかに

![Pasted image 20231013170845.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2777804/052e2ad2-f40e-9a0f-0915-3350e5011cf6.png)


``` xml
<Window x:Class="BookRegistration.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="書籍登録" Height="350" Width="525">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 書籍のタイトル -->
        <Label Grid.Row="0" Grid.Column="0" Content="タイトル:"/>
        <TextBox Grid.Row="0" Grid.Column="1"/>

        <!-- 出版社 -->
        <Label Grid.Row="1" Grid.Column="0" Content="出版社:"/>
        <TextBox Grid.Row="1" Grid.Column="1"/>

        <!-- カテゴリ -->
        <Label Grid.Row="2" Grid.Column="0" Content="カテゴリ:"/>
        <ComboBox Grid.Row="2" Grid.Column="1"/>

        <!-- 登録日 -->
        <Label Grid.Row="3" Grid.Column="0" Content="登録日:"/>
        <DatePicker Grid.Row="3" Grid.Column="1"/>

        <!-- 備考 -->
        <Label Grid.Row="4" Grid.Column="0" Content="備考:"/>
        <TextBox Grid.Row="4" Grid.Column="1"/>

        <!-- ボタン -->
        <StackPanel Grid.Row="5" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button Content="キャンセル" Margin="0,10,10,0"/>
            <Button Content="登録" Margin="0,10,0,0"/>
        </StackPanel>
    </Grid>
</Window>

```
お？ぱっと見良さそうだぞ

ちょっとだけ弄って、WPFプロジェクトへ投入！
（MaterialDesignとか付いてるのは気にしないでください）

![Pasted image 20231013171027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2777804/da390c3e-fe89-a181-8c13-ec4a0ee33afb.png)

**OH MY GOD**

若干の粗さはあるものの、基本のレイアウトとしては要求通りの画面が完成してしまった。
登録日については、何も指定していないにも関わらず`DatePicker`を採用してくれる気の利かせっぷり。
これが、たったの30秒で出力されるというのは……恐るべしChat GPT-4。

## おわりに
著作権の問題などもあり、企業での商用利用などではまだまだ賛否のあるAIによるコーディング支援。
一方で、`GitHub Copilot`の登場、及びIDEによる`Copilot`のサポートなど、業界的にはAIコーディング支援はガンガン推進されている。
我々.NET開発者に最も近い話で言うと、[IntelliSenceとCopilotの連携強化](https://atmarkit.itmedia.co.jp/ait/articles/2306/14/news036.html)といった話もあり、そのうち誰しもがAIのお世話になる日も近いっぽい。

なんてのは誰の目に見ても明白なので、とりあえず個人レベルで試してみた、というお話でした。
## 追記（2023.10.17）: より複雑な条件に対応してもらう

前回の内容では少し味気なかったので、より複雑な条件を指示してみた。

プロンプトは以下。XAMLに加えて、ViewModelにあたるC#の実装もお願いしてみる。
加えて、ライブラリ`Prism`を使用していることを前提に加えた。

```
以下の指示に従い、WPFにおけるViewにあたるXAML、及びViewModelに当たるClassを実装してください。

* 本アプリケーションではフレームワークとして`Prism`を採用している。そのため、ViewのDataContextには、`Prism`経由で自動的にViewModelが登録される
* `Prism`によって管理される`Region`に登録するため、生成されるべきViewは`UserControl`である
* 本画面は、データベースから取得されるコレクションを一覧形式で表示し、データの閲覧、および操作を行うための画面である
* データベースから取得されるデータは、本アプリケーションでは`Person`クラスとして定義され、以下のプロパティを持っている
	* Id
		* ID
	* Name
		* 名前
	* Age
		* 年齢
	* Belongs
		* 所属
* 一行は一つの`Person`インスタンスと結びつき、`Person`インスタンスのデータを表示する
* 一行につき3つのボタンが備わる。それぞれのボタンは以下の表示を持つ
	* 閲覧
	* 編集
	* 削除
```

### XAML
まずはXAMLから。
``` xml
<UserControl x:Class="YourNamespace.YourView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <DataGrid ItemsSource="{Binding Persons}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding Id}" />
                <DataGridTextColumn Header="名前" Binding="{Binding Name}" />
                <DataGridTextColumn Header="年齢" Binding="{Binding Age}" />
                <DataGridTextColumn Header="所属" Binding="{Binding Belongs}" />
                <DataGridTemplateColumn Header="操作">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <Button Content="閲覧" Command="{Binding DataContext.ViewCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
                                <Button Content="編集" Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
                                <Button Content="削除" Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

```

個人的に特筆したいのは**カラムヘッダーの指定部分**。

プロンプト内では以下のようにだけGPT-4に伝えていた。

```
* データベースから取得されるデータは、本アプリケーションでは`Person`クラスとして定義され、以下のプロパティを持っている
	* Id
		* ID
	* Name
		* 名前
	* Age
		* 年齢
	* Belongs
		* 所属
```

プロンプト内でカラムヘッダーについて一切言及していない。プロパティ名の後ろが概要であることも示していない。
しかしながらGPT-4は**意図を汲み**、それをXAMLへ反映している。

``` xml
<DataGridTextColumn Header="ID" Binding="{Binding Id}" />
<DataGridTextColumn Header="名前" Binding="{Binding Name}" />
<DataGridTextColumn Header="年齢" Binding="{Binding Age}" />
<DataGridTextColumn Header="所属" Binding="{Binding Belongs}" />
```

### ViewModel
続いてC#のコードを見ていく。

``` cs
using Prism.Commands;
using Prism.Mvvm;
using System.Collections.ObjectModel;

namespace YourNamespace
{
    public class YourViewModel : BindableBase
    {
        private ObservableCollection<Person> _persons;
        public ObservableCollection<Person> Persons
        {
            get { return _persons; }
            set { SetProperty(ref _persons, value); }
        }

        public DelegateCommand<Person> ViewCommand { get; private set; }
        public DelegateCommand<Person> EditCommand { get; private set; }
        public DelegateCommand<Person> DeleteCommand { get; private set; }

        public YourViewModel()
        {
            // ここでPersonsをデータベースから取得する処理を書く

            ViewCommand = new DelegateCommand<Person>(ViewPerson);
            EditCommand = new DelegateCommand<Person>(EditPerson);
            DeleteCommand = new DelegateCommand<Person>(DeletePerson);
        }

        private void ViewPerson(Person person)
        {
            // ここでpersonを閲覧する処理を書く
        }

        private void EditPerson(Person person)
        {
            // ここでpersonを編集する処理を書く
        }

        private void DeletePerson(Person person)
        {
            // ここでpersonを削除する処理を書く
        }
    }
}
```

非常に堅実にまとまっている。「`Prism`を使用している」ことを前提として伝えたことにより、以下の二点が達成されている。
* `BindableBase`の継承
* `SetProperty()`、`DelegateCommand`の使用

### 条件を加える
ここから更に、上記に加えて`ReactiveProperty`を使用した形式に書き換えるよう指示してみる。
プロンプト
```
ViewModelを、ライブラリ`ReactiveProperty`を使用する形式に書き換えてください
```

返答されたコードは以下。
``` cs
using Prism.Mvvm;
using Reactive.Bindings;
using System.Collections.ObjectModel;

namespace YourNamespace
{
    public class YourViewModel : BindableBase
    {
        public ReactiveCollection<Person> Persons { get; private set; }

        public ReactiveCommand<Person> ViewCommand { get; private set; }
        public ReactiveCommand<Person> EditCommand { get; private set; }
        public ReactiveCommand<Person> DeleteCommand { get; private set; }

        public YourViewModel()
        {
            // ここでPersonsをデータベースから取得する処理を書く

            Persons = new ReactiveCollection<Person>();

            ViewCommand = new ReactiveCommand<Person>();
            ViewCommand.Subscribe(ViewPerson);

            EditCommand = new ReactiveCommand<Person>();
            EditCommand.Subscribe(EditPerson);

            DeleteCommand = new ReactiveCommand<Person>();
            DeleteCommand.Subscribe(DeletePerson);
        }

        private void ViewPerson(Person person)
        {
            // ここでpersonを閲覧する処理を書く
        }

        private void EditPerson(Person person)
        {
            // ここでpersonを編集する処理を書く
        }

        private void DeletePerson(Person person)
        {
            // ここでpersonを削除する処理を書く
        }
    }
}
```

プロパティ、コマンド、コレクションに至るまで`ReactiveProperty`で代替されている。また、`DelegateCommand`と`ReactiveCommand`との間にあるメソッド登録のお作法の差もきっちりと対応している。

最後に、以下のプロンプトを発行した。
`ReactiveProperty`初心者が必ず忘れてしまう、`Value`の付加を見る。

```
`Person`クラスに実装されているプロパティもまた`ReactiveProperty`を使用して実装されています。それを踏まえ、XAMLを更新してください。
```

``` xml
<DataGridTextColumn Header="ID" Binding="{Binding Id.Value}" />
<DataGridTextColumn Header="名前" Binding="{Binding Name.Value}" />
<DataGridTextColumn Header="年齢" Binding="{Binding Age.Value}" />
<DataGridTextColumn Header="所属" Binding="{Binding Belongs.Value}" />
```

もはや驚くまいが、完璧。
後から、後からと付加される仕様の追加にもしっかりと対応してくれる。

もちろん、全てが完璧というわけではない（例えば、`Person`コレクションの取得はコンストラクタでは行わないほうが良い）が、大筋としては十分納得できるコードが出力されている。

GPT-4の精度、これは目を見張る物がありそうだ。