前回で「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)に続く。