【Winform】中級:JSON ベースのタスク管理アプリの作成

2024年8月28日

このドキュメントでは、C# と WinForm (.NET Framework) を使用し、JSON を使ってデータを管理するタスク管理アプリを作成する方法を解説します。初学者でも理解しやすいよう、丁寧に手順を説明しています。

概要

このアプリは、DateTime をキーとする Dictionary でタスクを管理し、JSON ファイルを使用してデータの保存と読み込みを行います。アプリは、タスクの追加、編集、削除ができ、アプリを再起動してもデータが保持されます。UI は Visual Studio のビジュアルデザイナーを使って配置します。

プロジェクトのセットアップ

  1. Visual Studio の起動
    Visual Studio を開き、新しい「Windows フォームアプリケーション (.NET Framework)」プロジェクトを作成します。
  2. プロジェクトの命名
    プロジェクト名を「TaskManager」とし、Form1 の名前を TaskManagerForm に変更します。フォームのタイトル(Text プロパティ)も「タスク管理アプリ」に設定します。

UI の配置

ビジュアルデザイナーを使ったレイアウト設定

以下の手順に従って、UIコントロールをフォーム上に配置します。

  1. コントロールの追加と配置
    • Visual Studio のツールボックスから以下のコントロールをフォームにドラッグ&ドロップします。
      • TextBoxtaskNameTextBox): フォーム上部に配置し、タスク名を入力できるようにします。
      • MonthCalendarmonthCalendar): TextBox の下に配置し、タスクの期限をカレンダーで選択できるようにします。
      • ListViewtaskListView): MonthCalendar の下に配置し、タスク一覧を表示します。
  2. ListView の設定
    • ListView を選択し、Visual Studio のプロパティウィンドウで次の設定を行います。
      • ViewDetails に設定します。これにより、タスクの詳細がリスト形式で表示されます。
      • FullRowSelectTrue に設定します。これにより、行全体を選択できるようになります。
      • GridLinesTrue に設定します。これにより、リストにグリッドラインが表示され、項目を見やすくします。
      • ColumnsColumns プロパティをクリックし、「列の追加」ダイアログを開きます。以下の2つの列を追加します:
        1. Header Text: タスク名, Width: 150
        2. Header Text: 締切日, Width: 150
  3. ボタンの配置
    • ButtonaddButtondeleteButton)をフォームの適切な位置に配置します。ボタンの Dock 設定は不要です。ボタンはタスクの追加および削除を行うために使用されます。
  4. イベントハンドラーの設定
    • taskListView の SelectedIndexChanged イベントを設定し、項目が選択されたときに taskNameTextBox にタスク名が表示されるようにします。

各コントロールをビジュアルデザイナーで配置した場合、以下のように InitializeComponent メソッドが自動生成されます。

namespace TaskManager
{
    partial class TaskManagerForm
    {
        /// <summary>
        /// 必要なデザイナー変数です。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 使用中のリソースをすべてクリーンアップします。
        /// </summary>
        /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows フォーム デザイナーで生成されたコード

        /// <summary>
        /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディターで変更しないでください。
        /// </summary>
        private void InitializeComponent()
        {
            this.taskNameTextBox = new System.Windows.Forms.TextBox();
            this.monthCalendar = new System.Windows.Forms.MonthCalendar();
            this.addButton = new System.Windows.Forms.Button();
            this.deleteButton = new System.Windows.Forms.Button();
            this.taskListView = new System.Windows.Forms.ListView();
            this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // taskNameTextBox
            // 
            this.taskNameTextBox.Location = new System.Drawing.Point(240, 360);
            this.taskNameTextBox.Name = "taskNameTextBox";
            this.taskNameTextBox.Size = new System.Drawing.Size(300, 19);
            this.taskNameTextBox.TabIndex = 0;
            // 
            // monthCalendar
            // 
            this.monthCalendar.Location = new System.Drawing.Point(30, 271);
            this.monthCalendar.Name = "monthCalendar";
            this.monthCalendar.TabIndex = 1;
            this.monthCalendar.DateSelected += new System.Windows.Forms.DateRangeEventHandler(this.MonthCalendar_DateSelected);
            // 
            // addButton
            // 
            this.addButton.Location = new System.Drawing.Point(280, 400);
            this.addButton.Name = "addButton";
            this.addButton.Size = new System.Drawing.Size(100, 30);
            this.addButton.TabIndex = 3;
            this.addButton.Text = "追加";
            this.addButton.Click += new System.EventHandler(this.AddButton_Click);
            // 
            // deleteButton
            // 
            this.deleteButton.Location = new System.Drawing.Point(410, 400);
            this.deleteButton.Name = "deleteButton";
            this.deleteButton.Size = new System.Drawing.Size(100, 30);
            this.deleteButton.TabIndex = 5;
            this.deleteButton.Text = "削除";
            this.deleteButton.Click += new System.EventHandler(this.DeleteButton_Click);
            // 
            // taskListView
            // 
            this.taskListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.columnHeader3,
            this.columnHeader4});
            this.taskListView.FullRowSelect = true;
            this.taskListView.GridLines = true;
            this.taskListView.HideSelection = false;
            this.taskListView.Location = new System.Drawing.Point(30, 30);
            this.taskListView.Name = "taskListView";
            this.taskListView.Size = new System.Drawing.Size(500, 200);
            this.taskListView.TabIndex = 6;
            this.taskListView.UseCompatibleStateImageBehavior = false;
            this.taskListView.View = System.Windows.Forms.View.Details;
            this.taskListView.SelectedIndexChanged += new System.EventHandler(this.TaskListView_SelectedIndexChanged);
            // 
            // columnHeader3
            // 
            this.columnHeader3.Text = "タスク名";
            this.columnHeader3.Width = 350;
            // 
            // columnHeader4
            // 
            this.columnHeader4.Text = "締切日";
            this.columnHeader4.Width = 150;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(240, 340);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(42, 12);
            this.label1.TabIndex = 7;
            this.label1.Text = "タスク名";
            // 
            // TaskManagerForm
            // 
            this.ClientSize = new System.Drawing.Size(564, 461);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.taskListView);
            this.Controls.Add(this.taskNameTextBox);
            this.Controls.Add(this.monthCalendar);
            this.Controls.Add(this.addButton);
            this.Controls.Add(this.deleteButton);
            this.Name = "TaskManagerForm";
            this.Text = "タスク管理アプリ";
            this.ResumeLayout(false);
            this.PerformLayout();

        }


        #endregion
        private System.Windows.Forms.Button addButton;
        private System.Windows.Forms.Button deleteButton;
        private System.Windows.Forms.TextBox taskNameTextBox;
        private System.Windows.Forms.MonthCalendar monthCalendar;
        private System.Windows.Forms.ColumnHeader columnHeader1;
        private System.Windows.Forms.ColumnHeader columnHeader2;
        private System.Windows.Forms.ListView taskListView;
        private System.Windows.Forms.ColumnHeader columnHeader3;
        private System.Windows.Forms.ColumnHeader columnHeader4;
        private System.Windows.Forms.Label label1;
    }
}

ビジュアルデザイナーでの設定結果

これらの操作を行うことで、レイアウトが自動的に InitializeComponent メソッドに反映されます。特にコードを手動で編集する必要はありません。

タスクのデータ管理を JSON で行う

データモデルの作成

タスクを管理するために Dictionary<DateTime, string> を使用します。

コード例

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Windows.Forms;

namespace TaskManager
{
    public partial class TaskManagerForm : Form
    {
        private Dictionary<DateTime, string> tasks;
        private const string DataFileName = "tasks.json";

        public TaskManagerForm()
        {
            InitializeComponent();
            tasks = LoadTasks();
            UpdateTaskListView();
        }

        private void AddButton_Click(object sender, EventArgs e)
        {
            DateTime selectedDate = monthCalendar.SelectionStart;
            string taskName = taskNameTextBox.Text;

            tasks[selectedDate] = taskName;
            UpdateTaskListView();
            SaveTasks();
        }

        private void DeleteButton_Click(object sender, EventArgs e)
        {
            if (taskListView.SelectedIndices.Count > 0)
            {
                // ListView で選択された項目のインデックスを取得
                int index = taskListView.SelectedIndices[0];
                
                // 選択されたタスクの締切日を取得
                DateTime selectedDate = DateTime.Parse(taskListView.Items[index].SubItems[1].Text);
                
                // 締切日をキーに辞書からタスクを削除
                if (tasks.ContainsKey(selectedDate))
                {
                    tasks.Remove(selectedDate);
                }

                // ListView を更新し、TextBox をクリア
                UpdateTaskListView();
                taskNameTextBox.Clear();

                // 変更を保存
                SaveTasks();
            }
        }

        private void TaskListView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (taskListView.SelectedIndices.Count > 0)
            {
                int index = taskListView.SelectedIndices[0];
                DateTime selectedDate = DateTime.Parse(taskListView.Items[index].SubItems[1].Text);
                
                // テキストボックスにタスク名を表示
                taskNameTextBox.Text = taskListView.Items[index].SubItems[0].Text;
                
                // カレンダーに該当の日付を表示
                monthCalendar.SetDate(selectedDate);
            }
        }

        private void MonthCalendar_DateSelected(object sender, DateRangeEventArgs e)
        {
            DateTime selectedDate = e.Start;
            if (tasks.ContainsKey(selectedDate))
            {
                taskNameTextBox.Text = tasks[selectedDate];
            }
            else
            {
                taskNameTextBox.Clear();
            }
        }

        private void UpdateTaskListView()
        {
            taskListView.Items.Clear();
            
            // タスクを締切日でソート
            var sortedTasks = tasks.OrderBy(task => task.Key);

            foreach (var task in sortedTasks)
            {
                var item = new ListViewItem(task.Value);
                item.SubItems.Add(task.Key.ToShortDateString());
                taskListView.Items.Add(item);
            }
        }

        private void SaveTasks()
        {
            var options = new JsonSerializerOptions { WriteIndented = true };
            File.WriteAllText(DataFileName, JsonSerializer.Serialize(tasks, options));
        }

        private Dictionary<DateTime, string> LoadTasks()
        {
            if (File.Exists(DataFileName))
            {
                string jsonData = File.ReadAllText(DataFileName);
                return JsonSerializer.Deserialize<Dictionary<DateTime, string>>(jsonData);
            }
            return new Dictionary<DateTime, string>();
        }
    }
}

タスクの追加、編集、削除

1. タスクの追加

  • 操作TextBox にタスク名を入力し、MonthCalendar で日付を選択した後、追加 ボタンをクリックします。
  • 結果: 新しいタスクが tasks 辞書に追加され、ListView に表示されます。既存のタスクが選択された場合、そのタスクの内容が上書きされます。ListView に表示されるタスクは、締切日でソートされます。

2. タスクの編集

  • 操作ListView で編集したいタスクを選択すると、そのタスク名が TextBox に表示され、対応する日付が MonthCalendar に反映されます。TextBox の内容を変更し、必要であれば MonthCalendar で日付を変更した後、追加 ボタンをクリックします。
  • 結果: 既存のタスクの内容が更新されます。元のタスクは削除され、新しい情報でタスクが再登録されます。ListView は締切日でソートされます。

3. タスクの削除

  • 操作ListView で削除したいタスクを選択し、削除 ボタンをクリックします。
  • 結果: 選択されたタスクが tasks 辞書および ListView から削除されます。削除後、TextBox の内容もクリアされます。ListView は締切日でソートされます。

JSONを使ったデータの保存と読み込み

アプリケーションを再起動してもタスクが保持されるように、JSON形式でデータを保存・読み込みします。


まとめ

このドキュメントでは、C# と WinForm (.NET Framework) を使い、JSONを利用したタスク管理アプリの作成方法を解説しました。UIの配置は Visual Studio のビジュアルデザイナーを使用して整理し、データの管理には DateTime をキーとする Dictionary と JSON を用いました。初学者でも理解しやすい手順で、実際に使えるシンプルなアプリケーションを構築するスキルが身につきます。