Question

I have a TableLayoutPanel and I want to add a control to the cell that I click on.

The problem is that I can't determine the cell that I click on at run time.

How do I determine the cell being clicked on?

Was it helpful?

Solution

You can use GetColumnWidths and GetRowHeights methods to calculate the cell row and column index:

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

Usage:

private void tableLayoutPanel1_Click(object sender, EventArgs e)
{
    var cellPos = GetRowColIndex(
        tableLayoutPanel1,
        tableLayoutPanel1.PointToClient(Cursor.Position));
}

But notice that the click event only is raised if the cell does not already contain a control.

OTHER TIPS

This worked for me:

public TableLayoutPanel tableLayoutPanel { get; set; }

private void Form_Load(object sender, EventArgs e)
{
    foreach (Panel space in this.tableLayoutPanel.Controls)
    {
        space.MouseClick += new MouseEventHandler(clickOnSpace);
    }
}

public void clickOnSpace(object sender, MouseEventArgs e)
{

    MessageBox.Show("Cell chosen: (" + 
                     tableLayoutPanel.GetRow((Panel)sender) + ", " + 
                     tableLayoutPanel.GetColumn((Panel)sender) + ")");
}

Note that my tableLayoutPanel is declared globally so that I can just use it without having to pass it to each function. Also, both the tableLayoutPanel and each Panel within it are created completely programatically elsewhere (my form [design] is completely blank).

My answer is based on @Mohammad Dehghan's answer above but has a couple of advantages:

  • It now takes into account vertical scrolling
  • The columns are in the correct order (starts at i=0 instead of i=length), meaning columns of different widths or heights are processed in the correct order

Here is the updated version of the code:

public Point? GetIndex(TableLayoutPanel tlp, Point point)
{
    // Method adapted from: stackoverflow.com/a/15449969
    if (point.X > tlp.Width || point.Y > tlp.Height)
        return null;

    int w = 0, h = 0;
    int[] widths = tlp.GetColumnWidths(), heights = tlp.GetRowHeights();

    int i;
    for (i = 0; i < widths.Length && point.X > w; i++)
    {
        w += widths[i];
    }
    int col = i - 1;

    for (i = 0; i < heights.Length && point.Y + tlp.VerticalScroll.Value > h; i++)
    {
        h += heights[i];
    }
    int row = i - 1;

    return new Point(col, row);
}

Nick's answer was the best solution, except that it can be made generic for TableLayoutPanels that contain different kinds of controls in the cells. Just change the explicit "Panel" type to "Control":

public TableLayoutPanel tableLayoutPanel { get; set; }

private void Form_Load(object sender, EventArgs e)
{
    foreach (Control c in this.tableLayoutPanel.Controls)
    {
        c.MouseClick += new MouseEventHandler(ClickOnTableLayoutPanel);
    }
}

public void ClickOnTableLayoutPanel(object sender, MouseEventArgs e)
{

    MessageBox.Show("Cell chosen: (" + 
                     tableLayoutPanel.GetRow((Control)sender) + ", " + 
                     tableLayoutPanel.GetColumn((Control)sender) + ")");
}

This works great and doesn't require doing coordinate math to find which cell was clicked.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top