はじめに
Visual Studio 2026で、
- WPF
- CommunityToolkit.Mvvm
- .NET 8 / .NET 10
- x64固定
- テストプロジェクト付き
…みたいな「毎回作る構成」を、生成AIに作らせてみました。
最初はかなり順調でした。
しかし、
sln と slnx
この2つが出てきたあたりから、急に雲行きが怪しくなります。
以下はそのやり取りの記述です。
1. 最初は簡単だった
やりたかったこと
まずはシンプルに、
- WPFアプリ
- CommunityToolkit.Mvvm導入済み
- テンプレート化
をやってみます。
ChatGPTへ投げた指示-1
- 最初の思い付きをChatGPTに投げてみました。
# Visual StudioでProjectTemplateを作成したい
次のことが可能か、教えてください。
- WPFアプリケーションをベースにする
- CommunityToolkit.Mvvmを入れる
- これをテンプレート化したい
- 手順を残したいので、PowerShellで手順を教えてください
- コマンドを一つ一つ手順通り出してください
- 作成場所は、``C:\Source\ProjectTemplates`` としてください
ChatGPTの回答-1
可能です。方針は WPFプロジェクトを作る → CommunityToolkit.Mvvmを追加 → .template.config/template.json を置く → dotnet new install でテンプレート登録 です。Microsoft公式でも、既存プロジェクトに .template.config/template.json を追加して dotnet new テンプレート化できます。
以下、PowerShellで1つずつです。
1. 作業フォルダー作成
mkdir C:\Source\ProjectTemplates
cd C:\Source\ProjectTemplates
2. WPFアプリ作成
dotnet new wpf -n MyCompany.Wpf.Mvvm.Template
3. CommunityToolkit.Mvvm追加
dotnet add package CommunityToolkit.Mvvm
4. ビルド確認
dotnet build
5. .template.config/template.json作成
mkdir .template.config
6. template.json 作成
{
"$schema": "http://json.schemastore.org/template",
"author": "MyCompany",
"classifications": [ "WPF", "MVVM", "CommunityToolkit" ],
"identity": "MyCompany.Wpf.Mvvm.Template",
"name": "MyCompany WPF MVVM App",
"shortName": "my-wpf-mvvm",
"sourceName": "MyCompany.Wpf.Mvvm.Template",
"preferNameDirectory": true,
"tags": {
"language": "C#",
"type": "project"
}
}
ここで、JSONのみが貼られたので「メモ帳で」と作成してみたところ、見事文字化け。。。
vscodeでやることに。
code .template.config\template.json
で、起動したvscodeで、JSONを貼り付けて保存。
7. テンプレートをローカル登録
dotnet new install .
8. 登録確認
dotnet new list my-wpf-mvvm
9. テンプレートから新規プロジェクト作成
cd C:\Source
dotnet new my-wpf-mvvm -n SampleApp
10. ビルド確認
cd SampleApp
dotnet build
まあ、エラーもなく当然通ります。
11. ViewModelとViewのサンプルコードを追加
ですが、バインディングサンプルがはいっていないので、プロンプトを再度発行
MVVMのサンプルコードが入っていません。 ViewModelフォルダーMainWindowViewModel.csと、それをBindingするサンプルコードをxamlに入れる部分を生成して
mkdir .\ViewModels
ここで、PowerShellがC#コードを直接上書きするコードを生成してきた。
メモ帳で苦戦したのなんだったの・・・
@'
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace MyCompany.Wpf.Mvvm.Template.ViewModels;
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private string message = "Hello, CommunityToolkit.Mvvm!";
[RelayCommand]
private void ChangeMessage()
{
Message = "Button clicked!";
}
}
'@ | Set-Content .\ViewModels\MainWindowViewModel.cs -Encoding UTF8
続けて、MainWindow.xamlのコード
@'
<Window x:Class="MyCompany.Wpf.Mvvm.Template.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyCompany.Wpf.Mvvm.Template.ViewModels"
Title="MainWindow"
Height="300"
Width="500">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid Margin="24">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding Message}"
FontSize="24"
HorizontalAlignment="Center"
Margin="0,0,0,24" />
<Button Content="Change Message"
Command="{Binding ChangeMessageCommand}"
Width="160"
Height="36"
HorizontalAlignment="Center" />
</StackPanel>
</Grid>
</Window>
'@ | Set-Content .\MainWindow.xaml -Encoding UTF8
12. ビルド確認
dotnet build
これもエラーも起こらずに動きます。
13. テンプレート更新
ここで、テンプレートを更新するために、再度テンプレート登録を行います。例によって、「テンプレート更新のコード」を出してもらうと・・・
dotnet new install . --force
あれ???
これまで、
dotnet new uninstall MyCompany.Wpf.Mvvm.Template
dotnet new install .
でアンインストールしてから、再度インストールするよう指示されていましたが、今回は「--force」オプションで上書きできるといわれました。
これも、エラーもなく通ります。
・・・毎度毎度打ち込んでいた私の苦労はいったい・・・
ここから沼の始まり・・・
ここまではChatGPTにやらせて何の問題もなかったですし、手順もある程度さっさと出してくれました。
思い付き
- ''DEBUG''/''RELEASE''を切り替えたら、.NET8と.NET10の両方で動くようにしたいなあ
- x64しか、動く環境がないんだから、x64固定にしたいなあ
このあたりの思い付きも、ChatGPTに投げてみました。
できます。おすすめは .csproj をこうします。
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net8.0-windows;net10.0-windows</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<Platforms>x64</Platforms>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
ん??
これだと、Debug/Releaseで切り替わるわけではないのでは??
- Debug_Net8/Release_Net8/Debug_Net10/Release_Net10/ みたいに選択できない?
<PropertyGroup>
<Configurations>Debug_Net8;Release_Net8;Debug_Net10;Release_Net10</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug_Net8'">
<TargetFramework>net8.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE;NET8</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release_Net8'">
<TargetFramework>net8.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE;NET8</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug_Net10'">
<TargetFramework>net10.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE;NET10</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release_Net10'">
<TargetFramework>net10.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE;NET10</DefineConstants>
</PropertyGroup>
おおーー
これで、Debug_Net8/Release_Net8/Debug_Net10/Release_Net10の4つの構成ができました。
これで安心・・・と思いきや
これでプロジェクトを作ると、''sln''ファイルができません。
なんで??
- slnファイルがないので、slnファイルも作ってくれる?
はい。
テンプレート元プロジェクト作成時に .sln も自動作成できます。
dotnet new wpf の直後に追加してください。
dotnet new sln -n MyCompany.Wpf.Mvvm.Template
dotnet sln .\MyCompany.Wpf.Mvvm.Template.sln add .\MyCompany.Wpf.Mvvm.Template.csproj
最終的な構成はこうなります。
MyCompany.Wpf.Mvvm.Template
├─ MyCompany.Wpf.Mvvm.Template.sln
├─ MyCompany.Wpf.Mvvm.Template.csproj
├─ App.xaml
├─ MainWindow.xaml
├─ ViewModels
│ └─ MainWindowViewModel.cs
└─ .template.config
└─ template.json
あー・・・これだと、slnとcsprojが同じ階層にあって、複数プロジェクトのソリューションにやりづらいんだよね。。。
しかもやってみると、slnファイルじゃなくて、slnxファイルができるし。。。
**【NET10から、slnxファイルがデフォルトになったそうです。】
- あと、複数プロジェクトを作ること多いから、slnファイルは一つ上の階層に置いてほしい
ソリューションファイルを1階層上に置くとします
構成イメージ:
C:\Source\Repos\MyCompany.Wpf.Mvvm.Template
├─ MyCompany.Wpf.Mvvm.Template.slnx
└─ src
└─ MyCompany.Wpf.Mvvm.Template
├─ MyCompany.Wpf.Mvvm.Template.csproj
├─ MainWindow.xaml
└─ ...
ここで、1からテンプレート作成指示が来ます
ルートフォルダーの作成
mkdir C:\Source\Repos\MyCompany.Wpf.Mvvm.Template
cd C:\Source\Repos\MyCompany.Wpf.Mvvm.Template
ソースフォルダーの作成
mkdir .\src
cd .\src
WPFプロジェクト作成
dotnet new wpf -n MyCompany.Wpf.Mvvm.Template
ルートへ戻る
cd ..
ソリューションファイル作成
dotnet new sln -n MyCompany.Wpf.Mvvm.Template
ここで、csprojファイルの書き換え、ViewModel/Viewのサンプルコードの追加、template.jsonの作成などを行うのですが、割愛します。
プロジェクト追加
dotnet sln .\MyCompany.Wpf.Mvvm.Template.slnx add .\src\MyCompany.Wpf.Mvvm.Template\MyCompany.Wpf.Mvvm.Template.csproj
困惑
slnxファイルを開いたけど、、、Debug/Releaseとx64/x86/AnyCPUの切り替えになってる・・・
csprojの設定もやったのに???
このままを、プロンプトとして渡します。
この症状はかなり自然です。.csproj に <Configurations> や <Platforms> を書いても、Visual Studio のソリューション構成は .sln/.slnx 側の構成情報を見ているため、そこが未更新だと Debug/Release と Any CPU/x86/x64 のまま残ります。
思考時間: 5s
はい、そこは .csproj だけでは足りません。
理由はこれです。
.csproj:プロジェクト側の構成
.slnx:ソリューション側の構成
Visual Studioの上部ドロップダウン:基本的に ソリューション構成 を表示
Visual Studioは、ソリューション構成とプロジェクト構成を別物として扱います。Microsoftの説明でも、Debug / Release はソリューション構成とプロジェクト構成の両方に存在するものとして扱われます。
つまり、今はこうです。
csprojには Debug_Net8 などを書いた
でも slnx側には Debug/Release/Any CPU/x86/x64 が残っている
なので Visual Studioにはそっちが出ている
対応
Visual Studioで:
ビルド
→ 構成マネージャー
→ アクティブ ソリューション構成
→ 新規作成
で、以下を作ります。
Debug_Net8
Release_Net8
Debug_Net10
Release_Net10
プラットフォームは x64 だけ残します。不要なら Any CPU / x86 は削除します。
重要
Debug_Net8|x64 のソリューション構成が、プロジェクト側の Debug_Net8|x64 に紐づいていないと意味がありません。
なので構成マネージャーで、各行がこうなるようにします。
ソリューション構成 プロジェクト構成
Debug_Net8 Debug_Net8
Release_Net8 Release_Net8
Debug_Net10 Debug_Net10
Release_Net10 Release_Net10
結論
.csproj の設定は間違いではありません。
ただし Visual Studioのドロップダウンを変えるには、.slnx 側の構成も作る必要があります。
PowerShellだけで完全自動化したい場合は、.slnx を直接編集するか、いったん Visual Studioで構成を作った完成版 .slnx をテンプレートに含めるのが現実的です。
は???
ここまできて、Visual Studio頼みになるの?
せっかくpowershellでやってきたのに、、、
そこ、かなりハマりどころです。
実は `.sln/.slnx` の「構成マネージャー情報」は、CLIがほぼ弱いです。
つまり:
* `dotnet new sln`
* `dotnet sln add`
まではCLI対応
でも:
* ソリューション構成追加
* 構成マッピング
* ActiveCfg
* Build.0
* Platform設定
この辺はVisual Studio寄り機能で、CLIが弱い。
なので実務では、テンプレート作成時に:
完成済み .sln/.slnx をテンプレートへ含める
をやることが多いです。
---
ただし、PowerShellだけでもできます。
方法は:
slnx/sln を直接生成・編集する
です。
特に `.sln` はテキストなので編集可能。
むしろ現状、完全自動化したいなら `.sln` の方が楽です。
---
## おすすめ
ここで `.slnx` やめて `.sln` に戻した方がいいです。
理由:
* `.sln` はテキスト編集可能
* PowerShellで置換しやすい
* CI/CD相性良い
* VSCode/Rider安定
* 構成自動生成しやすい
---
## 実務でよくやる方法
PowerShellで:
dotnet new sln --format sln
↓
テンプレート用 `.sln` を丸ごとコピー
↓
プロジェクトGUIDだけ置換
です。
---
## なぜこうなるか
`.csproj` はMSBuild管理。
でも `.sln/.slnx` は:
Visual Studio Solution Layer
で、別世界です。
なので:
csprojだけ編集
↓
Visual Studioが勝手に構成同期
…はしてくれない
です。
---
## 一番安定する方法
テンプレート元を「完成済み」にしてしまう。
つまり:
- 構成作成済み
- x64固定済み
- Debug_Net8など作成済み
- CommunityToolkit導入済み
- MVVMサンプル入り
- testプロジェクト追加済み
の状態をテンプレート化。
これが実務で一番事故少ないです。
実は割愛してますが「実務でよくやる方法」の--format slnを出させるまでに、30分ぐらいかかりました。
slnで指定してるから、slnxじゃなくてslnが出てるでしょ?としばらく言うことを聞かず・・・
では、slnファイルの内容でDebut_Net8・・・などを設定する内容を書き出して
`.sln` の場合、該当箇所は主に2か所です。
* `SolutionConfigurationPlatforms`
* `ProjectConfigurationPlatforms`
ここを書けば、Visual Studioの構成ドロップダウンに出ます。
例えば:
Debug_Net8|x64
Release_Net8|x64
Debug_Net10|x64
Release_Net10|x64
だけにしたいなら、`.sln` の `Global` 内をこうします。
---
# SolutionConfigurationPlatforms
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug_Net8|x64 = Debug_Net8|x64
Release_Net8|x64 = Release_Net8|x64
Debug_Net10|x64 = Debug_Net10|x64
Release_Net10|x64 = Release_Net10|x64
EndGlobalSection
---
# ProjectConfigurationPlatforms
ここが重要です。
`{PROJECT_GUID}` は `.csproj` のGUIDです。
.NET SDK形式ではGUID省略されることもありますが、Visual Studioが `.sln` 側で内部GUIDを持ちます。
例:
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{PROJECT_GUID}.Debug_Net8|x64.ActiveCfg = Debug_Net8|x64
{PROJECT_GUID}.Debug_Net8|x64.Build.0 = Debug_Net8|x64
{PROJECT_GUID}.Release_Net8|x64.ActiveCfg = Release_Net8|x64
{PROJECT_GUID}.Release_Net8|x64.Build.0 = Release_Net8|x64
{PROJECT_GUID}.Debug_Net10|x64.ActiveCfg = Debug_Net10|x64
{PROJECT_GUID}.Debug_Net10|x64.Build.0 = Debug_Net10|x64
{PROJECT_GUID}.Release_Net10|x64.ActiveCfg = Release_Net10|x64
{PROJECT_GUID}.Release_Net10|x64.Build.0 = Release_Net10|x64
EndGlobalSection
---
## PowerShellでGUID取得
`sln` に既に追加済みなら:
Get-Content .\MyCompany.Wpf.Mvvm.Template.sln
で:
Project("{...}") = "MyCompany.Wpf.Mvvm.Template", "src\MyCompany.Wpf.Mvvm.Template\MyCompany.Wpf.Mvvm.Template.csproj", "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
最後のGUIDを使います。
---
## csproj側も必要
これは以前のまま必要です。
<PropertyGroup>
<Configurations>Debug_Net8;Release_Net8;Debug_Net10;Release_Net10</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
+各 Condition="'$(Configuration)' == 'Debug_Net8'" 等。
---
## 結果
これを両方やると、Visual Studio上で:
Debug_Net8
Release_Net8
Debug_Net10
Release_Net10
だけ出るようになります。
この後も、上記を貼り付けたらうまく行かず、結局「完成済みのslnファイルをテンプレートに入れる」方法でだしてもらうことにしました。
そして・・・
- マニュアル化したいので、PowerShellでやることをまとめて出してもらっていいですか?
ここで、ずらずらずら~っと、PowerShellのコードが出てきました。
ん???
- もしかして、ps1ファイルを作って一連の動作ってできるようにできます?
はい、できます。
むしろマニュアル化するなら 1本の .ps1 にまとめる方が安全です。
再度、ずらずらずら~っと、PowerShellのコードが出てきました。
# Create-WpfMvvmTemplate.ps1
$Root = "C:\Source\Repos\MyCompany.Wpf.Mvvm.Template"
$ProjectName = "MyCompany.Wpf.Mvvm.Template"
$ShortName = "my-wpf-mvvm"
if (Test-Path $Root) {
throw "既にフォルダーが存在します: $Root"
}
mkdir $Root
mkdir "$Root\src"
dotnet new wpf -n $ProjectName -o "$Root\src\$ProjectName"
dotnet add "$Root\src\$ProjectName\$ProjectName.csproj" package CommunityToolkit.Mvvm
mkdir "$Root\src\$ProjectName\ViewModels"
@"
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace $ProjectName.ViewModels;
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private string message = "Hello, CommunityToolkit.Mvvm!";
[RelayCommand]
private void ChangeMessage()
{
Message = "Button clicked!";
}
}
"@ | Set-Content "$Root\src\$ProjectName\ViewModels\MainWindowViewModel.cs" -Encoding UTF8
@"
<Window x:Class="$ProjectName.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:$ProjectName.ViewModels"
Title="MainWindow"
Height="300"
Width="500">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<Grid Margin="24">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding Message}"
FontSize="24"
HorizontalAlignment="Center"
Margin="0,0,0,24" />
<Button Content="Change Message"
Command="{Binding ChangeMessageCommand}"
Width="160"
Height="36"
HorizontalAlignment="Center" />
</StackPanel>
</Grid>
</Window>
"@ | Set-Content "$Root\src\$ProjectName\MainWindow.xaml" -Encoding UTF8
@"
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configurations>Debug_Net8;Release_Net8;Debug_Net10;Release_Net10</Configurations>
<Platforms>x64</Platforms>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'`$(Configuration)' == 'Debug_Net8'">
<TargetFramework>net8.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE;NET8</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'`$(Configuration)' == 'Release_Net8'">
<TargetFramework>net8.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE;NET8</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'`$(Configuration)' == 'Debug_Net10'">
<TargetFramework>net10.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE;NET10</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'`$(Configuration)' == 'Release_Net10'">
<TargetFramework>net10.0-windows</TargetFramework>
<DebugType>portable</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE;NET10</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
</ItemGroup>
</Project>
"@ | Set-Content "$Root\src\$ProjectName\$ProjectName.csproj" -Encoding UTF8
cd $Root
dotnet new sln -n $ProjectName --format sln
dotnet sln "$Root\$ProjectName.sln" add "$Root\src\$ProjectName\$ProjectName.csproj"
$slnPath = "$Root\$ProjectName.sln"
$sln = Get-Content $slnPath -Raw
$projectGuid = [regex]::Match(
$sln,
'Project\("\{[^}]+\}"\)\s*=\s*"[^"]+",\s*"[^"]+",\s*"\{([^}]+)\}"'
).Groups[1].Value
$solutionConfig = @"
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug_Net8|x64 = Debug_Net8|x64
Release_Net8|x64 = Release_Net8|x64
Debug_Net10|x64 = Debug_Net10|x64
Release_Net10|x64 = Release_Net10|x64
EndGlobalSection
"@
$projectConfig = @"
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{$projectGuid}.Debug_Net8|x64.ActiveCfg = Debug_Net8|x64
{$projectGuid}.Debug_Net8|x64.Build.0 = Debug_Net8|x64
{$projectGuid}.Release_Net8|x64.ActiveCfg = Release_Net8|x64
{$projectGuid}.Release_Net8|x64.Build.0 = Release_Net8|x64
{$projectGuid}.Debug_Net10|x64.ActiveCfg = Debug_Net10|x64
{$projectGuid}.Debug_Net10|x64.Build.0 = Debug_Net10|x64
{$projectGuid}.Release_Net10|x64.ActiveCfg = Release_Net10|x64
{$projectGuid}.Release_Net10|x64.Build.0 = Release_Net10|x64
EndGlobalSection
"@
$sln = [regex]::Replace(
$sln,
' GlobalSection\(SolutionConfigurationPlatforms\) = preSolution[\s\S]*? EndGlobalSection',
$solutionConfig
)
$sln = [regex]::Replace(
$sln,
' GlobalSection\(ProjectConfigurationPlatforms\) = postSolution[\s\S]*? EndGlobalSection',
$projectConfig
)
Set-Content $slnPath $sln -Encoding UTF8
mkdir "$Root\.template.config"
@"
{
"`$schema": "http://json.schemastore.org/template",
"author": "MyCompany",
"classifications": [ "WPF", "MVVM", "CommunityToolkit" ],
"identity": "$ProjectName",
"name": "MyCompany WPF MVVM App",
"shortName": "$ShortName",
"sourceName": "$ProjectName",
"preferNameDirectory": true,
"tags": {
"language": "C#",
"type": "project"
}
}
"@ | Set-Content "$Root\.template.config\template.json" -Encoding UTF8
dotnet build "$Root\$ProjectName.sln" -c Debug_Net8 -p:Platform=x64
dotnet build "$Root\$ProjectName.sln" -c Release_Net8 -p:Platform=x64
dotnet build "$Root\$ProjectName.sln" -c Debug_Net10 -p:Platform=x64
dotnet build "$Root\$ProjectName.sln" -c Release_Net10 -p:Platform=x64
dotnet new install $Root --force
dotnet new list $ShortName
実行はこうです。
powershell -ExecutionPolicy Bypass -File .\Create-WpfMvvmTemplate.ps1
これで一連の作成・構成・ビルド確認・テンプレート登録まで自動化できます。
・・・なんか、最初からこうこうしたいから、PowerShellのバッチを作ってって言ってればよかったの?
すごく脱力・・・
なので
↓
- ここまでのやり取りを、この結末まで持っていくために、私がどうあなたにプロンプトを書けばよかったか、プロンプトを出して
# Visual Studio用 WPF + CommunityToolkit.Mvvm テンプレートを PowerShellだけで完全自動構築したい
以下の条件を満たすように、PowerShellで1コマンドずつ実行可能な形で、最終的には `.ps1` ファイル化できるレベルまで含めて、完全な手順を作成してください。
## やりたいこと
* Visual Studio用のProject Templateを作りたい
* ベースはWPFアプリケーション
* CommunityToolkit.Mvvm を導入する
* MVVMサンプルコードを最初から含めたい
* MainWindow.xaml と MainWindowViewModel.cs を生成したい
* Button + Binding + RelayCommand のサンプルを入れたい
## ソリューション構成
* src配下にプロジェクトを置きたい
* slnファイルは1階層上に置きたい
* 複数プロジェクト構成へ拡張しやすい形にしたい
希望構成:
<ProjectRoot>
├─ MyCompany.Wpf.Mvvm.Template.sln
├─ src
│ └─ MyCompany.Wpf.Mvvm.Template
├─ test
├─ tools
└─ docs
## ビルド構成
Visual Studioの構成ドロップダウンで以下を選択できるようにしたい。
* Debug_Net8
* Release_Net8
* Debug_Net10
* Release_Net10
さらに:
* x64固定
* x86 / AnyCPU は不要
* net8.0-windows
* net10.0-windows
を使いたい。
## 重要
* `.csproj` だけでなく `.sln` 側の設定も含めてください
* Visual Studioの「構成マネージャー」を手作業で触らなくて済むようにしてください
* `.sln` をPowerShellで自動編集する方法を含めてください
## テンプレート化
* `.template.config/template.json` を生成したい
* `dotnet new install . --force` で更新可能にしたい
* `dotnet new my-wpf-mvvm` で生成できるようにしたい
## 出力形式
以下の順番で出してください。
1. PowerShellを1コマンドずつ
2. 最終的な `.csproj`
3. 最終的な `.sln` の変更内容
4. 最終的な `.ps1` 完成版
5. 実行コマンド例
6. よくハマるポイント
## 注意
* diff形式ではなく、全文置換形式で出してください
* 「どこに入れるか」を曖昧にしないでください
* 実行順に並べてください
* Visual Studio 2026 / .NET 10 SDK 前提でお願いします
あ、プロンプトじゃなくて、仕様書みたいね・・・
思ったこと
五月雨に聞いていくと、ChatGPTはハマりやすいです。
私のChatGPTは「仕様書を書く」に特化した性格設定をしているため、
上記のような文書を書くと仕様っぽいプロンプトを出してきます。
最初からこうしておけばよかった。。。
最後に
なお、この記事自体も8割くらいはChatGPTに頑張ってもらっています。