1
2

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 + SQLite + MVVM Toolkit】最小構成で登録・一覧アプリを作る

Posted at

🎯 この記事の目的

この記事では、WPF + SQLite + CommunityToolkit.Mvvm を使って
「名前と年齢を登録し、DataGridで一覧表示するアプリ」を
最小構成で実装する方法を紹介します。


💡 完成イメージ

  • 名前と年齢を入力して「登録」ボタンを押すとSQLiteに保存されます
  • 下のDataGridに保存済みのデータが一覧表示されます
  • MVVM構成 + DI対応(Microsoft.Extensions.DependencyInjection)

🧱 プロジェクト構成

WpfMinimalMvvm/
│ App.xaml
│ App.xaml.cs
│ MainWindow.xaml
│ MainWindow.xaml.cs
├─ Models/
│   └── Employee.cs
├─ IServices/
│   └── IEmployeeRepository.cs
├─ Services/
│   ├── SqliteEmployeeRepository.cs
│   ├── IWindowService.cs
│   └── WindowService.cs
└─ ViewModels/
    └── MainViewModel.cs

🧩 WpfMinimalMvvm.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows7.0</TargetFramework>
    <UseWPF>true</UseWPF>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
  </ItemGroup>
</Project>

🧩 App.xaml / App.xaml.cs

<Application x:Class="WpfMinimalMvvm.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WpfMinimalMvvm.IServices;
using WpfMinimalMvvm.Services;
using WpfMinimalMvvm.ViewModels;

namespace WpfMinimalMvvm
{
    public partial class App : Application
    {
        private ServiceProvider? _provider;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var services = new ServiceCollection();
            services.AddSingleton<IEmployeeRepository, SqliteEmployeeRepository>();
            services.AddSingleton<IWindowService, WindowService>();
            services.AddTransient<MainViewModel>();
            services.AddTransient<MainWindow>();

            _provider = services.BuildServiceProvider();
            var window = _provider.GetRequiredService<MainWindow>();
            window.Show();
        }
    }
}

🧩 Models/Employee.cs

namespace WpfMinimalMvvm.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public int Age { get; set; }
        public bool IsActive { get; set; }
    }
}

🧩 IServices/IEmployeeRepository.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using WpfMinimalMvvm.Models;

namespace WpfMinimalMvvm.IServices
{
    public interface IEmployeeRepository
    {
        Task<int> InsertAsync(Employee employee);
        Task<List<Employee>> GetAllAsync();
    }
}

🧩 Services/SqliteEmployeeRepository.cs

using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Threading.Tasks;
using WpfMinimalMvvm.IServices;
using WpfMinimalMvvm.Models;

namespace WpfMinimalMvvm.Services
{
  using System.Data.SQLite;
using System.IO;
using WpfMinimalMvvm.IServices;
using WpfMinimalMvvm.Models;

namespace WpfMinimalMvvm.Services
{
    public class SqliteEmployeeRepository : IEmployeeRepository
    {
        private readonly string _dbPath;

        public SqliteEmployeeRepository()
        {
            _dbPath = Path.Combine(Directory.GetCurrentDirectory(), "app.db");

            // 初回のみテーブル作成
            using (var cn = new SQLiteConnection($"Data Source={_dbPath};Version=3;"))
            {
                cn.Open();
                using (var cmd = cn.CreateCommand())
                {
                    cmd.CommandText = @"
                        CREATE TABLE IF NOT EXISTS Employees(
                          Id       INTEGER PRIMARY KEY AUTOINCREMENT,
                          Name     TEXT    NOT NULL,
                          Age      INTEGER NOT NULL,
                          IsActive INTEGER NOT NULL
                        );";
                    cmd.ExecuteNonQuery();
                }
            }
        }

        public async Task<int> InsertAsync(Employee employee)
        {
            if (string.IsNullOrWhiteSpace(employee.Name))
            {
                throw new ArgumentException("Name は必須です。");
            }

            using (var cn = new SQLiteConnection($"Data Source={_dbPath};Version=3;"))
            {
                await cn.OpenAsync();
                using (var cmd = cn.CreateCommand())
                {
                    cmd.CommandText = @"
                        INSERT INTO Employees (Name, Age, IsActive)
                        VALUES (@name, @age, @active);
                        SELECT last_insert_rowid();
                        ";
                    cmd.Parameters.AddWithValue("@name", employee.Name);
                    cmd.Parameters.AddWithValue("@age", employee.Age);
                    cmd.Parameters.AddWithValue("@active", employee.IsActive ? 1 : 0);

                    var idObj = await cmd.ExecuteScalarAsync();
                    return Convert.ToInt32(idObj);
                }
            }
        }

        public async Task<List<Employee>> GetAllAsync()
        {
            var list = new List<Employee>();

            using (var cn = new SQLiteConnection($"Data Source={_dbPath};Version=3;"))
            {
                await cn.OpenAsync();
                using (var cmd = cn.CreateCommand())
                {
                    cmd.CommandText = @"SELECT Id, Name, Age, IsActive FROM Employees ORDER BY Id;";
                    using (var rd = await cmd.ExecuteReaderAsync())
                    {
                        while (await rd.ReadAsync())
                        {
                            var e = new Employee
                            {
                                Id = rd.GetInt32(0),
                                Name = rd.GetString(1),
                                Age = rd.GetInt32(2),
                                IsActive = rd.GetInt32(3) == 1
                            };
                            list.Add(e);
                        }
                    }
                }
            }

            return list;
        }
    }
}


🧩 MainWindow.xaml

<Window x:Class="WpfMinimalMvvm.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SQLite Insert Sample" Height="480" Width="700">
    <DockPanel Margin="12">

        <TextBlock DockPanel.Dock="Top"
                   Text="{Binding Status}" Margin="0,0,0,8" />

        <StackPanel DockPanel.Dock="Top"
                    Orientation="Horizontal"
                    Margin="0,0,0,8">
            <StackPanel Width="200">
                <TextBlock Text="Name" />
                <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
            </StackPanel>

            <StackPanel Width="100" Margin="12,0,0,0">
                <TextBlock Text="Age" />
                <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" />
            </StackPanel>

            <CheckBox Content="Is Active"
                      IsChecked="{Binding IsActive}"
                      VerticalAlignment="Bottom"
                      Margin="12,0,0,0" />

            <Button Content="登録"
                    Command="{Binding SaveCommand}"
                    Width="100" Height="32"
                    Margin="12,0,0,0"
                    VerticalAlignment="Bottom" />
        </StackPanel>

        <DataGrid ItemsSource="{Binding Items}"
                  AutoGenerateColumns="True"
                  IsReadOnly="True" />

        <StatusBar DockPanel.Dock="Bottom" Margin="0,8,0,0">
            <TextBlock Text="{Binding Message}" />
        </StatusBar>
    </DockPanel>

</Window>

🧩 MainWindow.xaml.cs

using System.Windows;
using WpfMinimalMvvm.ViewModels;

namespace WpfMinimalMvvm
{
    public partial class MainWindow : Window
    {
        public MainWindow(MainViewModel vm)
        {
            InitializeComponent();
            DataContext = vm;
            Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            if (DataContext is MainViewModel vm)
            {
                vm.LoadCommand.Execute(null);
            }
        }
    }
}

✅ 動作手順

  1. この構成のままビルド・実行
  2. 「Name」「Age」を入力して「登録」
  3. DataGrid に一覧表示されます
  4. app.db が自動生成され、SQLiteでデータが永続化されます

🚀 まとめ

このサンプルは 最小のMVVM + DI + SQLite構成 です。
拡張する場合は以下のような機能を追加できます:

  • 更新・削除処理の追加
  • SubWindowをIWindowService経由で開く
  • 検索やフィルタリング機能
  • 非同期処理中のプログレス表示

💬 コメント・LGTM歓迎です!

1
2
0

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?