前回で「DataGridViewから派生させた、idListクラス(idListコントロール)のアプリにおける使用の実際」を紹介しましたので、今回はそれが入っているIDList_CS.dllのソースコードであるIDList_CS.csを紹介します。(何れ字数オーバーになるのだろうと、最初から(1)にしてしまいました。)適宜解説文を入れておきますので、深堀りは今までのブログを振り返ってください。
【IDList_CS.cs】
///////////////////////////////
// IDList_CS.cs
// Copyright (c) 2026 by ysama
// https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.datagridview?view=windowsdesktop-10.0
///////////////////////////////
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; //[DisplayName()]使用の為
using System.IO; //StreamReader使用の為
using System.Text; //Encoding.GetEncoding使用の為
using System.Security.Cryptography; //データの暗号化の為
namespace IDList_CS
{
///////////////////////////////////
//IDListで使用するIDデータのクラス
///////////////////////////////////
public class idItem
{
//解説:これについては既に「【DataGridView】IDList for CS写真集」の「3.編集操作」で触れていますので省略します。
}
/////////////////////////////////////
//idListクラス(DataGridViewから派生)
/////////////////////////////////////
public class idList : DataGridView; //解説:これが今回の主役(心臓部)です。
{
//idItemのデータリスト(解説:idItemクラスインスタンスをListに入れて使います。)
List <idItem> idItems = new List <idItem> ();
//DataGridViewのバインディングデータソース(解説:今回初めて知りました。DataGridViewとListをつなぐインターフェースです。)
BindingSource bs = new BindingSource();
//システムデータファイルパス(解説:セキュリティ上はユーザーの好きなところに作らせ、そのパスも暗号化すべきでしょうが...)
const string dfPath = ".\\IDListFiles\\IDList.dat"; //解説:相対パスはChat-GPTに批判されましたが、これでいいです。
//コンテキストメニュー(解説:右クリックでポップアップさせるメニューです。)
ContextMenuStrip cms = new ContextMenuStrip();
//並び替え順(true - 昇順、false - 降順-解説:ソートの為にこれに記憶させておきます。)
bool sortOrder = true;
//コンストラクター
public idList()
{
//DataGridViewの設定
this.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.EditMode = DataGridViewEditMode.EditProgrammatically; //ダブルクリック編集不可(解説:セキュリティ目的で殺しました。)
this.SelectionMode = DataGridViewSelectionMode.FullRowSelect; //一行選択(飽くまでidItem単位です。)
this.AllowUserToAddRows = false; //末尾の空行禁止
bs.DataSource = idItems; //bsのデータソースにidItemsをセット(解説:上記参照)
this.DataSource = bs; //DataGridViewのデータソースにbsをセット(解説:Ditto)
this.DataBindingComplete += On_DataBindingComplete;
this.ColumnHeaderMouseClick += On_ColumnHeaderMouseClick;
this.CellDoubleClick += On_CellDoubleClick;
//コンテキストメニュー設定
cms.Items.Add("追加", null, AddItem_Click);
cms.Items.Add("検索", null, FindItem_Click);
cms.Items.Add("編集", null, EditItem_Click);
cms.Items.Add("削除", null, DeleteItem_Click);
this.ContextMenuStrip = cms;
//*.datファイル用ディレクトリーの作成(初回のみ-解説:既にある場合もエラーにはなりません。)
Directory.CreateDirectory(".\\IDListFiles");
//dfPathファイルのデータを読み込む
DataRead(dfPath, true); //dfPathを暗号化で読む
}
//サイズ変更時処理
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
//起動時はColumnCount == 0でエラーになる為(解説:利便性の反面、バインディングが遅いのが弱点で、タイミングを合わせることが大事です。)
if(this.ColumnCount > 3 && this.ClientSize.Width >= 560)
this.Columns[3].Width = this.ClientSize.Width - 560;
}
//終了時ファイル保存処理(解説:ウィンドウならOnClosingですが、コントロールには無いのでこれにしました。)
protected override void OnHandleDestroyed(EventArgs e)
{
if (this.IsCurrentCellInEditMode)
{
this.EndEdit(); //編集中データを確定させる
}
DataWrite(dfPath, true, true); //dfPathを暗号化、隠しファイルで保存
base.OnHandleDestroyed(e);
}
//DataGridViewのデータバインディング完了イベント(解説:これもデータバインディングが遅いので、タイミングを計る為に最適なイベントを探しました。)
private void On_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
//列幅の設定(解説:列が生成される迄、列数や列幅は設定できない<エラーが出ます>ので。)
this.Columns[0].Width = 240;
this.Columns[1].Width = 160;
this.Columns[2].Width = 160;
if(this.Width < 560) //「備考」幅は560以下にはしない(解説:Resizeイベントと同じです。)
this.Columns[3].Width = 560;
else
this.Columns[3].Width = this.ClientSize.Width - 560;
//第2、3列は中央揃えにする
this.Columns[1].DefaultCellStyle.Alignment =DataGridViewContentAlignment.MiddleCenter;
this.Columns[2].DefaultCellStyle.Alignment =DataGridViewContentAlignment.MiddleCenter;
}
//DataGridViewの列ヘッダークリックイベント(解説:これはソート目的です。)
private void On_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
//クリックされた列の番号を取得
int colIndex = e.ColumnIndex;
string colName = (sender as DataGridView).Columns[colIndex].Name;
switch(colName)
{ //解説:列のIDを調べ、それによってListのSortメソッドにidItemの比較メソッドのデリゲートを与えます。
case "Membership":
if(sortOrder)
idItems.Sort((x, y) => x.CompareMembershipTo(y));
else
idItems.Sort((x, y) => y.CompareMembershipTo(x));
break;
case "ID":
if(sortOrder)
idItems.Sort((x, y) => x.CompareIDTo(y));
else
idItems.Sort((x, y) => y.CompareIDTo(x));
break;
case "Password":
if(sortOrder)
idItems.Sort((x, y) => x.ComparePasswordTo(y));
else
idItems.Sort((x, y) => y.ComparePasswordTo(x));
break;
case "Remark":
if(sortOrder)
idItems.Sort((x, y) => x.CompareRemarkTo(y));
else
idItems.Sort((x, y) => y.CompareRemarkTo(x));
break;
}
bs.ResetBindings(false);
sortOrder = !sortOrder; //トグル変更(昇順←→降順)-解説:この為にフラグを設けました。)
}
//DataGridViewのダブルクリックイベント(解説:デフォルトではセルの編集モードに入りますが、このプログラムではセキュリティを考え、idItemの編集ダイアログを呼ぶことにしています。)
private void On_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewRow target = this.Rows[e.RowIndex];
//編集処理
AddEditDlg aeDlg = new AddEditDlg(target);
aeDlg.ShowDialog(); //AddEditDlg表示
bs.EndEdit(); //編集データのListとの同期
aeDlg.Dispose();
}
//解説:↓以降同じように「追加の公開メソッドと内部イベントメソッド」と書いていますが、「追加」を例にとって解説します。idListコントロールのメソッドとして公開(public)の引数の無いAdd()メソッドが使えますが、それは単にprivateのイベントメソッドを呼んでいるだけです。
//追加の公開メソッドと内部イベントメソッド
public void Add() //解説:コントロール外部から利用できる公開メソッド
{
AddItem_Click(cms, null); //解説:実際は↓のイベントメソッドを呼んでいるだけ。
}
private void AddItem_Click(object sender, EventArgs e)
{
//追加処理
AddEditDlg aeDlg = new AddEditDlg(); //AddEditDlg表示(解説:コンストラクターに引数がないと追加になります。)
aeDlg.ShowDialog();
if(!ReferenceEquals(aeDlg.iditem, null))
{
idItems.Add(aeDlg.iditem); //追加データのListへの追加
bs.ResetBindings(false); //idItems変更をDataGridViewへ反映する
}
else
MessageBox.Show("新データは追加されませんでした。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
aeDlg.Dispose();
}
//編集の公開メソッドと内部イベントメソッド
public void Edit()
{
EditItem_Click(cms, null);
}
private void EditItem_Click(object sender, EventArgs e)
{
//選択がなければその旨確認して何もしない
if(this.SelectedRows.Count == 0)
{
MessageBox.Show("選択された行がありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
//編集処理(複数行可)
foreach (DataGridViewRow sel in this.SelectedRows)
{
//編集処理
AddEditDlg aeDlg = new AddEditDlg(sel); //AddEditDlg表示(解説:編集の場合は、引数に行情報を入れます。)
aeDlg.ShowDialog();
bs.EndEdit(); //編集データのListとの同期
aeDlg.Dispose();
}
}
//検索の公開メソッドと内部イベントメソッド
public void Find()
{
FindItem_Click(cms, null);
}
private void FindItem_Click(object sender, EventArgs e)
{
//検索処理(検索結果の行をSelectedRowとして表示する)
FindDlg findDlg = new FindDlg(this); //FindDlg表示
findDlg.ShowDialog(); //List(idItems)の検索は行わない(解説:表の列を選択するだけです。)
findDlg.Dispose();
}
//削除の公開メソッドと内部イベントメソッド
public void Delete()
{
DeleteItem_Click(cms, null);
}
private void DeleteItem_Click(object sender, EventArgs e)
{
//選択がなければその旨確認して何もしない
if(this.SelectedRows.Count == 0)
{
MessageBox.Show("選択された行がありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
//削除処理(複数行可)-コントロールの選択行を基にidItemsの要素を削除
if(MessageBox.Show("本当に削除しますか?", "確認", MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.Yes)
{
foreach (DataGridViewRow sel in this.SelectedRows)
{
idItems.RemoveAt(sel.Index);
}
}
bs.ResetBindings(false); //idItems変更をDataGridViewへ反映する
this.ClearSelection(); //選択を解除する(解説:これがないと、削除後も「選択番号(Index)」で選択し続けます。)
}
//データの並び替え
public void Sort()
{
SortDlg sortDlg = new SortDlg(); //SortDlg表示
sortDlg.ShowDialog();
//列番号に基づいて並び変える(解説:↑の列ヘッダーのソートと比較してください。)
switch(sortDlg.ColIndex)
{ //解説:列番号(ColIndex)によってListのSortメソッドにデリゲートを与えます。
case 0:
if(sortDlg.IsAscending)
idItems.Sort((x, y) => x.CompareMembershipTo(y));
else
idItems.Sort((x, y) => y.CompareMembershipTo(x));
break;
case 1:
if(sortDlg.IsAscending)
idItems.Sort((x, y) => x.CompareIDTo(y));
else
idItems.Sort((x, y) => y.CompareIDTo(x));
break;
case 2:
if(sortDlg.IsAscending)
idItems.Sort((x, y) => x.ComparePasswordTo(y));
else
idItems.Sort((x, y) => y.ComparePasswordTo(x));
break;
case 3:
if(sortDlg.IsAscending)
idItems.Sort((x, y) => x.CompareRemarkTo(y));
else
idItems.Sort((x, y) => y.CompareRemarkTo(x));
break;
}
bs.ResetBindings(false);
sortDlg.Dispose();
}
(2)に続く。