|
C#のバージョンなどの開発環境が明示されていないので、参考になるかわかりませんが、
LINQ(C#3.0 .NET Framework3.5以上で使用可)を使って実装してみました。
LINQを使うのが初めてなので、効率的にかけているかまではわかりませんので、全面的な信頼はなさらないよう。
(たとえばデータ量が多くなった場合にどうなのか?とか、もっとシンプルになるはずとか)
◆コードの前提:
treeView1とbutton1を配置していて、button1をクリックしたらツリービューが作成されます。
◆実装内容の補足:
処理をしやすいように、まず以下のようなデータの変換を行ってから、ツリービューを構成しています。
[商品テーブル]
商品 商品 使用
コード 名 商品
"111", "AAA", "222"
"111", "AAA", "444"
"111", "AAA", "555"
"222", "BBB", "333"
"333", "CCC", ""
"444", "DDD", "333"
"444", "DDD", "666"
"555", "EEE", ""
"666", "FFF", ""
↓
[子商品テーブル] … 子の商品から見た表
商品 商品 親商品
コード 名 コード
"111", "AAA", ""
"222", "BBB", "111"
"333", "CCC", "222"
"333", "CCC", "444"
"444", "DDD", "111"
"555", "EEE", "111"
"666", "FFF", "444"
'-- コードはここから --
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
using System.Linq;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private DataTable dt;
private void Form1_Load(object sender, EventArgs e)
{
dt = new DataTable("商品テーブル");
dt.Columns.Add("商品コード", typeof(string));
dt.Columns.Add("商品名", typeof(string));
dt.Columns.Add("使用商品", typeof(string));
dt.Rows.Add("111", "AAA", "222");
dt.Rows.Add("111", "AAA", "444");
dt.Rows.Add("111", "AAA", "555");
dt.Rows.Add("222", "BBB", "333");
dt.Rows.Add("333", "CCC", "");
dt.Rows.Add("444", "DDD", "333");
dt.Rows.Add("444", "DDD", "666");
dt.Rows.Add("555", "EEE", "");
dt.Rows.Add("666", "FFF", "");
}
private class 商品
{
public string 商品コード { get; private set; }
public string 商品名 { get; private set;}
public string 使用商品 { get; private set;}
public 商品(string 商品コード, string 商品名, string 使用商品)
{
this.商品コード = 商品コード;
this.商品名 = 商品名;
this.使用商品 = 使用商品;
}
public override bool Equals(object obj)
{
商品 s = obj as 商品;
if (s == null) return false;
return this.商品コード.Equals(s.商品コード);
}
public override int GetHashCode()
{
return this.商品コード.GetHashCode();
}
public override string ToString()
{
return string.Format("{0},{1},{2}", this.商品コード, this.商品名, this.使用商品);
}
}
private class 子商品
{
public string 商品コード { get; private set; }
public string 商品名 { get; private set; }
public string 親商品コード { get; private set; }
public 子商品(string 商品コード, string 商品名, string 親商品コード)
{
this.商品コード = 商品コード;
this.商品名 = 商品名;
this.親商品コード = 親商品コード;
}
public override bool Equals(object obj)
{
子商品 s = obj as 子商品;
if (s == null) return false;
return this.商品コード.Equals(s.商品コード) && this.親商品コード.Equals(s.親商品コード);
}
public override int GetHashCode()
{
return this.商品コード.GetHashCode() * 10 + this.親商品コード.GetHashCode();
}
public override string ToString()
{
return string.Format("{0},{1},{2}", this.商品コード, this.商品名, this.親商品コード);
}
}
private void button1_Click(object sender, EventArgs e)
{
var 商品テーブル =
from DataRow row in dt.Rows
select new 商品(row.Field<string>("商品コード"), row.Field<string>("商品名"), row.Field<string>("使用商品"));
// 商品テーブルを子の商品からまとめなおしたテーブル。子からその親の商品コードを引くことができる。
var 子商品テーブル =
(from 親商品 in 商品テーブル
join 子商品 in 商品テーブル on 親商品.商品コード equals 子商品.使用商品 into 商品s
from 商品 in 商品s.DefaultIfEmpty()
select new 子商品(親商品.商品コード, 親商品.商品名, (商品 == null ? "" : 商品.商品コード))).Distinct();
int count = 子商品テーブル.Count(); // 最終的なノードの数になる
// 一回の「foreach (子商品 商品 in 処理対象商品)」で処理済みとなった親商品コードの一覧
// クエリ:処理対象商品の抽出に利用
List<string> processed = new List<string>() { "" };
// 一回の「foreach (子商品 商品 in 処理対象商品)」の処理中の親商品コードの一覧
List<string> processing = new List<string>();
// TreeNode.NodesもTreeView.Nodesも一律扱えるように
// 商品コードとその子を管理するTreeNodeCollectionのペアを保管しておく
Dictionary<string, TreeNodeCollection> allNodes = new Dictionary<string, TreeNodeCollection>();
allNodes.Add("", treeView1.Nodes);
while (allNodes.Count < count)
{
// 前回処理した商品コードを親に持つ子の商品のみを処理対象とする
// ツリーの上位階層の商品コードから順に処理していくため
var 処理対象商品 =
from 商品 in 子商品テーブル
join 処理済商品コード in processed on 商品.親商品コード equals 処理済商品コード
select 商品;
processing.Clear();
foreach (子商品 商品 in 処理対象商品)
{
TreeNode node = new TreeNode(商品.商品コード + ":" + 商品.商品名);
allNodes[商品.親商品コード].Add(node);
if (!allNodes.ContainsKey(商品.商品コード))
{
allNodes.Add(商品.商品コード, node.Nodes);
}
processing.Add(商品.商品コード);
}
processed = new List<string>(processing);
}
treeView1.TopNode.Expand();
}
}
}
|