前回までの概要
前回まででタスク管理アプリの大枠作成、ブラッシュアップをしました。
今回からは新機能の追加を実施します。
プロンプト
以下の機能(画面)を追加したいので、コードを出力してください。
・追加する機能
・マスタ管理機能
・新規作成
・担当者、カテゴリ、工程をマスタ管理
・wbs画面カラム修正機能
・新規作成
・現在wbs画面の項目は以下である
タスク,開始予定,終了予定,開始実績,終了実績,進捗,担当者,予定工数,実績工数,残工数,遅延,カテゴリ,工程,日付
上記以外にもタスクの保持する情報がある。
→ユーザにより表示する項目とその順番を決めることができるよう、管理画面を追加したい。
・機能
・画面内容:一覧(datagrid等),項目移動ボタン(上下),削除ボタン,更新ボタン,キャンセルボタン
一覧内項目は以下
→No(label),固定(チェックボックス),項目(リスト)
・初期表示:一覧へ現在のwbs表示項目が表示される。
・修正:項目を選択、上下ボタン(ドラッグ&ドロップ)により順番を入れ替える。
削除ボタンより行を削除する。
「項目」のリストより機能を選択し、変更する。
→項目一覧はタスクの持つ全項目(複数持つ項目は除く)に加え、No項目(行数)を追加する。(一番上に追加)
更新ボタン押下で更新、キャンセルボタンでキャンセルを行う。(共に警告メッセージ出力)
回答
# 追加方針
以下 2 機能を追加します。
| 機能 | 内容 |
|---|---|
| マスタ管理 | 担当者、カテゴリ、工程 |
| WBSカラム管理 | 表示項目、順序、固定列制御 |
追加構成
Forms
├─ MasterManageForm.cs
├─ MasterManageForm.Designer.cs
├─ WbsColumnSettingForm.cs
├─ WbsColumnSettingForm.Designer.cs
Domain
├─ MasterData.cs
├─ WbsColumnSetting.cs
Services
├─ MasterService.cs
├─ WbsColumnSettingService.cs
1. マスタ管理機能
新規追加 MasterData.cs
namespace TaskManager.Domain
{
public class MasterData
{
public List<string> Assignees
{
get;
set;
} = new();
public List<string> Categories
{
get;
set;
} = new();
public List<string> Processes
{
get;
set;
} = new();
}
}
新規追加 MasterService.cs
using System.Text.Json;
using TaskManager.Domain;
namespace TaskManager.Services
{
public class MasterService
{
private const string FILE_PATH =
"master.json";
public MasterData Load()
{
if (!File.Exists(FILE_PATH))
{
return new MasterData();
}
string json =
File.ReadAllText(FILE_PATH);
return JsonSerializer.Deserialize<MasterData>(json)
?? new MasterData();
}
public void Save(MasterData data)
{
string json =
JsonSerializer.Serialize(
data,
new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(
FILE_PATH,
json);
}
}
}
新規追加 MasterManageForm.Designer.cs
private TabControl tabMaster;
private TabPage tabAssignee;
private TabPage tabCategory;
private TabPage tabProcess;
private ListBox lstAssignee;
private ListBox lstCategory;
private ListBox lstProcess;
private Button btnAdd;
private Button btnDelete;
private Button btnSave;
private Button btnCancel;
InitializeComponent()
tabMaster = new TabControl();
tabAssignee = new TabPage("担当者");
tabCategory = new TabPage("カテゴリ");
tabProcess = new TabPage("工程");
lstAssignee = new ListBox();
lstCategory = new ListBox();
lstProcess = new ListBox();
btnAdd = new Button();
btnDelete = new Button();
btnSave = new Button();
btnCancel = new Button();
tabMaster.Dock = DockStyle.Top;
tabMaster.Height = 400;
lstAssignee.Dock = DockStyle.Fill;
lstCategory.Dock = DockStyle.Fill;
lstProcess.Dock = DockStyle.Fill;
tabAssignee.Controls.Add(lstAssignee);
tabCategory.Controls.Add(lstCategory);
tabProcess.Controls.Add(lstProcess);
tabMaster.TabPages.Add(tabAssignee);
tabMaster.TabPages.Add(tabCategory);
tabMaster.TabPages.Add(tabProcess);
btnAdd.Text = "追加";
btnDelete.Text = "削除";
btnSave.Text = "更新";
btnCancel.Text = "キャンセル";
btnAdd.Location = new Point(20, 420);
btnDelete.Location = new Point(120, 420);
btnSave.Location = new Point(220, 420);
btnCancel.Location = new Point(320, 420);
Controls.Add(tabMaster);
Controls.Add(btnAdd);
Controls.Add(btnDelete);
Controls.Add(btnSave);
Controls.Add(btnCancel);
ClientSize = new Size(500, 500);
新規追加 MasterManageForm.cs
using Microsoft.VisualBasic;
using TaskManager.Domain;
using TaskManager.Services;
namespace TaskManager.Forms
{
public partial class MasterManageForm : Form
{
private readonly MasterService _service;
private MasterData _data;
public MasterManageForm()
{
InitializeComponent();
_service = new MasterService();
_data = _service.Load();
LoadData();
btnAdd.Click += btnAdd_Click;
btnDelete.Click += btnDelete_Click;
btnSave.Click += btnSave_Click;
btnCancel.Click += btnCancel_Click;
}
private void LoadData()
{
lstAssignee.Items.Clear();
lstCategory.Items.Clear();
lstProcess.Items.Clear();
lstAssignee.Items.AddRange(
_data.Assignees.ToArray());
lstCategory.Items.AddRange(
_data.Categories.ToArray());
lstProcess.Items.AddRange(
_data.Processes.ToArray());
}
private ListBox CurrentList()
{
if (tabMaster.SelectedTab == tabAssignee)
{
return lstAssignee;
}
if (tabMaster.SelectedTab == tabCategory)
{
return lstCategory;
}
return lstProcess;
}
private void btnAdd_Click(
object sender,
EventArgs e)
{
string value =
Interaction.InputBox(
"追加する値を入力してください");
if (string.IsNullOrWhiteSpace(value))
{
return;
}
CurrentList().Items.Add(value);
}
private void btnDelete_Click(
object sender,
EventArgs e)
{
var list = CurrentList();
if (list.SelectedIndex < 0)
{
return;
}
list.Items.RemoveAt(
list.SelectedIndex);
}
private void btnSave_Click(
object sender,
EventArgs e)
{
if (MessageBox.Show(
"更新しますか?",
"確認",
MessageBoxButtons.YesNo)
!= DialogResult.Yes)
{
return;
}
_data.Assignees =
lstAssignee.Items
.Cast<string>()
.ToList();
_data.Categories =
lstCategory.Items
.Cast<string>()
.ToList();
_data.Processes =
lstProcess.Items
.Cast<string>()
.ToList();
_service.Save(_data);
MessageBox.Show("保存しました");
}
private void btnCancel_Click(
object sender,
EventArgs e)
{
if (MessageBox.Show(
"変更を破棄しますか?",
"確認",
MessageBoxButtons.YesNo)
!= DialogResult.Yes)
{
return;
}
_data = _service.Load();
LoadData();
}
}
}
2. WBSカラム設定機能
新規追加 WbsColumnSetting.cs
namespace TaskManager.Domain
{
public class WbsColumnSetting
{
public int No
{
get;
set;
}
public bool Fixed
{
get;
set;
}
public string ColumnName
{
get;
set;
} = string.Empty;
}
}
新規追加 WbsColumnSettingService.cs
using System.Text.Json;
using TaskManager.Domain;
namespace TaskManager.Services
{
public class WbsColumnSettingService
{
private const string FILE_PATH =
"wbs_columns.json";
public List<WbsColumnSetting> Load()
{
if (!File.Exists(FILE_PATH))
{
return CreateDefault();
}
string json =
File.ReadAllText(FILE_PATH);
return JsonSerializer.Deserialize<
List<WbsColumnSetting>>(json)
?? CreateDefault();
}
public void Save(
List<WbsColumnSetting> settings)
{
string json =
JsonSerializer.Serialize(
settings,
new JsonSerializerOptions
{
WriteIndented = true
});
File.WriteAllText(
FILE_PATH,
json);
}
private List<WbsColumnSetting>
CreateDefault()
{
string[] columns =
{
"タスク",
"開始予定",
"終了予定",
"開始実績",
"終了実績",
"進捗",
"担当者",
"予定工数",
"実績工数",
"残工数",
"遅延",
"カテゴリ",
"工程"
};
return columns
.Select((x, i)
=> new WbsColumnSetting
{
No = i + 1,
Fixed = true,
ColumnName = x
})
.ToList();
}
}
}
新規追加 WbsColumnSettingForm.Designer.cs
private DataGridView dgvColumns;
private Button btnUp;
private Button btnDown;
private Button btnDelete;
private Button btnSave;
private Button btnCancel;
InitializeComponent()
dgvColumns =
new DataGridView();
btnUp = new Button();
btnDown = new Button();
btnDelete = new Button();
btnSave = new Button();
btnCancel = new Button();
dgvColumns.Dock =
DockStyle.Top;
dgvColumns.Height = 400;
dgvColumns.AllowUserToAddRows = false;
dgvColumns.Columns.Add(
"No",
"No");
var fixedCol =
new DataGridViewCheckBoxColumn();
fixedCol.Name = "Fixed";
fixedCol.HeaderText = "固定";
dgvColumns.Columns.Add(fixedCol);
var combo =
new DataGridViewComboBoxColumn();
combo.Name = "ColumnName";
combo.HeaderText = "項目";
combo.Items.AddRange(
new object[]
{
"No",
"タスク",
"開始予定",
"終了予定",
"開始実績",
"終了実績",
"進捗",
"担当者",
"予定工数",
"実績工数",
"残工数",
"遅延",
"カテゴリ",
"工程"
});
dgvColumns.Columns.Add(combo);
btnUp.Text = "↑";
btnDown.Text = "↓";
btnDelete.Text = "削除";
btnSave.Text = "更新";
btnCancel.Text = "キャンセル";
btnUp.Location = new Point(20, 420);
btnDown.Location = new Point(100, 420);
btnDelete.Location = new Point(180, 420);
btnSave.Location = new Point(280, 420);
btnCancel.Location = new Point(380, 420);
Controls.Add(dgvColumns);
Controls.Add(btnUp);
Controls.Add(btnDown);
Controls.Add(btnDelete);
Controls.Add(btnSave);
Controls.Add(btnCancel);
ClientSize = new Size(700, 500);
新規追加 WbsColumnSettingForm.cs
using TaskManager.Domain;
using TaskManager.Services;
namespace TaskManager.Forms
{
public partial class WbsColumnSettingForm : Form
{
private readonly
WbsColumnSettingService _service;
private List<WbsColumnSetting>
_settings;
public WbsColumnSettingForm()
{
InitializeComponent();
_service =
new WbsColumnSettingService();
_settings =
_service.Load();
LoadData();
btnUp.Click += btnUp_Click;
btnDown.Click += btnDown_Click;
btnDelete.Click += btnDelete_Click;
btnSave.Click += btnSave_Click;
btnCancel.Click += btnCancel_Click;
}
private void LoadData()
{
dgvColumns.Rows.Clear();
foreach (var s in _settings)
{
dgvColumns.Rows.Add(
s.No,
s.Fixed,
s.ColumnName);
}
}
private void btnUp_Click(
object sender,
EventArgs e)
{
MoveRow(-1);
}
private void btnDown_Click(
object sender,
EventArgs e)
{
MoveRow(1);
}
private void MoveRow(int offset)
{
if (dgvColumns.CurrentRow == null)
{
return;
}
int index =
dgvColumns.CurrentRow.Index;
int newIndex =
index + offset;
if (newIndex < 0 ||
newIndex >= dgvColumns.Rows.Count)
{
return;
}
var row =
dgvColumns.Rows[index];
dgvColumns.Rows.RemoveAt(index);
dgvColumns.Rows.Insert(
newIndex,
row);
dgvColumns.CurrentCell =
dgvColumns.Rows[newIndex]
.Cells[0];
}
private void btnDelete_Click(
object sender,
EventArgs e)
{
if (dgvColumns.CurrentRow == null)
{
return;
}
dgvColumns.Rows.Remove(
dgvColumns.CurrentRow);
}
private void btnSave_Click(
object sender,
EventArgs e)
{
if (MessageBox.Show(
"更新しますか?",
"確認",
MessageBoxButtons.YesNo)
!= DialogResult.Yes)
{
return;
}
_settings.Clear();
for (int i = 0;
i < dgvColumns.Rows.Count;
i++)
{
var row =
dgvColumns.Rows[i];
_settings.Add(
new WbsColumnSetting
{
No = i + 1,
Fixed =
Convert.ToBoolean(
row.Cells["Fixed"]
.Value),
ColumnName =
row.Cells["ColumnName"]
.Value?
.ToString()
?? ""
});
}
_service.Save(_settings);
MessageBox.Show("保存しました");
}
private void btnCancel_Click(
object sender,
EventArgs e)
{
if (MessageBox.Show(
"変更を破棄しますか?",
"確認",
MessageBoxButtons.YesNo)
!= DialogResult.Yes)
{
return;
}
_settings =
_service.Load();
LoadData();
}
}
}
WBS側反映
WbsForm.cs の InitializeGrid() を、
固定実装
↓
設定ファイル読込型
へ変更します。
追加
private readonly
WbsColumnSettingService
_columnService =
new();
private List<WbsColumnSetting>
_columnSettings;
コンストラクタ
_columnSettings =
_columnService.Load();
InitializeGrid() 全変更
dgvWbs.Columns.Clear();
foreach (var setting
in _columnSettings)
{
dgvWbs.Columns.Add(
setting.ColumnName,
setting.ColumnName);
dgvWbs.Columns[
setting.ColumnName]
.Frozen = setting.Fixed;
}
実装、起動確認
マスタ管理
WBSカラム設定
まとめ
WBSの実用化も一旦完了かと思います。
次回は一覧画面への反映を行っていきます。


