2

I have a simple winform application which allows users to drag controls to a tablelayoutpanel. But after some testing and trying to drag controls to a specific index row, I found out it doesn't work, not even with a hardcoded index number.

With the provided code example, I'm trying to add a textbox to row index 2, but when I drag content from the listbox to the tablelayoutpanel, it just adds the textbox in 'random' places as seen in the screenshot below

enter image description here

I expect the existing textboxes to shift down and make place for the textbox that's being added, as far as I understand from this: https://social.msdn.microsoft.com/Forums/windows/en-US/e4312cd8-6031-4a5c-92bf-e8adb1941fe5/insert-row-at-particular-position-in-table-layout-panel?forum=winforms.

Am I doing something wrong?

Designer code:

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.listBox1 = new System.Windows.Forms.ListBox();
        this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
        this.SuspendLayout();
        // 
        // listBox1
        // 
        this.listBox1.FormattingEnabled = true;
        this.listBox1.Location = new System.Drawing.Point(367, 12);
        this.listBox1.Name = "listBox1";
        this.listBox1.Size = new System.Drawing.Size(190, 407);
        this.listBox1.TabIndex = 0;
        this.listBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.listBox1_MouseDown);
        // 
        // tableLayoutPanel1
        // 
        this.tableLayoutPanel1.ColumnCount = 1;
        this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
        this.tableLayoutPanel1.Location = new System.Drawing.Point(13, 12);
        this.tableLayoutPanel1.Name = "tableLayoutPanel1";
        this.tableLayoutPanel1.RowCount = 1;
        this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
        this.tableLayoutPanel1.Size = new System.Drawing.Size(332, 407);
        this.tableLayoutPanel1.TabIndex = 1;
        this.tableLayoutPanel1.DragDrop += new System.Windows.Forms.DragEventHandler(this.tableLayoutPanel1_DragDrop);
        this.tableLayoutPanel1.DragEnter += new System.Windows.Forms.DragEventHandler(this.tableLayoutPanel1_DragEnter);
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(569, 431);
        this.Controls.Add(this.tableLayoutPanel1);
        this.Controls.Add(this.listBox1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.ListBox listBox1;
    private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
}

Form code:

public partial class Form1 : Form
{
    private int tempInt = 0;

    public Form1()
    {
        InitializeComponent();
        listBox1.Items.AddRange(new object[] { "test" });
        tableLayoutPanel1.AllowDrop = true;
        tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
        tableLayoutPanel1.AutoScroll = true;
        tableLayoutPanel1.RowCount = 3;
    }

    private void listBox1_MouseDown(object sender, MouseEventArgs e)
    {
        tempInt++;
        DoDragDrop("test" + tempInt, DragDropEffects.Copy);
    }

    private void tableLayoutPanel1_DragEnter(object sender, DragEventArgs e)
    {
        e.Effect = DragDropEffects.Copy;
    }

    private void tableLayoutPanel1_DragDrop(object sender, DragEventArgs e)
    {
        string text = e.Data.GetData(typeof(String)) as string;
        TextBox tb = new TextBox();
        tb.Dock = DockStyle.Fill;
        tb.Text = text;
        // I want to add the textbox to the second row
        tableLayoutPanel1.Controls.Add(tb, 0, 2);
        tableLayoutPanel1.SetRow(tb, 2);
    }
}

EDIT:

Added code based as DonBoitnott's suggested

    private void tableLayoutPanel1_DragDrop(object sender, DragEventArgs e)
    {
        string text = e.Data.GetData(typeof(String)) as string;
        TextBox tb = new TextBox();
        tb.Dock = DockStyle.Fill;
        tb.Text = text;
        tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
        {
            int pos = tableLayoutPanel1.GetRow(tableLayoutPanel1.Controls[i]);
            if (pos > 1)
            {
                tableLayoutPanel1.SetRow(tableLayoutPanel1.Controls[i], pos + 1);
            }
        }
        tableLayoutPanel1.Controls.Add(tb, 0, 2);
    }
Denny
  • 1,766
  • 3
  • 17
  • 37
  • 1
    Describe in more detail what you expect to happen to the **existing** TextBoxes when you add to Row 2... – Idle_Mind Feb 06 '17 at 20:14
  • Okay I've added an explaination, also added a link to the source of the information. – Denny Feb 06 '17 at 20:18
  • You've got one at row 2 already. You then attempt to add another, that's bad. Add a RowStyle for the bottom, slide them all down, then put your new one in the newly emptied slot #2. – DonBoitnott Feb 06 '17 at 22:03
  • @DonBoitnott I've added some new code, I can't tell why it's still not working, probably still a fault somewhere, could you take a look at it? – Denny Feb 06 '17 at 23:31
  • 1
    Use FlowLayoutPanel for the more obvious "shift down" behavior. – Hans Passant Feb 07 '17 at 14:00
  • @HansPassant The reason why I didn't choose for a flowlayoutpanel is because I only want a single column, and described here: http://stackoverflow.com/questions/5395754/flowlayoutpanel-automatic-width-for-controls, it seemed to me that using a tablelayoutpanel would be more effective. – Denny Feb 07 '17 at 15:12

1 Answers1

0

Give this modified form class a try, I've made a few changes throughout:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            listBox1.Items.AddRange(new Object[] { "TextBox" });
            tableLayoutPanel1.AllowDrop = true;
            tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
            tableLayoutPanel1.AutoScroll = true;
            tableLayoutPanel1.RowStyles.Clear();
            tableLayoutPanel1.ColumnStyles.Clear();
            tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
        }

        private void listBox1_MouseDown(Object sender, MouseEventArgs e)
        {
            var count = tableLayoutPanel1.Controls.Count;
            DoDragDrop($"test{count + 1}", DragDropEffects.Copy);
        }

        private void tableLayoutPanel1_DragEnter(Object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy;
        }

        private void tableLayoutPanel1_DragDrop(Object sender, DragEventArgs e)
        {
            var tb = new TextBox();
            tb.Dock = DockStyle.Fill;
            tb.Text = (e.Data.GetData(typeof(String)) as String);

            var newRow = tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            var ctrl = tableLayoutPanel1.GetChildAtPoint(tableLayoutPanel1.PointToClient(new Point(e.X, e.Y)));
            if (ctrl != null)
            {
                var pos = tableLayoutPanel1.GetRow(ctrl);
                for (Int32 i = tableLayoutPanel1.RowStyles.Count - 2; i >= pos; i--)
                {
                    var c = tableLayoutPanel1.GetControlFromPosition(0, i);
                    if (c != null)
                        tableLayoutPanel1.SetRow(c, i + 1);
                }
                tableLayoutPanel1.Controls.Add(tb, 0, pos);
            }
            else
                tableLayoutPanel1.Controls.Add(tb, 0, newRow);
        }
    }

The basic steps are:

  1. Did I drop onto an existing control? If so, determine where.
  2. If so, work bottom to top, moving each control down a slot. You must avoid overlapping, two controls cannot occupy the same cell.
  3. If so, insert the new control into the newly emptied slot at the drop point.
  4. If not, simply add the control at the end.
DonBoitnott
  • 10,787
  • 6
  • 49
  • 68