1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# の自作テーブル

Posted at
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
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?