How to make TableLayoutPanel with resizable cells like using Splitter

7.6k views Asked by At

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?

3

There are 3 answers

3
TaW On BEST ANSWER

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;
    }
}
}
0
dyermaker610 On

I couldn't comment on steve-e's last post because I'm a new user, but I did find a bug when I was trying out his version of the control. If you mousewheel, all the splitters cease to function. If you scroll back to the top, they're okay again. This is because whenever you mousewheel to scroll down the panel, the RectangeF Y parameter of each record in tlpRows is miscalculated and thus needs to be offset by the distance the scroll moved. I did this by adding an event for the MouseWheel and Scroll events that would call getRowRectangles and pass in a new parameter that will hold the offset (AutoScrollPosition.Y). Within the getRowRectangles method, I add the offset to the Y parameter when creating the tlpRows list.

tlpRows.Add(new RectangleF(0, y - sz + offset, w, size));
0
stev-e On

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;
        }
    }
}