问题
I am trying to dynamically add a TextBox to a TableLayoutPanel with the click of a Button. A Row is selected by click the mouse.
Following a Row selection, a button click inserts a TextBox on the selected Row number.
Problem is that after correctly displaying a TextBox 3 or 4 times on differently selected rows, further button clicks start displaying the TextBox on random rows, even if, debugging, the correct row number is shown.
I need help please, here is the full code:
public partial class Form1 : Form
{
private TableLayoutPanel _tableLayout;
private int _selectedRow = -1;
public Form1()
{
InitializeComponent();
var button = new Button()
{
Text = "Add Text"
};
button.MouseClick += AddText;
this.Controls.Add(button);
_tableLayout = new TableLayoutPanel()
{
Dock = DockStyle.Fill,
AutoScroll = true,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
CellBorderStyle = TableLayoutPanelCellBorderStyle.Single
};
_tableLayout.MouseClick += SelectRow;
this.Controls.Add(_tableLayout);
}
private void SelectRow(object sender, MouseEventArgs e)
{
_selectedRow = GetRowColIndex(_tableLayout, e.Location).Value.Y;
}
private void AddText(object sender, MouseEventArgs e)
{
_tableLayout.ColumnStyles.Add(new ColumnStyle() { SizeType = SizeType.AutoSize });
_tableLayout.RowStyles.Add(new RowStyle() { SizeType = SizeType.AutoSize });
_tableLayout.Controls.Add(new TextBox(), 0, _selectedRow);
}
Point? GetRowColIndex(TableLayoutPanel tlp, Point point)
{
if (point.X > tlp.Width || point.Y > tlp.Height)
return null;
int w = tlp.Width;
int h = tlp.Height;
int[] widths = tlp.GetColumnWidths();
int i;
for (i = widths.Length - 1; i >= 0 && point.X < w; i--)
w -= widths[i];
int col = i + 1;
int[] heights = tlp.GetRowHeights();
for (i = heights.Length - 1; i >= 0 && point.Y < h; i--)
h -= heights[i];
int row = i + 1;
return new Point(col, row);
}
}
回答1:
Some parts are missing in the current code. It doesn't account for Scrollbars and the current TableLayoutPanel's DisplayRectangle. As you can see in the .Net Source code, the TableLayoutPanel's OnPaintBackGround method already performs these calculation, to obtain the coordinates of each cell and call OnPaintCell() with a TableLayoutCellPaintEventArgs class initialized using the current cell's Bounds and ClipBounds.
You could use the same logic in your code or use the already known measures, retrieving the clicked Cell bounds from the e.CellBounds
parameter in the CellPaint event.
The example here, take the Mouse location from the TLP's MouseClick
event and uses the Rectangle.Contains(Point) method in e.CellBounds.Contains(currentPointerLocation)
to paint a Cell's background when the Cell's Bounds contain the Mouse pointer and updates a couple of Labels with current coordinates, stored in 2 fields, here, for semplicity:
Point currentPointerLocation = Point.Empty;
Rectangle currentCellBounds = Rectangle.Empty;
Point currentCell = Point.Empty;
private void tableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
if (e.CellBounds.Contains(currentPointerLocation)) {
currentCellBounds = e.CellBounds;
currentCell = new Point(e.Row, e.Column);
e.Graphics.FillRectangle(Brushes.Red, e.CellBounds);
}
else {
using (var brush = new SolidBrush(tableLayoutPanel1.BackColor)) {
e.Graphics.FillRectangle(brush, e.CellBounds);
}
}
}
private void tableLayoutPanel1_MouseClick(object sender, MouseEventArgs e)
{
currentPointerLocation = e.Location;
this.tableLayoutPanel1.Invalidate();
this.tableLayoutPanel1.Update();
lblCurrentCell.Text = currentCell.ToString();
lblCellBounds.Text = currentCellBounds.ToString();
}
With these values, it simpler to determine the current Cell's coordinates and use them when adding Controls to a Cell selected with the Mouse pointer.
For example, using a Button (btnAddControl
) and a ContextMenuStrip to add different controls to the TableLayoutPanel:
private void btnAddControl_Click(object sender, EventArgs e)
{
tableLayoutPanel1.Controls.Add(new TextBox() {
Multiline = true, Text = "TextBox from Button", Dock = DockStyle.Fill }, currentCell.Y, currentCell.X);
}
// Each ToolStripMenuItem sub-item subscribes to the event using this handler
private void contextTLPMenu_Clicked(object sender, EventArgs e)
{
Control ctl = null;
switch ((sender as ToolStripMenuItem).Text)
{
case "TextBox":
ctl = new TextBox() { Multiline = true, Text = "TextBox from ContextMenu" };
break;
case "Button":
ctl = new Button() { Text = "A Button", ForeColor = Color.White };
break;
case "Panel":
ctl = new Panel() { BackColor = Color.LightGreen };
break;
default:
break;
}
if (ctl != null) {
ctl.Dock = DockStyle.Fill;
tableLayoutPanel1.Controls.Add(ctl, currentCell.Y, currentCell.X);
}
}
Visual result:
来源:https://stackoverflow.com/questions/59017507/dynamically-added-rows-to-a-tablelayoutpanel-are-displayed-on-a-different-row-po