How to make TableLayoutPanel with resizable cells

2019-05-06 04:29发布

问题:

Got TableLayoutPanel with 1 column and N rows needed to cells be resizable with somthing like Splitter component between cells. without using SplitContainer.

May be any other idea without TableLayoutPanel?

回答1:

It really depends on what you want to do with it: A DataGridView brings with it the interactivity, but its cells are neither controls nor containers..

You can try if this TableLayoutPanel subclass does what you want:

public partial class SplitTablePanel : TableLayoutPanel
{
    public int SplitterSize { get; set; }

    int sizingRow = -1;
    int currentRow = -1;
    Point mdown = Point.Empty;
    int oldHeight = -1;
    bool isNormal = false;
    List<RectangleF> TlpRows = new List<RectangleF>();
    int[] rowHeights = new int[0];

    public SplitTablePanel()
    {
        InitializeComponent();
        this.MouseDown += SplitTablePanel_MouseDown;
        this.MouseMove += SplitTablePanel_MouseMove;
        this.MouseUp += SplitTablePanel_MouseUp;
        this.MouseLeave += SplitTablePanel_MouseLeave;
        SplitterSize = 6;
    }

    void SplitTablePanel_MouseLeave(object sender, EventArgs e)
    {
        Cursor = Cursors.Default;
    }


    void SplitTablePanel_MouseUp(object sender, MouseEventArgs e)
    {
        getRowRectangles(SplitterSize);
    }

    void SplitTablePanel_MouseMove(object sender, MouseEventArgs e)
    {
        if (!isNormal) nomalizeRowStyles();
        if (TlpRows.Count <= 0) getRowRectangles(SplitterSize);
        if (rowHeights.Length <= 0) rowHeights = GetRowHeights();

        if (e.Button == System.Windows.Forms.MouseButtons.Left)
        {
            if (sizingRow < 0) return;
            int newHeight = oldHeight + e.Y - mdown.Y;
            sizeRow(sizingRow, newHeight);
        }
        else
        {
            currentRow = -1;
            for (int i = 0; i < TlpRows.Count; i++)
                if (TlpRows[i].Contains(e.Location)) { currentRow = i; break;}
            Cursor = currentRow >= 0 ? Cursors.SizeNS : Cursors.Default;
        }
    }

    void SplitTablePanel_MouseDown(object sender, MouseEventArgs e)
    {
        mdown = Point.Empty;
        sizingRow = -1;
        if (currentRow < 0) return;
        sizingRow = currentRow;
        oldHeight = rowHeights[sizingRow];
        mdown = e.Location;
    }


    void getRowRectangles(float size)
    {   // get a list of mouse sensitive rectangles
        float sz = size / 2f;
        float y = 0f;
        int w = ClientSize.Width;
        int[] rw = GetRowHeights();

        TlpRows.Clear();
        for (int i = 0; i < rw.Length - 1; i++)
        {
            y += rw[i];
            TlpRows.Add(new RectangleF(0, y - sz, w, size));
        }

    }

    void sizeRow(int row, int newHeight)
    {   // change the height of one row
        if (newHeight == 0) return;
        if (sizingRow < 0) return;
        SuspendLayout();
        rowHeights = GetRowHeights();
        if (sizingRow >= rowHeights.Length) return;

        if (newHeight > 0) 
            RowStyles[sizingRow] = new RowStyle(SizeType.Absolute, newHeight);
        ResumeLayout();
        rowHeights = GetRowHeights();
        getRowRectangles(SplitterSize);
    }

    void nomalizeRowStyles()
    {   // set all rows to absolute and the last one to percent=100!
        if (rowHeights.Length <= 0) return;
        rowHeights = GetRowHeights();
        RowStyles.Clear();
        for (int i = 0; i < RowCount - 1; i++)
        {
            RowStyle cs = new RowStyle(SizeType.Absolute, rowHeights[i]);
            RowStyles.Add(cs);
        }
        RowStyles.Add ( new RowStyle(SizeType.Percent, 100) );
        isNormal = true;
    }
}
}


回答2:

In addition to TaW 's great answer, here is further approach that allows to resize rows and columns as well and closes a little bug that appears if the control is resized (-> in TaW's code the splitter rectangles are not resized too).

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace SomeNameSpace.Controls
{   
    public partial class SplitTableLayoutPanel : TableLayoutPanel
    {
        public int SplitterSize { get; set; }

        int sizingRow = -1;
        int currentRow = -1;
        Point mdown = Point.Empty;
        int oldHeight = -1;
        bool isNormalRow = false;
        List<RectangleF> tlpRows = new List<RectangleF>();
        int[] rowHeights = new int[0];

        int sizingCol = -1;
        int currentCol = -1;
        int oldWidth = -1;
        bool isNormalCol = false;
        List<RectangleF> tlpCols = new List<RectangleF>();
        int[] colWidths = new int[0];

        public SplitTableLayoutPanel()
        {
            InitializeComponent();
            this.MouseDown += SplitTablePanel_MouseDown;
            this.MouseMove += SplitTablePanel_MouseMove;
            this.MouseUp += SplitTablePanel_MouseUp;
            this.MouseLeave += SplitTablePanel_MouseLeave;
            this.Resize += SplitTablePanel_Resize;
            SplitterSize = 6;
        }

        void SplitTablePanel_Resize(object sender, EventArgs e)
        {
            getRowRectangles(SplitterSize);
            getColRectangles(SplitterSize);
        }
        void SplitTablePanel_MouseLeave(object sender, EventArgs e)
        {
            Cursor = Cursors.Default;
        }


        void SplitTablePanel_MouseUp(object sender, MouseEventArgs e)
        {
            getRowRectangles(SplitterSize);
            getColRectangles(SplitterSize);
        }

        void SplitTablePanel_MouseMove(object sender, MouseEventArgs e)
        {
            bool r = rowMove(sender, e);
            bool c = colMove(sender, e);    

            if (r && !c)
                Cursor = Cursors.SizeNS;
            else if (!r && c)
                Cursor = Cursors.SizeWE;
            else if (r && c)
                Cursor = Cursors.SizeAll;
            else
                Cursor = Cursors.Default;
        }

        bool rowMove(object sender, MouseEventArgs e)
        {
            bool isMove = false;
            if (!isNormalRow) nomalizeRowStyles();
            if (tlpRows.Count <= 0) getRowRectangles(SplitterSize);
            if (rowHeights.Length <= 0) rowHeights = GetRowHeights();

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (sizingRow < 0) return false;
                int newHeight = oldHeight + e.Y - mdown.Y;
                sizeRow(sizingRow, newHeight);
                isMove = true;
            }
            else
            {
                currentRow = -1;
                for (int i = 0; i < tlpRows.Count; i++)
                if (tlpRows[i].Contains(e.Location)) 
                { 
                    currentRow = i;
                    isMove = true;
                    break;
                }
            }
            return isMove;
        }

        bool colMove(object sender, MouseEventArgs e)
        {
            bool isMove = false;
            if (!isNormalCol) nomalizeColStyles();
            if (tlpCols.Count <= 0) getColRectangles(SplitterSize);
            if (colWidths.Length <= 0) colWidths = GetColumnWidths();

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (sizingCol < 0) return false;
                int newWidth = oldWidth + e.X - mdown.X;
                sizeCol(sizingCol, newWidth);
                isMove = true;
            }
            else
            {
                currentCol = -1;
                for (int i = 0; i < tlpCols.Count; i++)
                if (tlpCols[i].Contains(e.Location)) 
                {
                    currentCol = i; 
                    isMove = true;
                    break;
                }
            }
            return isMove;
        }

        void SplitTablePanel_MouseDown(object sender, MouseEventArgs e)
        {
            mdown = Point.Empty;
            rowDown();   
            colDown();
            mdown = e.Location;
        }

        void rowDown()
        {
            sizingRow = -1;
            if (currentRow < 0) return;
            sizingRow = currentRow;
            oldHeight = rowHeights[sizingRow];
        }

        void colDown()
        {
            sizingCol = -1;
            if (currentCol < 0) return;
            sizingCol = currentCol;
            oldWidth = colWidths[sizingCol];
        }


        void getRowRectangles(float size)
        {   // get a list of mouse sensitive rectangles
            float sz = size / 2f;
            float y = 0f;
            int w = ClientSize.Width;
            int[] rw = GetRowHeights();

            tlpRows.Clear();
            for (int i = 0; i < rw.Length - 1; i++)
            {
                y += rw[i];
                tlpRows.Add(new RectangleF(0, y - sz, w, size));
            }

        }

        void getColRectangles(float size)
        {   // get a list of mouse sensitive rectangles
            float sz = size / 2f;
            float x = 0f;
            int h = ClientSize.Height;
            int[] rw = GetColumnWidths();

            tlpCols.Clear();
            for (int i = 0; i < rw.Length - 1; i++)
            {
                x += rw[i];
                tlpCols.Add(new RectangleF(x - sz, 0, size, h));
            }

        }

        void sizeRow(int row, int newHeight)
        {   // change the height of one row
            if (newHeight == 0) return;
            if (sizingRow < 0) return;
            SuspendLayout();
            rowHeights = GetRowHeights();
            if (sizingRow >= rowHeights.Length) return;

            if (newHeight > 0) 
                RowStyles[sizingRow] = new RowStyle(SizeType.Absolute, newHeight);
            ResumeLayout();
            rowHeights = GetRowHeights();
            getRowRectangles(SplitterSize);
        }

        void sizeCol(int col, int newWidth)
        {   // change the height of one row
            if (newWidth == 0) return;
            if (sizingCol < 0) return;
            SuspendLayout();
            colWidths = GetColumnWidths();
            if (sizingCol >= colWidths.Length) return;

            if (newWidth > 0) 
                ColumnStyles[sizingCol] = new ColumnStyle(SizeType.Absolute, newWidth);
            ResumeLayout();
            colWidths = GetColumnWidths();
            getColRectangles(SplitterSize);
        }

        void nomalizeRowStyles()
        {   // set all rows to absolute and the last one to percent=100!
            if (rowHeights.Length <= 0) return;
            rowHeights = GetRowHeights();
            RowStyles.Clear();
            for (int i = 0; i < RowCount - 1; i++)
            {
                RowStyle cs = new RowStyle(SizeType.Absolute, rowHeights[i]);
                RowStyles.Add(cs);
            }
            RowStyles.Add ( new RowStyle(SizeType.Percent, 100) );
            isNormalRow = true;
        }

        void nomalizeColStyles()
        {   // set all rows to absolute and the last one to percent=100!
            if (colWidths.Length <= 0) return;
            colWidths = GetColumnWidths();
            ColumnStyles.Clear();
            for (int i = 0; i < ColumnCount - 1; i++)
            {
                ColumnStyle cs = new ColumnStyle(SizeType.Absolute, colWidths[i]);
                ColumnStyles.Add(cs);
            }
            ColumnStyles.Add ( new ColumnStyle(SizeType.Percent, 100) );
            isNormalCol = true;
        }
    }
}