[C#]TableLayoutPanelの追加時のパフォーマンス

TableLayoutPanelは便利ですがちょっと困ったことがあります。

セルの位置や結合の設定を、格納されているアイテムからいじるプロパティがないのです(見つけてないだけかも)。
ListViewみたいに独自のアイテムクラスを持ったコレクションがあれば、そこからいじるという手があるのですが、一度コントロールを格納してしまうと、セルの位置を変更する方法はおそらくこれだけだと思います。

>tableLayoutPanel.SetCellPosition(item, tableLayoutPanelCellPosition);

ただ、これはちょっとパフォーマンス的にん?って感じがします。あくまで予想ですが、itemが格納されている場所が明示されていないので、内部的にコレクションの中からitemをいちいち検索しているはず。位置の指定がダブったときに後続のセルの位置も書き換えないといけないので、おそらくこんな風なメソッド形式で定義したのだと思いますが、なんかちょっとまどろっこしいですね。

TableLayoutPanelに大量のアイテムを追加する場合で実験してみます。Labelを大量に(1000個ぐらい)用意しして、
tableLayoutTest.png
こんな感じのフォームを使ってみます。
0から1000までの数字を追加するだけだと面白く無いので、50ごとに区切りのラベルを入れています。区切りのラベルでは横のセルを全て結合させています。次のような2つのケースを用意して実行時間を比較します。

ケース1:TableLayoutPanelにControl.Addでひとつづつセルの位置、結合を指定しながら追加

ケース2:TableLayoutPanelにControl.AddRangeで一気に追加、そのあとセルの位置・結合を再度指定


ケース1はContol.Addの引数で位置を指定、ケース2はSetCellPositionで位置を指定います。

ソース




using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;


static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

public class Form1 : Form
{
//ボタン
Button case1;
Button case2;

//初期化
public Form1()
{
this.Size = new Size(200, 200);
case1 = new Button()
{
Text = "ケース1", Location = new Point(50, 50),
AutoSize = true,
};
case2 = new Button()
{
Text = "ケース2", Location = new Point(50, 100),
AutoSize = true,
};
case1.Click += case1_Click;
case2.Click += case2_Click;
this.Controls.AddRange(new Control[]{ case1, case2});
}

//メソッド
//ケース1クリック時のイベント
private void case1_Click(object sender, EventArgs e)
{
Case1 case1 = new Case1();
case1.ShowDialog();
}

//ケース2クリック時のイベント
private void case2_Click(object sender, EventArgs e)
{
Case2 case2 = new Case2();
case2.ShowDialog();
}
}

//ラベル生成用
public class LabelMake
{
List<Label> label;
static string separate = "------------------";

public static string Separate {get{return separate;}}

public LabelMake()
{
label = new List<Label>();
}
//ラベルを初期化して返すメソッド
public Label[] GetLabel()
{
for(int i=0; i<1000; i++)
{
Label item;
//50刻みに区切りを入れる
if(i%50==0)
{
item = new Label();
item.AutoSize = true;
item.Text = separate;
item.Dock = DockStyle.Fill;
label.Add(item);
}
item = new Label();
item.Size = new Size(35, 15);
item.Text = i.ToString();
item.Dock = DockStyle.Fill;
//背景色の設定
if(i % 2==0) item.BackColor = SystemColors.Window;
else item.BackColor = Color.SkyBlue;
label.Add(item);
}
return label.ToArray();
}
}

//ケース1:逐次場所を指定しながら格納する場合
public class Case1 : Form
{
public Case1()
{
DateTime start = DateTime.Now;
this.Size = new Size(400, 300);
this.BackColor = SystemColors.Window;
//ラベル
LabelMake lm = new LabelMake();
Label[] label = lm.GetLabel();
//TableLayoutPanelの初期化
TableLayoutPanel table = new TableLayoutPanel();
table.ColumnCount = 10;
table.RowCount = label.Length / 10 + 20;
table.Dock = DockStyle.Fill;
table.AutoScroll = true;
//TableLayoutPanelに格納
TableLayoutPanelCellPosition index = new TableLayoutPanelCellPosition();//格納する場所
index.Column = 0; index.Row = -1;
foreach(Label item in label)
{
//区切りラベルだった場合:改行して、区切りの行を結合
if(item.Text == LabelMake.Separate)
{
index.Column = 0;
index.Row++;
table.Controls.Add(item, index.Column, index.Row);
table.SetColumnSpan(item, table.ColumnCount);
}
else
{
index.Row += (index.Column + 1) / table.ColumnCount;
index.Column = (index.Column + 1) % table.ColumnCount;
table.Controls.Add(item, index.Column, index.Row);
}
}
this.Controls.Add(table);
DateTime end = DateTime.Now;
Console.WriteLine("---Case 1---");
Console.WriteLine("Start : {0}", start.ToString("yyyy/MM/dd hh:mm:ss.fff"));
Console.WriteLine("End : {0}", end.ToString("yyyy/MM/dd hh:mm:ss.fff"));
Console.WriteLine("Elapsed : {0}", (end - start));
}
}

//ケース2:AddRangeで追加して後からスタイルを設定
public class Case2 : Form
{
public Case2()
{
DateTime start = DateTime.Now;
Console.WriteLine("---Case 2---");
Console.WriteLine("Start : {0}", start.ToString("yyyy/MM/dd hh:mm:ss.fff"));
this.Size = new Size(400, 300);
this.BackColor = SystemColors.Window;
//ラベル
LabelMake lm = new LabelMake();
Label[] label = lm.GetLabel();
//TableLayoutPanelの初期化
TableLayoutPanel table = new TableLayoutPanel();
table.ColumnCount = 10;
table.Dock = DockStyle.Fill;
table.AutoScroll = true;
//AddRangeで格納
table.Controls.AddRange(label);
//スタイルの設定
TableLayoutPanelCellPosition index = new TableLayoutPanelCellPosition(0, -1);
foreach(Label item in label)
{
//区切りだった場合
if (item.Text == LabelMake.Separate)
{
table.SetColumnSpan(item, table.ColumnCount);
index.Column = 0; index.Row++;
}
else
{
index.Row += (index.Column + 1) / table.ColumnCount;
index.Column = (index.Column + 1) % table.ColumnCount;
}
table.SetCellPosition(item, index);
}
this.Controls.Add(table);
DateTime end = DateTime.Now;
Console.WriteLine("End : {0}", end.ToString("yyyy/MM/dd hh:mm:ss.fff"));
Console.WriteLine("Elapsed : {0}", (end - start).ToString());
}
}






結果:
---Case 1---
Start : 2013/08/16 01:24:32.254
End : 2013/08/16 01:24:38.850
Elapsed : 00:00:06.5961003
---Case 2---
Start : 2013/08/16 01:24:56.655
End : 2013/08/16 01:25:00.861
Elapsed : 00:00:04.2060641

若干Case2のほうが速かったです。逐一場所を指定してAddで追加するよりも、AddRangeで一気に追加して後から場所を指定するほうが速くなるようです。それでも5秒近くかかっているので、実用に耐えうるかといったら微妙かも。TableLayoutPanelってこんな風に使っちゃダメなのかしら。
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク