C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Listの入れ子があるListをソースに設定する方法について

[トピック内 12 記事 (1 - 12 表示)]  << 0 >>

■92931 / inTopicNo.1)  Listの入れ子があるListをソースに設定する方法について
  
□投稿者/ 河童 (76回)-(2019/11/11(Mon) 01:26:37)

分類:[C#] 

いつも大変お世話になっております。
Listの入れ子があるListをDataGridViewのソースに設定する方法について教えてください。


グループのリスト(GroupLst)があります。
グループリストにはチームの入れ子があり、チームリストにはユーザのリストがあります。

フォーム画面には、3つのDataGridViewがあります。
・グループ用(dgvGroup)group_num group_name 
・チーム用(dgvTeam)team_num team_name 
・ユーザ用(dgvUser)user_num user_name 


ロード時にまずdgvGroupのソースにGroupLstを設定します。
グループを選択すると次にdgvTeamのソースは、選択されたグループに属するチームのみのソースを設定したいです。
さらにdgvUserは、選択されたチームに属するユーザのみをソースに設定したいです。

わからないことは、入れ子があるListをソースに設定する方法と各DataGridViewのソースを連動させることです。
どの様に設定すれば良いでしょうか?
グループを選択した後の各ソースを作成する方法を教えてください。

ご教示のほど、よろしくお願いします。

理想の動きは、
最初のグループは、「GroupA」「GroupB」が表示されて
「GroupA」が選択されるとチームは「TeamA」が表示されて
「TeamA」が選択されると「User2」が表示されることです。

グループ、チームとユーザは今後増えていきます。
チームは複数のグループには属しません。
ユーザは複数のチームには属しません。


        // グループリスト
        List<GroupInfo> GroupLst;
        public class GroupInfo
        {
            public int group_num { get; set; }
            public string group_name { get; set; }
            public List<TeamInfo> teamList = new List<TeamInfo>();
        }
        public class TeamInfo
        {
            public int team_num { get; set; }
            public string team_name { get; set; }
            public List<UserInfo> userList = new List<UserInfo>();
        }
        public class UserInfo
        {
            public int user_num { get; set; }
            public string user_name { get; set; }
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            // リスト作成
            GroupLst = new List<GroupInfo>();
            GroupInfo d = new GroupInfo();
            d.group_num = 1;
            d.group_name = "GroupA";
            d.teamList = new List<TeamInfo>();
            TeamInfo t = new TeamInfo();
            t.team_num = 1;
            t.team_name = "TeamA";
            t.userList = new List<UserInfo>();
            UserInfo u = new UserInfo();
            u.user_num = 1;
            u.user_name = "User1";
            t.userList.Add(u);
            d.teamList.Add(t);
            GroupLst.Add(d);

            GroupInfo d2 = new GroupInfo();
            d2.group_num = 2;
            d2.group_name = "GroupB";
            d2.teamList = new List<TeamInfo>();
            TeamInfo t2 = new TeamInfo();
            t2.team_num = 2;
            t2.team_name = "TeamB";
            t2.userList = new List<UserInfo>();
            UserInfo u2 = new UserInfo();
            u2.user_num = 2;
            u2.user_name = "User2";
            t2.userList.Add(u2);
            d2.teamList.Add(t2);
            GroupLst.Add(d2);

            // ソース設定
            dgvGroup.DataSource = GroupLst;

            //dgvTeam.DataSource = GroupLst;
            //dgvUser.DataSource = GroupLst;
        }

        private void dgvGroup_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            int Gnum = (int)dgvGroup.CurrentRow.Cells[0].Value;
            //textBox1.Text = Convert.ToString(Gnum);
            string Gname = (string)dgvGroup.CurrentRow.Cells[1].Value;
            //textBox2.Text = Convert.ToString(Gname);

        }

引用返信 編集キー/
■92932 / inTopicNo.2)  Re[1]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ WebSurfer (1968回)-(2019/11/11(Mon) 09:59:59)
No92931 (河童 さん) に返信

DataGridView にはどのように表示したいのですか?
引用返信 編集キー/
■92934 / inTopicNo.3)  Re[1]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 魔界の仮面弁士 (2467回)-(2019/11/11(Mon) 10:40:06)
No92931 (河童 さん) に返信
> List<GroupInfo> GroupLst;
DataGridView にバインドすることが目的ならば、
List<T> ではなく
BindingList<T> の方が良いでしょう。

ただ、階層構造の移動通知を盛り込むことを考えると、
型付 DataSet でリレーションを表現した方が手っ取り早いと思います。
DataSet での表現例は No92929 を参考に。



> グループ、チームとユーザは今後増えていきます。
> チームは複数のグループには属しません。
> ユーザは複数のチームには属しません。

新しいユーザーを追加する場合、user_num が競合しないことを検査する必要がありますよね。

DataSet なら、競合検査も同時に組み込んでおくことが出来ますが、独自管理するなら
user_num を Key とする Dictionary<,> を用意しておいた方が検査しやすいかと。


それと、ユーザーを他のチームに異動させる場合、どのように操作させる予定なのでしょうか?

UI がまだ未決定なら、グループ/チーム/ユーザーを TreeView で表現して、
ノードをドラッグ移動で操作可能にするのも面白いかも。

num と name 以外にも情報が増えそうなら、 TreeListView な実装が
Web 上で幾つか公開されているようです。
引用返信 編集キー/
■92945 / inTopicNo.4)  Re[2]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 河童 (77回)-(2019/11/11(Mon) 16:33:27)
No92934 (魔界の仮面弁士 さん) に返信

こんにちは。魔界の仮面弁士 さん、お返事ありがとうございます。

わからないことが2点あります。

1.リストの値をテーブルにセットする方法
入れ子があるリストのグループリストからどのようにDataSetに値を設定させれば良いでしょうか?


2.キーの設定方法について
> グループ、チームとユーザは今後増えていきます。
> チームは複数のグループには属しません。
> ユーザは複数のチームには属しません。
データを追加する必要が今後でてくるのですが、そのときのチームとユーザの番号を一意にするのではなく、
同じ番号も使用したいと思っています。
例えば、
group_num team_num user_num 
1         1        1
1         1        2
1         1        3
2         1        1
2         1        2
3         1        1
のようにgroup_num,team_num,user_numの3つをキーに設定したいです。
tblGに3つのキーを設定すると、
teamとuserがnullでエラーが発生します。

新しいユーザを追加するときは、
group_num,team_num,user_numで重複がなくuser_numの最大値を設定したいです。

同様にチームを追加するときは、
groupj_num,team_numuで重複がなくteam_numの最大値を設定したいです。


        private void FormMain_Load(object sender, EventArgs
        {

            #region 型付DataSetを使えば、この部分をデザイン時に作成しておくこともできる
            var tblG = ds.Tables.Add("Group");
            tblG.Columns.Add("group_num", typeof(int));
            tblG.Columns.Add("group_name");
            tblG.Columns.Add("team_num");
            tblG.Columns.Add("user_num");
            tblG.PrimaryKey = new DataColumn[] { tblG.Columns["group_num"] };
            //tblG.PrimaryKey = new DataColumn[] { tblG.Columns["group_num"], tblG.Columns["team_num"], tblG.Columns["user_num"] };

            var tblT = ds.Tables.Add("Team");
            tblT.Columns.Add("group_num", typeof(int));
            tblT.Columns.Add("team_num", typeof(int));
            tblT.Columns.Add("team_name");
            tblT.PrimaryKey = new DataColumn[] { tblT.Columns["group_num"], tblT.Columns["team_num"] };

            var tblU = ds.Tables.Add("User");
            tblU.Columns.Add("team_num", typeof(int));
            tblU.Columns.Add("user_num", typeof(int));
            tblU.Columns.Add("user_name");
            tblU.PrimaryKey = new DataColumn[] { tblU.Columns["team_num"], tblU.Columns["user_num"] };

            ds.Relations.Add("G_T", tblG.Columns["group_num"], tblT.Columns["group_num"]);
            ds.Relations.Add("T_U", tblT.Columns["team_num"], tblU.Columns["team_num"]);

            bindingSource1.DataSource = ds;
            bindingSource1.DataMember = "Group";
            bindingSource2.DataSource = bindingSource1;
            bindingSource2.DataMember = "G_T";
            bindingSource3.DataSource = bindingSource2;
            bindingSource3.DataMember = "T_U";

            dgvGroup.DataSource = bindingSource1;
            dgvTeam.DataSource = bindingSource2;
            dgvUser.DataSource = bindingSource3;

            dgvGroup.Columns["team_num"].Visible = false;
            dgvGroup.Columns["user_num"].Visible = false;

            dgvTeam.Columns["group_num"].Visible = false;
            dgvUser.Columns["team_num"].Visible = false;
            #endregion


            // リスト作成
            GroupLst = new List<GroupInfo>();
            GroupInfo d = new GroupInfo();
            d.group_num = 1;
            d.group_name = "GroupA";
            d.teamList = new List<TeamInfo>();
            TeamInfo t = new TeamInfo();
            t.team_num = 1;
            t.team_name = "TeamA";
            t.userList = new List<UserInfo>();
            UserInfo u = new UserInfo();
            u.user_num = 1;
            u.user_name = "User1";
            t.userList.Add(u);
            d.teamList.Add(t);
            GroupLst.Add(d);

            GroupInfo d2 = new GroupInfo();
            d2.group_num = 2;
            d2.group_name = "GroupB";
            d2.teamList = new List<TeamInfo>();
            TeamInfo t2 = new TeamInfo();
            t2.team_num = 2;
            t2.team_name = "TeamB";
            t2.userList = new List<UserInfo>();
            UserInfo u2 = new UserInfo();
            u2.user_num = 2;
            u2.user_name = "User2";
            t2.userList.Add(u2);
            d2.teamList.Add(t2);
            GroupLst.Add(d2);

            // 不明点1
            // グループリストからテーブルにどのように設定すれば良いでしょう
            

            // データを追加
            ds.Tables["Group"].Rows.Add(1, "GroupA");
            ds.Tables["Group"].Rows.Add(2, "GroupB");
            ds.Tables["Group"].Rows.Add(3, "GroupC");
            ds.Tables["Team"].Rows.Add(1, 1, "GA_TeamA");
            ds.Tables["Team"].Rows.Add(1, 2, "GA_TeamB");
            ds.Tables["Team"].Rows.Add(1, 3, "GA_TeamC");
            // 不明点2
            // チームやユーザが入れ替わることはないのですが、
            // チームとユーザの同じ番号を使いたいです。
            // チームのキーの設定はグループとチームの番号の2種類で
            // ユーザのキーの設定はグループとチームとユーザの番号の3種類で
            // group_numとteam_numの2つを主キーに設定
            //ds.Tables["Team"].Rows.Add(2, 1, "GB_TeamA");
            ds.Tables["Team"].Rows.Add(2, 4, "GB_TeamA");

            ds.Tables["User"].Rows.Add(1, 1, "User1");
            ds.Tables["User"].Rows.Add(1, 2, "User2");
            ds.Tables["User"].Rows.Add(2, 3, "User3");
            ds.Tables["User"].Rows.Add(2, 4, "User4");
            ds.Tables["User"].Rows.Add(3, 5, "User5");
            ds.AcceptChanges();
        }

引用返信 編集キー/
■92946 / inTopicNo.5)  Re[3]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 河童 (78回)-(2019/11/11(Mon) 17:01:19)
No92945 (河童 さん) に返信

 
入れ子があるリストでも繰り返し処理ができました。
グループ、チーム、ユーザごとに繰り返し処理を実行しました。

            foreach (var item_g in GroupLst)
            {
                ds.Tables["Group"].Rows.Add(item_g.group_num, item_g.group_name);

                // チームリスト
                foreach (var item_t in item_g.teamList)
                {
                    ds.Tables["Team"].Rows.Add(item_g.group_num,item_t.team_num, item_t.team_name);

                    // ユーザリスト
                    foreach (var item_u in item_t.userList)
                    {
                        ds.Tables["User"].Rows.Add(item_t.team_num, item_u.user_num, item_u.user_name);
                    }                   
                }

            }

 
もう1つのわからないことで
複数の主キーの設定方法がわかりません。
ご教示よろしくお願いします。


引用返信 編集キー/
■92952 / inTopicNo.6)  Re[3]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 魔界の仮面弁士 (2471回)-(2019/11/11(Mon) 18:51:05)
No92945 (河童 さん) に返信
> 1.リストの値をテーブルにセットする方法
> 入れ子があるリストのグループリストからどのようにDataSetに値を設定させれば良いでしょうか?

リストをループ処理して、DataSet のテーブルに行を Add していってください。


なお、DataGridView を readonly とせず、編集目的でも用いる場合、
DataSet とリストとで二重管理になってしまい、更新時の同期が面倒なので、
いっそのことリストは使わず、DataSet だけで管理するという選択肢もあるかと思います。

その場合、DataSet の構造はこんな感じになります。
(user_num が全体で一意ではなく、group_num, team_num ごとに一意となる場合)

DataSet
├Tables["GroupInfo"] … PrimaryKey = group_num
│├Columns["group_num"]
│└Columns["group_name"]
├Tables["TeamInfo"] … PrimaryKey = group_num, team_num
│├Columns["group_num"] … 非表示列 親GroupInfoの group_num が入る。
│├Columns["team_num"]
│└Columns["team_name"]
├Tables["UserInfo"] … PrimaryKey = group_num, team_num, user_num
│├Columns["group_num"] … 非表示列。親Teamの group_num が入る。
│├Columns["team_num"] … 非表示列。親Team の team_num が入る。
│├Columns["user_num"]
│└Columns["user_name"]
├Relations["Group_Team"]
└Relations["Team_User"]


DataGridView を編集目的でも用いる場合、
num の変更を許可するかどうか(変更を許可するなら競合検査も必要)や、
レコードの削除や新規行の追加やを許可するかどうかも検討してください。



もしも DataSet 案は使わず、当初の設計通り「リスト」だけで行う場合には、
階層の移動は手動で行うことになります。たとえばこんな感じです。
(DataGridView で編集させる目的なら、List<> ではなく BindingList<> がお奨め)


private void FormMain_Load(object sender, EventArgs e)
{
  dgvGroup.DataSource = GroupLst;
}

private void dgvGroup_RowEnter(object sender, DataGridViewCellEventArgs e)
{
  dgvTeam.DataSource = null;
  var group = dgvGroup.Rows[e.RowIndex].DataBoundItem as GroupInfo;
  dgvTeam.DataSource = (group != null) ? (object)group.teamList : typeof(TeamInfo);
  if (group == null || !group.teamList.Any())
  {
    dgvUser.DataSource = new UserInfo[0];
  }
}

private void dgvTeam_RowEnter(object sender, DataGridViewCellEventArgs e)
{
  dgvUser.DataSource = null;
  var team = dgvTeam.Rows[e.RowIndex].DataBoundItem as TeamInfo;
  dgvUser.DataSource = (team != null) ? (object)team.userList : typeof(UserInfo);
}





> 2.キーの設定方法について
>>グループ、チームとユーザは今後増えていきます。
>>チームは複数のグループには属しません。
>>ユーザは複数のチームには属しません。
> データを追加する必要が今後でてくるのですが、そのときのチームとユーザの番号を一意にするのではなく、
> 同じ番号も使用したいと思っています。

group_num は、全体で一意。
team_num は、全体で一意ではなく、group ごとに一意。
user_num は、全体で一意ではなく、group_num, team_num ごとに一意。

ということでしょうか。
だとすると、num の重複チェックは自階層内のチェックだけで済みますね。
引用返信 編集キー/
■92962 / inTopicNo.7)  Re[4]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 魔界の仮面弁士 (2476回)-(2019/11/11(Mon) 23:08:41)
No92946 (河童 さん) に返信
> ds.Tables["User"].Rows.Add(item_t.team_num, item_u.user_num, item_u.user_name);

team_num が「全体で一意」になる場合は上記で良いですが、
team_num が「グループ内で一意」である場合には、
 ds.Tables["User"].Rows.Add(item_g.group_num, item_t.team_num, item_u.user_num, item_u.user_name);
にする必要があるでしょう。


> 複数の主キーの設定方法がわかりません。

ds.Tables["Group"].PrimryKey … group_num だけ
ds.Tables["Team"].PrimryKey … group_num と team_num
ds.Tables["User"].PrimryKey … group_num と team_num と user_num

という構成になるかと思います。


複数の主キーを持つテーブル同士を繋ぐ場合、
.Relations.Add(string, DataColumn[], DataColumn[]) もしくは
.Relations.Add(string, DataColumn[], DataColumn[], bool) のオーバーライドを使います。

コードではなくデザイナから指定する場合は、
DataSet デザイナ上で右クリックして、「リレーションシップ」から設定できます。



もしも登録順の都合で、「存在しない num」が一時的に割り当てられるようなケースでは、
ループ処理の前に DataSet の EnforceConstraints プロパティを false にしておきます。

false に設定している間は、リレーションシップや DBNull 制約などが無視されますので、
ループを終えて、全件登録が完了した後で EnforceConstraints を true に戻します。
true に戻した瞬間に制約検査が実行され、問題があれば例外として通知されます。
引用返信 編集キー/
■92980 / inTopicNo.8)  Re[4]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 河童 (79回)-(2019/11/12(Tue) 13:57:18)
No92952 (魔界の仮面弁士 さん) に返信

たくさんアドバイスいただいて、ありがとうございます。
少し頭が混乱してきました。

申し訳ないです。やっぱりリストを使って処理を行いたいです。

Listの入れ子があるListをソースに設定する方法について
やりたいことを整理しました。

画面上にDataGridViewが3つあります。
dgvGroup
dgvTeam
dgvUser

DataGridViewのソースとなるデータは、外部ファイルからリスト型で読み込まれます。
この処理は実装済みです。
読み込んだリスト型のデータを各DataGridViewのソースに設定したいです。

画面上の動きとしては、
画面のロード時にグループ(GroupA)を表示
「GroupA」を選択するとチーム(TeamA)を表示
「TeaA」を選択するとユーザ(User1)を表示
のように段階を踏ませたいです。

グループ、チーム、ユーザの新規追加は、
別フォームを開いてから登録処理を実行させようと思っています。

今わからないことは、
段階的にDataGridViewを表示させる方法です。
ロード時に入れ子のリストごとに新たにリスト作成して
それをDataGridViewのソースに設定しています。

どのように各DataGridViewのリストのソースを設定すれば良いでしょうか?

Groupが選択されたときにTeamのDataGridViewのソースの作成方法
Teamが選択されたときのUserのDataGridViewのソースの作成方法
group_numとteam_numのキーの渡し方がわかりません。


        // グループリスト
        List<GroupInfo> GroupLst;
        public class GroupInfo
        {
            public int group_num { get; set; }
            public string group_name { get; set; }
            public List<TeamInfo> teamList = new List<TeamInfo>();
        }
        public class TeamInfo
        {
            public int team_num { get; set; }
            public string team_name { get; set; }
            public List<UserInfo> userList = new List<UserInfo>();
        }
        public class UserInfo
        {
            public int user_num { get; set; }
            public string user_name { get; set; }
        }


        // DataGridView用
        public class dgvGroupInfo
        {
            public int group_num { get; set; }
            public string group_name { get; set; }
        }
        public class dgvTeamInfo
        {
            public int team_num { get; set; }
            public string team_name { get; set; }
        }
        public class dgvUserInfo
        {
            public int user_num { get; set; }
            public string user_name { get; set; }
        }

        List<dgvGroupInfo> DgvGroupLst;         // グリッド用グループリスト
        int RowIdx1 = 0; // 選択行インデックス
        List<dgvTeamInfo> DgvTeamLst;       // グリッド用チームリスト
        int RowIdx2 = 0; // 選択行インデックス
        List<dgvUserInfo> DgvUserLst;     // グリッド用ユーザリスト
        int RowIdx3 = 0; // 選択行インデックス

        private void FormMain_Load(object sender, EventArgs e)
        {

            // リストデータ作成
            GroupLst = new List<GroupInfo>();
            GroupInfo d = new GroupInfo();
            d.group_num = 1;
            d.group_name = "GroupA";
            d.teamList = new List<TeamInfo>();
            TeamInfo t = new TeamInfo();
            t.team_num = 1;
            t.team_name = "TeamA";
            t.userList = new List<UserInfo>();
            UserInfo u = new UserInfo();
            u.user_num = 1;
            u.user_name = "User1";
            t.userList.Add(u);
            d.teamList.Add(t);
            GroupLst.Add(d);

            // DataGridViewソース作成
            CreatDgvSource();

        }
 
        void CreatDgvSource()
        {
            // リスト更新、表示
            dgvGroup.DataSource = null;
            dgvTeam.DataSource = null;
            dgvUser.DataSource = null;

            // リストのデータを取得
            int iRet = Create_DgvLst();
            if (iRet == -1) return;

            // グループのソースセット
            dgvGroup.DataSource = DgvGroupLst;

            // チームのソースセット
            dgvTeam.DataSource = DgvTeamLst;

            // ユーザのソースセット
            dgvUser.DataSource = DgvUserLst;

            // 選択行設定
            if (RowIdx1 >= dgvGroup.Rows.Count) RowIdx1--;
            if (dgvGroup.Rows.Count > 0 && RowIdx1 < 0) RowIdx1 = 0;
         
            dgvGroup.Refresh();
            dgvTeam.Refresh();
            dgvUser.Refresh();
        }

        private int Create_DgvLst()
        {
            // グリッド用リストを初期化
            DgvGroupLst = new List<dgvGroupInfo>();
            DgvTeamLst = new List<dgvTeamInfo>();
            DgvUserLst = new List<dgvUserInfo>();

            // 取得データからリストを作成
            foreach (var g in GroupLst) // グループ
            {
                dgvGroupInfo d1 = new dgvGroupInfo();
                d1.group_num = g.group_num;
                d1.group_name = g.group_name;

                DgvGroupLst.Add(d1);

                foreach (var t in g.teamList)  // チーム
                {
                    dgvTeamInfo d2 = new dgvTeamInfo();
                    d2.team_num = t.team_num;
                    d2.team_name = t.team_name;

                    DgvTeamLst.Add(d2);

                    foreach (var u in t.userList)  // ユーザ
                    {
                        dgvUserInfo d3 = new dgvUserInfo();
                        d3.user_num = u.user_num;
                        d3.user_name = u.user_name;

                        DgvUserLst.Add(d3);
                    }
                }
            }
            return 0;
        }


引用返信 編集キー/
■92983 / inTopicNo.9)  Re[5]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 魔界の仮面弁士 (2477回)-(2019/11/12(Tue) 14:24:25)
No92980 (河童 さん) に返信
> 画面上にDataGridViewが3つあります。

これらは ReadOnly でしょうか?

編集可能とする場合は、さらに

(1)主キーの変更を許可するか否か
(2)データの削除を許可するか否か
(3)新規レコードの作成を許可するか否か

についても考える必要があります。
(許可する場合、それぞれに応じた追加の処理が必要です)


> 読み込んだリスト型のデータを各DataGridViewのソースに設定したいです。

その場合のコードは No92952 に示しています。



> DataGridViewのソースとなるデータは、外部ファイルからリスト型で読み込まれます。
> この処理は実装済みです。

上記の入力処理(読み込み)の後、
出力処理(リストの内容を外部ファイルに書き戻す処理)も発生しますか?

入力処理(読み込み)だけなら、さほど問題は無いのですが、
出力処理もある場合、単純なファイルの再生成だけで良いのかでも設計が変わってきます。

上書き再生成で出力するのならば良いですが、データベース等が相手だと、
 -- group_num が 100 のレコードを、データベースから削除する
 DELETE FROM GRP_TBL WHERE NUM = 100
のように「削除対象の group_num の一覧」を覚えておく必要があるため、
単純に GroupLst から .Remove() されるだけだと都合が悪くなるかもしれません。
主キー項目の書き換えを許可する場合も同様。



> 段階的にDataGridViewを表示させる方法です。

No92952 の RowEnter では駄目ですか?


> int RowIdx1 = 0; // 選択行インデックス

DataGridView は列クリックでのソートも可能なので、インデックスでのアクセスはお奨めしません。

.DataSource にバインドしている場合には、 No92952 に示したように、
選択行の DataBoundItem プロパティから該当レコードを参照するようにします。
引用返信 編集キー/
■92986 / inTopicNo.10)  Re[6]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 河童 (80回)-(2019/11/12(Tue) 15:19:48)
No92983 (魔界の仮面弁士 さん) に返信

> これらは ReadOnly でしょうか?
全部ReadOnly に設定しています。


> 上記の入力処理(読み込み)の後、
> 出力処理(リストの内容を外部ファイルに書き戻す処理)も発生しますか?
はい、発生します。出力するときに同じリスト型にして、それを出力する形を取りたいです。

>
>>段階的にDataGridViewを表示させる方法です。
>
> No92952 の RowEnter では駄目ですか?
もう一度確認してみます。



引用返信 編集キー/
■92989 / inTopicNo.11)  Re[7]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 河童 (81回)-(2019/11/12(Tue) 16:00:42)
No92983 (魔界の仮面弁士 さん) に返信

GroupLst を BindingList にしました。
そして RowEnter イベントを追加するとリスト表示することができました。
ありがとうございます。

続けて質問させてください。

追加や編集の処理を行うときGroupLst にどのように値を更新すれば良いですか?
またこのDataGridView のソースをそのままGroupLstに戻す方法を教えてください。
GroupLst は、上書き更新で行いたいです。



// グループリスト
BindingList<GroupInfo> GroupLst;
public class GroupInfo
{
public int group_num { get; set; }
public string group_name { get; set; }
public List<TeamInfo> teamList = new List<TeamInfo>();
}
public class TeamInfo
{
public int team_num { get; set; }
public string team_name { get; set; }
public List<UserInfo> userList = new List<UserInfo>();
}
public class UserInfo
{
public int user_num { get; set; }
public string user_name { get; set; }
}

private void FormMain_Load(object sender, EventArgs e)
{

// リストデータ作成
GroupLst = new BindingList<GroupInfo>();
GroupInfo d = new GroupInfo();
d.group_num = 1;
d.group_name = "GroupA";
d.teamList = new List<TeamInfo>();
TeamInfo t = new TeamInfo();
t.team_num = 1;
t.team_name = "TeamA";
t.userList = new List<UserInfo>();
UserInfo u = new UserInfo();
u.user_num = 1;
u.user_name = "User1";
t.userList.Add(u);
d.teamList.Add(t);
GroupLst.Add(d);

// リストデータ作成
//GroupLst = new List<GroupInfo>();
GroupInfo d2 = new GroupInfo();
d2.group_num = 2;
d2.group_name = "GroupB";
d2.teamList = new List<TeamInfo>();
TeamInfo t2 = new TeamInfo();
t2.team_num = 1;
t2.team_name = "TeamB";
t2.userList = new List<UserInfo>();
UserInfo u2 = new UserInfo();
u2.user_num = 1;
u2.user_name = "User2";
t2.userList.Add(u2);
d2.teamList.Add(t2);
GroupLst.Add(d2);


// DataGridViewソース作成
CreatDgvSource();
}

void CreatDgvSource()
{
// リスト更新、表示
dgvGroup.DataSource = null;

// グループのソースセット
dgvGroup.DataSource = GroupLst;

dgvGroup.Refresh();
}

private void dgvGroup_RowEnter(object sender, DataGridViewCellEventArgs e)
{
dgvTeam.DataSource = null;
var group = dgvGroup.Rows[e.RowIndex].DataBoundItem as GroupInfo;
dgvTeam.DataSource = (group != null) ? (object)group.teamList : typeof(TeamInfo);
if (group == null || !group.teamList.Any())
{
dgvUser.DataSource = new UserInfo[0];
}
}

private void dgvTeam_RowEnter(object sender, DataGridViewCellEventArgs e)
{
dgvUser.DataSource = null;
var team = dgvTeam.Rows[e.RowIndex].DataBoundItem as TeamInfo;
dgvUser.DataSource = (team != null) ? (object)team.userList : typeof(UserInfo);
}
引用返信 編集キー/
■92996 / inTopicNo.12)  Re[8]: Listの入れ子があるListをソースに設定する方法について
□投稿者/ 魔界の仮面弁士 (2480回)-(2019/11/12(Tue) 17:52:35)
2019/11/12(Tue) 20:10:28 編集(投稿者)

No92989 (河童 さん) に返信
> GroupLst を BindingList にしました。

BindingList<T> にしたのは GroupLst だけで、
teamList や userList は List<T> のままにしたいということでしょうか。

その場合、dgvUser.DataSource には team.userList ではなく
new BindingList<UserInfo>(team.userList) をセットします。
※dgvTeam も同様。


また、DataGridView 側から新規行の追加や編集・削除を行えないようにしたい場合には、
BindingList<T> のプロパティで制御できます。
 GroupLst.AllowNew = false;
 GroupLst.AllowEdit = false;
 GroupLst.AllowRemove = false;

List<T> の場合は上記が使えませんので、DataGridView の
AllowUserTo何某 系プロパティでも代用してあげてください。


DataGridView に List<T> を渡していた場合、リストを操作しても
編集結果が DataGridView に伝わらないので、画面に反映させるために
少し追加のコードが必要になることに注意して下さい。


BindingList<T> や DataSet/DataTable/DataView を渡していた場合は、
これらの変更通知機能によって、画面が自動的に更新されます。


> public int team_num { get; set; }
> public string team_name { get; set; }
> public List<UserInfo> userList = new List<UserInfo>();

team_num と team_name は「読み書き可能なプロパティ」になっていますが、
userList は「読み書き可能なフィールド」になっていますね。

userList は「読み取り専用プロパティ」または「読み取り専用フィールド」の方がベターかと思います。
TeamInfo 側を改修できない事情がある場合は仕方ないですが。


> 追加や編集の処理を行うときGroupLst にどのように値を更新すれば良いですか?

DataGridView 側からの編集ではなく、リスト側への操作となりますので、
たとえば【削除処理】なら、先の DataBoundItem を通じて

 if (dgvGroup.CurrentRow!=null)
 {
  var selectedGroup = (GroupInfo)dgvGroup.CurrentRow.DataBoundItem;
  GroupLst.Remove(selectedGroup); // リストから取り除く
 }

とします。

GroupLst が BindingList<T> が相手の場合は上記だけで良いですが、
相手が List<T> の場合は、削除後に DataGridView への
再バインド処理も必要となります。
※List<T> 時に再バインドしなかった場合、DataGridView 上の件数が
 変化しないため、削除済み行へのアクセスされてしまい、例外が発生します。



【編集処理】の場合も同様に、CurrentRow からバインド先のアイテムを得て、
それをそのまま編集すれば OK です。
  var selectedGroup = (GroupInfo)dgvGroup.CurrentRow.DataBoundItem;
  selectedGroup.group_num = 123; // 新しいnum
  selectedGroup.group_name = "新しい名前";
上記の selectedGroup を操作すると、GroupLst 内の該当グループも
連動して書き換わります。GroupLst[n].group_name を操作しているのと同義。

ただしこれだけだと、編集結果が画面に即座に反映されないので、上記の後に
  // BindingList<GroupInfo> から、変更通知を伝える
  GroupLst.ResetItem(GroupLst.IndexOf(selectedGroup));
もしくは
  // DataGridView から、行に対して再描画依頼を投げる
  dgvGroup.InvalidateRow(dgvGroup.CurrentRow.Index);
のようにして、変更したことを DataGridView に通知する必要があります。



【追加処理】については、DataGridView とは無関係に
  // BindingList<GroupInfo> の場合
  var newGroup = GroupLst.AddNew();
  newGroup.group_num = 12345;
  newGroup.group_name = "新しいグループ";
  // BindingList<T> の場合、追加時の画面更新は自動的に行われる
もしくは
  // List<GroupInfo> の場合
  var newGroup = new GroupInfo();
  newGroup.group_num = 12345;
  newGroup.group_name = "新しいグループ";
  GroupLst.Add(newGroup);
  // List<T> には変更通知依頼が無いので、自分で再バインドして通知する
  var oldSource = dgvGroup.DataSource;
  dgvGroup.DataSource = null;
  dgvGroup.DataSource = oldSource;
という感じになります。


編集・追加時にキーが重複することを防ぎたい場合には、
事前にリスト内を列挙して、同じキーが入っていないことを
検査しておいてください。


グループを新規追加する場合は以上で終わりですが、
チームの場合は親グループの teamList への追加処理が必要です。
(ユーザーも同様に、親チームの userList へ追加する)


追加先となるチームのリストは先ほどと同様に、親階層の DataGridView から
 GroupInfo selectedGroup = (GroupInfo)dgvGroup.CurrentRow.DataBoundItem;
 var teamList = selectedGroup.teamList;
のように得ることができます。


> またこのDataGridView のソースをそのままGroupLstに戻す方法を教えてください。

上記の処理で、GroupLst の内容は既に書き換わっています。
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ