レシピ管理アプリにおける複数フォームでのデータ共有方法

2024年9月3日

本技術資料では、C#を使用して作成するWindows Forms (WinForm) アプリケーションにおいて、複数のフォーム間でデータ(レシピリスト)を共有する方法について解説します。特に初学者を対象に、複数の実装方法をわかりやすく紹介します。

目的

複数のフォーム間で共通のデータ(例:レシピリスト)を管理・利用する際に、効果的な設計パターンを理解し、実装することを目的とします。

使用する技術

  • シングルトンパターン
  • 依存性注入(DI: Dependency Injection)
  • 静的クラスと静的フィールド
  • イベントハンドリング

1. シングルトンパターンを使用する方法

1.1 シングルトンクラスの作成

シングルトンパターンを使用すると、アプリケーション内で1つのインスタンスしか存在しないクラスを作成し、そのインスタンスを通じてデータを管理することができます。

public class RecipeManager
{
    private static RecipeManager instance;
    public List<Recipe> Recipes { get; private set; }

    private RecipeManager()
    {
        Recipes = new List<Recipe>();
    }

    public static RecipeManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new RecipeManager();
            }
            return instance;
        }
    }
}

1.2 フォームでの使用方法

シングルトンクラスを利用して、各フォームでレシピリストにアクセスできます。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        
        // レシピリストを取得して表示
        var recipes = RecipeManager.Instance.Recipes;
        foreach (var recipe in recipes)
        {
            listBoxRecipes.Items.Add(recipe.Name);
        }
    }
}

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
        
        // レシピリストに新しいレシピを追加
        RecipeManager.Instance.Recipes.Add(new Recipe { Name = "New Recipe" });
    }
}

2. 依存性注入(DI)を使用する方法

2.1 コンストラクタでのレシピリストの受け渡し

依存性注入を利用することで、コンストラクタを通じて必要なデータを各フォームに渡すことができます。

public partial class Form1 : Form
{
    private List<Recipe> _recipes;

    public Form1(List<Recipe> recipes)
    {
        InitializeComponent();
        _recipes = recipes;

        foreach (var recipe in _recipes)
        {
            listBoxRecipes.Items.Add(recipe.Name);
        }
    }
}

public partial class Form2 : Form
{
    private List<Recipe> _recipes;

    public Form2(List<Recipe> recipes)
    {
        InitializeComponent();
        _recipes = recipes;

        _recipes.Add(new Recipe { Name = "New Recipe" });
    }
}

2.2 フォームの呼び出し例

public partial class MainForm : Form
{
    private List<Recipe> recipes = new List<Recipe>();

    public MainForm()
    {
        InitializeComponent();
        
        recipes.Add(new Recipe { Name = "Sample Recipe" });

        var form1 = new Form1(recipes);
        form1.Show();

        var form2 = new Form2(recipes);
        form2.Show();
    }
}

3. 静的フィールドを使用する方法

3.1 静的クラスの作成

静的フィールドを利用すると、アプリケーション全体でデータを共有することができます。

public static class RecipeRepository
{
    public static List<Recipe> Recipes { get; set; } = new List<Recipe>();
}

3.2 フォームでの使用方法

各フォームで静的クラスのフィールドを利用してレシピリストにアクセスします。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        foreach (var recipe in RecipeRepository.Recipes)
        {
            listBoxRecipes.Items.Add(recipe.Name);
        }
    }
}

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();

        RecipeRepository.Recipes.Add(new Recipe { Name = "New Recipe" });
    }
}

4. イベントを使用してリストの変更を通知する方法

4.1 イベントの追加

レシピリストが変更された際に、他のフォームに通知するためのイベントをシングルトンクラスに追加します。

public class RecipeManager
{
    private static RecipeManager instance;
    public List<Recipe> Recipes { get; private set; }

    public event Action RecipesUpdated;

    private RecipeManager()
    {
        Recipes = new List<Recipe>();
    }

    public static RecipeManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new RecipeManager();
            }
            return instance;
        }
    }

    public void AddRecipe(Recipe recipe)
    {
        Recipes.Add(recipe);
        RecipesUpdated?.Invoke();
    }
}

4.2 フォームでのイベントハンドリング

フォームでイベントを購読し、リストの変更を反映します。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        RecipeManager.Instance.RecipesUpdated += OnRecipesUpdated;
        UpdateRecipeList();
    }

    private void OnRecipesUpdated()
    {
        UpdateRecipeList();
    }

    private void UpdateRecipeList()
    {
        listBoxRecipes.Items.Clear();
        foreach (var recipe in RecipeManager.Instance.Recipes)
        {
            listBoxRecipes.Items.Add(recipe.Name);
        }
    }
}

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();

        RecipeManager.Instance.AddRecipe(new Recipe { Name = "New Recipe" });
    }
}

結論

複数のフォームでデータを共有する方法はいくつかありますが、それぞれの方法には利点と欠点があります。シングルトンパターンや静的フィールドは手軽に実装できますが、大規模なプロジェクトでは依存性注入(DI)やイベントハンドリングの方が柔軟性と拡張性を提供します。アプリケーションの要件に応じて、最適な方法を選択してください。

UML図

クラス図

クラスの関係やメソッド、プロパティを視覚的に表現することができます。

シーケンス図

シーケンス図は、Form1がレシピを検索する操作を行うシナリオを示しています。

シーケンス図 は、ユーザーが検索ボタンをクリックしてレシピを検索するシナリオを表現しています。ユーザーが検索ボタンをクリックすると、Form1はデータをロードし、検索結果を表示します。

アクティビティ図

レシピの追加フローを表すアクティビティ図です。

アクティビティ図 は、ユーザーが新しいレシピを追加するフローを示します。ユーザーがすべての入力を正しく行った場合、レシピは保存され、リストビューが更新されます。入力に不備がある場合は、エラーメッセージが表示されます。

状態図

Recipeオブジェクトの状態遷移を表す状態図

状態図 は、Recipeオブジェクトの状態遷移を表現しています。レシピは新規作成から編集、保存、表示、削除といった一連の状態遷移を行います。