5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WPFでアプリを作ってみた①(環境構築~簡易版アプリ作成まで)

Last updated at Posted at 2026-01-24

はじめに

現在参画中の案件にて初めてC#やWPFを触る機会があったため、自己学習として簡単なWPFアプリを作成しています
まだ開発途中ですが、途中経過を記事に残しておこうと思い記事を作成しました
色々な技術へ触れて知識を増やすことが今回の学習の目的なので、作成したいものに対して適切な環境でないかもしれませんがご容赦ください。。。


■今回行うこと

  • 環境構築(dockerメイン)
  • 簡易的な機能の実装
  • 動作確認

作成したアプリの概要

画面に入力した内容をExcelに出力します
現在はA1セルへ固定出力していますが、将来的に機能を拡張しより柔軟な出力処理が行えるようにできたらと思っています

入力画面
image.png

出力したExcel
image.png

環境構築

今回の環境は下記の通りです
※現在の簡易版アプリではDBは使用していませんが、今後追加予定の機能にてDBが必要なためDB環境も導入しておきます

分類 使用するもの
IDE Visual Studio 2022
フロント WPF (.NET)
DB PostgreSQL
ORM Entity Framework Core
マイグレーション Flyway
仮想化 Docker / Docker Compose
バージョン管理 Git

全体構成
WPFアプリ
↓ EF Core
PostgreSQL(Docker)
↑ Flyway

  • DBはDockerコンテナ上に構築
  • DB定義はFlywayのSQLでバージョン管理
  • アプリ側はEF Core(DBファースト)でアクセス

VS2022、DockerDesktop、Gitのインストールについては今回は省略し、dockerの設定周りについて書いていきます

ちなみにGitのインストールについては以前投稿した以下の記事で触れているので、よければご覧いただけますと幸いです😊(宣伝です)
VSCodeとgit(gitHub)を連携してみた

wpfプロジェクトの作成

VS2022で新しいプロジェクトの作成⇒WPFアプリケーション⇒プロジェクト名を入力し、.NETバージョンを選択して作成

Docker + PostgreSQL + Flyway環境の構築

docker関連のディレクトリ構成

docker関連のファイルはアプリ用のファイルとは分けたいので、dockerフォルダへまとめる構成にしました

docker
├ docker-compose.yaml
└ sql
 └ V1__create_tables.sql

  • docker-compose.yaml
    PostgreSQL / Flywayコンテナの設定を記載
  • sql配下
    Flywayが実行するマイグレーション用SQLを配置

docker-compose.yamlの作成

dockerの設定ファイルです
記載内容は作成したい環境に合わせて変更してください
flyway:~の部分はflyway用の設定のため、flywayを導入しない場合は不要です

docker-compose:yaml
version: "3.9"
services:
  db:
    image: postgres:15
    container_name: postgres-db
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: sampleuser
      POSTGRES_PASSWORD: samplepass
      POSTGRES_DB: sampledb
    volumes:
        - db_data:/var/lib/postgresql/data
    flyway:
      image: flyway/flyway:10
      container_name: flyway
      command: -url=jdbc:postgresql://db:5432/sampledb -user=sampleuser -password=samplepass -connectRetries=60 migrate
      volumes:
        - ./sql:/flyway/sql
      depends_on:
        - db
volumes:
  db_data:

※ちなみに最近のdockerではversion指定を省略しても動くみたい?です

flyway用のsql作成

flywayを導入すると、DBのテーブル定義やデータ変更をSQLファイルとしてバージョン管理できるようになります

  • SQLファイルの履歴=DBの変更履歴として管理できる
  • 新しい環境でも同じSQLを流すだけでDBを再現できる

まずは初期テーブル作成用のsqlを配置します(テスト用の仮テーブルです)

flyway
└ sql
 └ V1__create_tables.sql

CREATE TABLE Customers (
   Id SERIAL PRIMARY KEY,
   Name VARCHAR(100) NOT NULL
);

INSERT INTO Customers (Name) VALUES ('Alice'), ('Bob'), ('Charlie');

コンテナ起動

DockerDesktopを起動し、docker compose up -dを実行するとyamlファイルに記載したポスグレのコンテナを立ち上げることができます
この際、flywayが自動でSQLを実行しDBにテーブルが作成されます

毎回コマンドを打つのは手間なので、バッチファイルにしておくと起動が楽です

EF Coreの導入

Entity Framework Coreの略称
C#からデータベースを操作するためのORM(ObjectRelationalMapper)です

SQLを直接書かなくても、テーブル=クラス、レコード=オブジェクトとして扱うことができるためアプリ側の実装がシンプルになります

今回はDB定義からEFのクラスを生成します
(DBファースト方式というらしい)

※参考:【C#】Entity Framework Core(EF Core)について

NuGetパッケージ追加

Vs2022のパッケージマネージャーコンソールから以下をインストールします

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Tools

DbContext / エンティティの生成(Scaffoldコマンド)

Scaffoldコマンドを実行すると、DBからEFのクラスを作成することができます

  • DbContext クラス
  • テーブルに対応したエンティティクラス

OutputDir :生成したファイルの配置先
Context :DbContextクラスの名前

コマンド例
Scaffold-DbContext
Host=localhost;Port=5432;Database=sampledb;Username=sampleuser;Password=samplepass
Npgsql.EntityFrameworkCore.PostgreSQL
-OutputDir Models
-Context AppDbContext

こちらもバッチファイルにしておくと実行が楽です
(中身は環境に合わせて変えてください)

@echo off
REM =========================================
REM EF Core Scaffold 実行バッチ
REM =========================================

REM 環境に合わせてcdコマンドを設定(.csprojがあるフォルダに移動)
cd XXX

REM 接続文字列を設定(自分の環境に合わせて変更)
set CONNECTION="Host=XXXX;Port=XXXX;Database=XXXX;Username=XXXX;Password=XXXX"

REM 使用するプロバイダ
set PROVIDER=Npgsql.EntityFrameworkCore.PostgreSQL

REM 出力先フォルダ
set OUTPUT_DIR=ModelsGenerated

REM DbContext のクラス名
set CONTEXT=AppDbContext

echo Running scaffold...
dotnet ef dbcontext scaffold %CONNECTION% %PROVIDER% -o %OUTPUT_DIR% --context %CONTEXT% --force

echo.
echo Scaffold 完了しました。
pause

DBの接続テスト

先ほどのScaffoldコマンドを実行すると、以下のようなファイルが出力されます

DbContextクラス
using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseNpgsql("Host=localhost;Port=5432;Database=sampledb;Username=sampleuser;Password=samplepass");
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

DBに接続できているかテストするために、レコード数を表示するプログラムを作成します

MainWindow.xaml.cs
using WPFSampleProject.Models;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System;
using System.IO;
using System.Windows;
using ClosedXML.Excel;

namespace WPFSampleProject
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //DB接続テスト用
            using var db = new AppDbContext();
            var tables = db.Customers.ToList();

            MessageBox.Show($"レコード数: {tables.Count}");
        }
    }
}

メッセージボックスにレコード数が表示されたらOKです
今回は先ほど作成したsqlファイルで3件データをinsertしているので「レコード数:3」と表示されています
image.png

簡易版のアプリ作成

環境の構築ができたので、ここからはアプリの作成を行います

プロジェクト構成

今回はMVVM構成で作成する方針にしたいと思っています

  • View
    • 画面の見た目を定義(xaml)
    • ユーザー操作を受け付けるが、処理は持たない
  • ViewModel
    • 画面の状態や処理ロジックを定義
    • ViewとはBinding / Commandを通して連携
  • Model
    • アプリケーションで扱うデータ構造(EFのエンティティクラスなど)
WPFSampleProject
├ docker
│  ├ docker-compose.yaml
│  ├ sql
│  │  └ V1__create_tables.sql
│  └ scaffold.bat
└ WPFSampleProject
   ├ Views
   │  └ MainWindow.xaml
   ├ ViewModels
   │  └ MainWindowViewModel.cs
   ├ Models
   ├ ModelsGenerated
   │  ├ AppDbContext.cs
   │  └ Customer.cs
   └ WPFSampleProject.csproj

MainWindowViewModel.cs

using WPFSampleProject.Models;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System;
using System.IO;
using System.Windows;
using ClosedXML.Excel;
using System.ComponentModel.DataAnnotations.Schema;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace WPFSampleProject.ViewModels
{
    public partial class MainWindowViewModel : ObservableObject
    {
        [ObservableProperty]
        public string _inputTextBox = "";

        [RelayCommand]
        private void OutputButtonClick()
        {
            try
            {
                string text = InputTextBox;

                if (string.IsNullOrWhiteSpace(text))
                {
                    MessageBox.Show("文字を入力してください");
                    return;
                }

                // 出力先(実行フォルダ)
                string outputDir = AppDomain.CurrentDomain.BaseDirectory;
                string fileName = $"Sample_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
                string filePath = System.IO.Path.Combine(outputDir, fileName);

                // Excel作成
                using (var workbook = new XLWorkbook())
                {
                    var sheet = workbook.Worksheets.Add("Sheet1");
                    sheet.Cell("A1").Value = text;

                    workbook.SaveAs(filePath);
                }

                MessageBox.Show("Excel出力が完了しました。\n" + filePath);
            }
            catch (Exception ex)
            {
                MessageBox.Show("出力に失敗しました。\n" + ex.Message);
            }
        }
    }
}

MainWindow.xaml

<Window x:Class="WPFSampleProject.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFSampleProject"
        xmlns:vm="clr-namespace:WPFSampleProject.ViewModels" 
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <TextBox x:Name="InputTextBox"
                 Height="30"
                 Margin="0,0,0,10"
                 Text="{Binding InputTextBox}"/>

        <Button Grid.Row="1"
                Height="30"
                Content="Excelに出力"
                Command="{Binding OutputButtonClickCommand}"/>
    </Grid>
</Window>

実装のポイント

画面(xaml)の入力値をViewModelに渡す

WPFではBindingを使って値の受け渡しを行います
今回はテキストボックスの値をvmのInputTextBoxプロパティにバインドしています

xaml
<TextBox
    Height="30"
    Margin="0,0,0,10"
    Text="{Binding InputTextBox}" />
vm
[ObservableProperty]
private string _inputTextBox = "";

[ObservableProperty]はCommunityToolkit.Mvvmというライブラリが提供する機能です
WPFでは「Binding設定+変更通知の設定」を行うことで変更通知がきたときに値の受け渡しをする…という仕組みになっているそうです
[ObservableProperty]をつけておくとこの変更通知の設定を自動で行ってくれます

※vmにObservableObjectを継承させる必要があります(public partial class MainWindowViewModel : ObservableObjectの部分)
※ライブラリを使わず自分で変更通知の設定を書くこともできます

画面(xaml)でボタンが押下された時のイベントをvmで処理する

xaml
<Button
    Height="30"
    Content="Excelに出力"
    Command="{Binding OutputButtonClickCommand}" />
vm
[RelayCommand]
private void OutputButtonClick()
{
    // Excel出力処理
}

[RelayCommand]もCommunityToolkit.Mvvmが提供している機能です
これにより、xaml.csにClickイベントを書くことなくボタン押下時の処理をvmに集約できるので

  • View(xaml)は見た目と操作のみを担当
  • ViewModel は処理ロジックのみを担当

というMVVMの責務分離を意識した構成にすることができます

最後に

ここまで読んでいただきありがとうございます

Javaを自己学習で少しだけ触ったことがあったので、View / ViewModelを分離する設計思想はController / Serviceの分離と似ているのかなと感じました。BindingやCommandの設定が難しく苦戦したので、値渡しなどの仕組みは今後も意識して学習していきたいです

EntityFrameworkの使い方にもあまり慣れていないので、DB関連の操作もこれから学習していけたらと思います

最後に備忘として今後対応したいことだけメモして終わります

  • 機能拡張
  • Excel操作に使用するライブラリの精査
    • 今回は(chatGPTにおすすめされたので)ClosedXMLを使用しましたが、各ライブラリのメリットやデメリットを調べて適切なものを選べるようにしたいです
  • DB接続文字列の分離
    • 今は簡易さと分かりやすさ重視で各所に直書きしていますが、実際はセキュリティ面や一元管理の観点から分離するのが一般的なので修正したいです
5
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?