7

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?

E.Monogarov
  • 460
  • 8
  • 17
  • 1
    You would probably be best using a single column `DataGridView` with `AllowUserToResizeRows` set to true and turn of row and column headers. – Anthony Jun 26 '15 at 12:34
  • For only 1 column, a flowlayoutpanel might be handier (flowdirection topdown and wrapcontents=false) and the splitter would only have to resize the controls, the flowlayout will follow. As it happens, there already is a project that implements a flowlayout with splitters: http://www.codeproject.com/Articles/43158/MultisplitContainer-A-FlowlayoutPanel-Providing-Si . Haven't tried it, but it might be what you need. – Me.Name Jun 27 '15 at 09:09

3 Answers3

8

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;
    }
}
}
TaW
  • 53,122
  • 8
  • 69
  • 111
  • How to set splitter width ? – Gray Programmerz Mar 01 '23 at 10:41
  • Do you mean my property `SplitterSize` ? – TaW Mar 01 '23 at 14:18
  • Sorry. Like we use `SplitContainer` and it has property named [SplitterWidth](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.splitcontainer.splitterwidth). Increasing it will make border (that we grab) more wider and easy to grab. Any alternative of that in this code ? – Gray Programmerz Mar 01 '23 at 17:47
7

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;
        }
    }
}
André
  • 4,417
  • 4
  • 29
  • 56
stev-e
  • 436
  • 6
  • 15
1

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