TableCellText.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace CalculationForms1
{
#region ------- enum --------------
public enum MyTextAlign {
TopLeft,
TopCenter,
TopRight,
MiddleLeft,
MiddleCenter,
MiddleRight
}
public enum MergedControlKind {
Auto, // Control があればそれ、なければ TextBox
TextBox, // 強制で TextBox 生成
Label, // 強制で Label 生成
PanelLabel // 見栄え用:Panel+Label(枠線・中央揃え)
}
#endregion
#region ---- 自作のセル結合可能なテーブルのユーザーコントロール -------
public partial class TableCellText : UserControl
{
private int rowCount = 3;
private int columnCount = 3;
#region 行数・列数プロパティ(設定時にレイアウトを再適用)
[Category("Layout")]
[Description("行数")]
[RefreshProperties(RefreshProperties.All)]
public int RowCount
{
get => rowCount;
set
{
rowCount = value;
ApplyLayout();
}
}
[Category("Layout")]
[Description("列数")]
[RefreshProperties(RefreshProperties.All)]
public int ColumnCount
{
get => columnCount;
set
{
columnCount = value;
ApplyLayout();
}
}
#endregion
[Category("Layout")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<MergedCell> MergedCells { get; set; } = new List<MergedCell>();
[TypeConverter(typeof(ExpandableObjectConverter))]
public class MergedCell
{
public Control Control { get; set; }
public int Row { get; set; }
public int Column { get; set; }
public int RowSpan { get; set; } = 1;
public int ColSpan { get; set; } = 1;
// 新しく追加(既定はTextBox)
public MergedControlKind Kind { get; set; } = MergedControlKind.Auto;
public override string ToString() => $"({Row},{Column}) {ColSpan}x{RowSpan}";
}
#region レイアウト適用処理(行列数や結合セルに応じて再構築)
private void ApplyLayout() {
tableLayoutPanel1.SuspendLayout();
try {
//VarDumper.DebugLog("ラベル");
tableLayoutPanel1.Controls.Clear();
tableLayoutPanel1.RowStyles.Clear();
tableLayoutPanel1.ColumnStyles.Clear();
tableLayoutPanel1.RowCount = RowCount;
tableLayoutPanel1.ColumnCount = ColumnCount;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.FixedSize; // 追加: 余計な行列の自動増を防止
for (int r = 0; r < RowCount; r++)
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100f / RowCount));
for (int c = 0; c < ColumnCount; c++)
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / ColumnCount));
bool[,] occupied = new bool[RowCount, ColumnCount];
foreach (var cell in MergedCells) {
// 範囲チェック(はみ出しはスキップ or 例外)
if (cell.Row < 0 || cell.Column < 0 ||
cell.Row + cell.RowSpan > RowCount ||
cell.Column + cell.ColSpan > ColumnCount) {
// throw new InvalidOperationException("結合範囲が表外です。");
continue;
}
// 既存結合との重なりチェック(任意)
for (int rr = 0; rr < cell.RowSpan; rr++)
for (int cc = 0; cc < cell.ColSpan; cc++)
if (occupied[cell.Row + rr, cell.Column + cc])
; // ここで例外 or continue; 好みで
// throw new InvalidOperationException("結合セルが重複しています。");
// 占有マーキング
for (int rr = 0; rr < cell.RowSpan; rr++)
for (int cc = 0; cc < cell.ColSpan; cc++)
occupied[cell.Row + rr, cell.Column + cc] = true;
// コントロール生成
Control ctrl;
if (cell.Control != null) {
ctrl = cell.Control;
} else {
switch (cell.Kind) {
case MergedControlKind.Label:
ctrl = new Label {
Dock = DockStyle.Fill,
AutoSize = false,
Margin = new Padding(0),
TextAlign = ContentAlignment.MiddleCenter,
BorderStyle = BorderStyle.FixedSingle
};
break;
case MergedControlKind.PanelLabel:
ctrl = CreateCellLabel(string.Empty, MyTextAlign.MiddleCenter, Color.White); // Panel+Label
break;
case MergedControlKind.TextBox:
case MergedControlKind.Auto:
default:
ctrl = new TextBox {
Dock = DockStyle.Fill,
Margin = new Padding(0),
Multiline = true,
BorderStyle = BorderStyle.FixedSingle
};
break;
}
}
// ★ここは1回だけ!
tableLayoutPanel1.Controls.Add(ctrl, cell.Column, cell.Row);
if (cell.RowSpan > 1) tableLayoutPanel1.SetRowSpan(ctrl, cell.RowSpan);
if (cell.ColSpan > 1) tableLayoutPanel1.SetColumnSpan(ctrl, cell.ColSpan);
}
// 空きセルをTextBoxで埋める
for (int r = 0; r < RowCount; r++) {
for (int c = 0; c < ColumnCount; c++) {
if (!occupied[r, c]) {
var tb = new TextBox {
Dock = DockStyle.Fill,
Margin = new Padding(0),
Multiline = true,
BorderStyle = BorderStyle.FixedSingle
};
tableLayoutPanel1.Controls.Add(tb, c, r);
}
}
}
} finally {
tableLayoutPanel1.ResumeLayout(true);
// tableLayoutPanel1.PerformLayout(); // 必要なら
}
}
#endregion
#region コンストラクタ(初期化とレイアウト適用)
public TableCellText()
{
InitializeComponent();
tableLayoutPanel1.CellBorderStyle = TableLayoutPanelCellBorderStyle.None;
tableLayoutPanel1.Padding = new Padding(0);
tableLayoutPanel1.Margin = new Padding(0);
tableLayoutPanel1.RowCount = rowCount;
tableLayoutPanel1.ColumnCount = columnCount;
ApplyLayout();
}
#endregion
#region ラベル作成(指定テキスト・整列・背景色)
public static Control CreateCellLabel(string text, MyTextAlign align = MyTextAlign.MiddleCenter, Color? backColor = null)
{
var label = new Label
{
Text = text,
Dock = DockStyle.Fill,
TextAlign = ToContentAlignment(align),
BackColor = backColor ?? Color.White,
Margin = new Padding(0),
AutoSize = false // ← 必須!サイズ固定
};
var panel = new Panel
{
BorderStyle = BorderStyle.FixedSingle,
Margin = new Padding(0),
Dock = DockStyle.Fill,
BackColor = Color.White,
AutoSize = false // ← 重要!Labelの中央揃えが崩れるのを防止
};
panel.Controls.Add(label);
return panel;
}
#endregion
#region 水平・垂直方向の整列変換(MyTextAlign → ContentAlignment)
public static ContentAlignment ToContentAlignment(MyTextAlign align)
{
switch (align)
{
case MyTextAlign.TopLeft: return ContentAlignment.TopLeft;
case MyTextAlign.TopCenter: return ContentAlignment.TopCenter;
case MyTextAlign.TopRight: return ContentAlignment.TopRight;
case MyTextAlign.MiddleLeft: return ContentAlignment.MiddleLeft;
case MyTextAlign.MiddleCenter: return ContentAlignment.MiddleCenter;
case MyTextAlign.MiddleRight: return ContentAlignment.MiddleRight;
default: return ContentAlignment.MiddleCenter;
}
}
#endregion
#region 結合セル追加
public void AddMergedControl(Control control, int column, int row, int colSpan, int rowSpan)
{
MergedCells.Add(new MergedCell
{
Control = control,
Row = row,
Column = column,
RowSpan = rowSpan,
ColSpan = colSpan
});
ApplyLayout();
}
#endregion
#region 結合セルの全削除
public void ClearMergedCells()
{
MergedCells.Clear();
ApplyLayout();
}
#endregion
#region 指定セルへの値設定(ラベル・テキストボックス・パネル内ラベル対応)
public void SetCellValue(int row, int column, string value, ContentAlignment labelAlignment = ContentAlignment.MiddleCenter)
{
foreach (Control ctrl in tableLayoutPanel1.Controls)
{
if (tableLayoutPanel1.GetRow(ctrl) == row && tableLayoutPanel1.GetColumn(ctrl) == column)
{
switch (ctrl)
{
case TextBox tb:
tb.Text = value;
return;
case Label lbl:
lbl.Text = value;
lbl.TextAlign = labelAlignment;
return;
case Panel pnl:
// パネル内にラベルがあるかをチェックして設定
var labelInPanel = pnl.Controls.OfType<Label>().FirstOrDefault();
if (labelInPanel != null)
{
labelInPanel.Text = value;
//labelInPanel.TextAlign = labelAlignment;
}
return;
}
}
}
}
// TableCellText.cs の中で
public void SetCellValue(int row, int col, Control control) {
control.Dock = DockStyle.Fill;
// 既存の同じ位置のコントロールがあれば削除(必要であれば)
Control oldControl = tableLayoutPanel1.GetControlFromPosition(col, row);
if (oldControl != null) {
tableLayoutPanel1.Controls.Remove(oldControl);
}
// 追加
tableLayoutPanel1.Controls.Add(control, col, row);
}
#endregion
#region 指定行の全セルに値を設定(ラベル/テキストボックス)
public void SetRowValues(int rowIndex, string[] values, bool centerAlign = false)
{
// ... 既存の SetRowValues 内容 ...
}
#endregion
#region ラベルとして行全体に値を設定(結合済みセルを考慮)
public void SetRowValuesAsLabels(int rowIndex, string[] values, MyTextAlign align = MyTextAlign.MiddleCenter, Color? backColor = null)
{
for (int col = 0; col < values.Length; col++)
{
Control existingCtrl = tableLayoutPanel1.GetControlFromPosition(col, rowIndex);
if (existingCtrl != null)
{
int startCol = tableLayoutPanel1.GetColumn(existingCtrl);
int startRow = tableLayoutPanel1.GetRow(existingCtrl);
int colSpan = tableLayoutPanel1.GetColumnSpan(existingCtrl);
int rowSpan = tableLayoutPanel1.GetRowSpan(existingCtrl);
// 自分が結合セルの途中ならスキップ(上書きしない)
if ((col != startCol || rowIndex != startRow) && (colSpan > 1 || rowSpan > 1))
continue;
// 結合セルの先頭であっても、手動で追加されたものは残す
if (MergedCells.Any(m => m.Control == existingCtrl))
continue;
// 通常のセルなら削除して置き換え
tableLayoutPanel1.Controls.Remove(existingCtrl);
}
// ラベル追加
Control ctrl = CreateCellLabel(values[col], align, backColor);
tableLayoutPanel1.Controls.Add(ctrl, col, rowIndex);
}
}
#endregion
#region 任意のセルコントロールを取得(指定行・列から検索)
public Control GetControlAt(int row, int col)
{
foreach (Control ctrl in tableLayoutPanel1.Controls)
{
if (tableLayoutPanel1.GetRow(ctrl) == row &&
tableLayoutPanel1.GetColumn(ctrl) == col)
{
return ctrl;
}
}
return null;
}
#endregion
#region 表の行を非表示する関数
public void SetRowVisible(int rowIndex, bool visible)
{
if (rowIndex < 0 || rowIndex >= tableLayoutPanel1.RowCount)
return;
tableLayoutPanel1.SuspendLayout();
if (visible)
{
// 表示する場合:元の高さを復元(例:均等割)
tableLayoutPanel1.RowStyles[rowIndex].Height = 100f / RowCount;
tableLayoutPanel1.RowStyles[rowIndex].SizeType = SizeType.Percent;
} else
{
// 非表示にする場合:高さ0に設定
tableLayoutPanel1.RowStyles[rowIndex].Height = 0;
tableLayoutPanel1.RowStyles[rowIndex].SizeType = SizeType.Absolute;
}
// 各コントロール自体も非表示にする
foreach (Control ctrl in tableLayoutPanel1.Controls)
{
if (tableLayoutPanel1.GetRow(ctrl) == rowIndex)
{
ctrl.Visible = visible;
}
}
tableLayoutPanel1.ResumeLayout();
// ----[ 使用例 ]---
//// 3行目を非表示にする
//tableCellText1.SetRowVisible(2, false);
//// 5行目を非表示にする
//tableCellText1.SetRowVisible(4, false);
//// 7行目を非表示にする
//tableCellText1.SetRowVisible(6, false);
}
#endregion
}
#endregion
}