[英]How to make TableLayoutPanel with resizable cells like using Splitter
獲得了具有 1 列和 N 行的 TableLayoutPanel,需要使用單元格之間的 Splitter 組件來調整單元格的大小。 不使用 SplitContainer。
沒有 TableLayoutPanel 可能還有其他想法嗎?
這實際上取決於您想用它做什么: DataGridView
帶來了交互性,但它的單元格既不是控件也不是容器。
您可以嘗試此TableLayoutPanel
子類是否滿足您的要求:
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 的好答案之外,這里還有進一步的方法,它允許調整行和列的大小,並關閉調整控件大小時出現的小錯誤(-> 在 TaW 的代碼中,拆分器矩形也沒有調整大小)。
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;
}
}
}
由於我是新用戶,我無法對 steve-e 的最后一篇文章發表評論,但是當我嘗試他的控件版本時確實發現了一個錯誤。 如果您使用鼠標滾輪,所有分離器都將停止工作。 如果您滾動回頂部,它們又可以了。 這是因為每當您使用鼠標滾輪向下滾動面板時,tlpRows 中每條記錄的 RectangeF Y 參數都會計算錯誤,因此需要通過滾動移動的距離來偏移。 為此,我為 MouseWheel 和 Scroll 事件添加了一個事件,該事件將調用getRowRectangles
並傳入一個新參數,該參數將保存偏移量 ( AutoScrollPosition.Y
)。 在getRowRectangles
方法中,我在創建 tlpRows 列表時將偏移量添加到 Y 參數。
tlpRows.Add(new RectangleF(0, y - sz + offset, w, size));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.