There is a form of winforms, on it TableLayoutPanel {.Dock = Fill, .RowCount = 37, .ColumnCount = 4} . screen form Everything was fine, and then there was such a glitch - two Label are visible on the screen: label1 {.Column = 0, .Row = 31} and label2 {.Column = 1, .Row = 31} . As you can see label2 is under label1, and not on the right, as it should be. Runtime is the same. The bug is not only unpleasant, it does not allow to edit the form as it should. The file MyForm.designer.cs everything is smooth:

 this.tlpEdit.Controls.Add(this.label1, 0, 31); this.tlpEdit.Controls.Add(this.label2, 1, 31); 

, in MyForm.resx there are no references at all. Who is to blame and what to do?

  • Are you sure that the Label property correctly defines the Parent property and they belong to the grid? - rdorn
  • The Parent property is not available in the designer, but its value in Runtime ==tlpEdit is 4per
  • hmm ... check also the value of colSpan , from the shaded fragment of the table you can see that some cells use column combining, maybe a side effect is rdorn

1 answer 1

On a test project, we managed to find out that this is a side effect of using the ColumnSpan property> 1. For example, there is a table 2X2. In cells [0,0] and [0,1] there are two Label . ColumnSpan both Label is 1 - everything is fine. As soon as we set ColumnSpan = 2 for the first Label in the cell [0,0], the second Label will automatically move to the nearest available cell with the coordinates [1,0], BUT, its initial coordinates will be indicated in the properties.

Further more. If the next cells are occupied, then all of them will be shifted one by one to the right, and as soon as the space to the right ends, the last cells will be pushed to the beginning of the next line. And so on all filled rows, if there are not enough rows, the missing rows will be added to the end of the table.

If you set the ColumnSpan > 1 property in the last column, it will automatically move to the next line. And if after that you place a new control in the vacant cell, then you will have 2 controls with the same coordinates on the grid.

RowSpan has a similar side effect, only it is less painful, because the column simply moves down adding the missing rows to the table.

Thus, it turns out that the coordinates of the control in the parent grid are very weakly related to its real position.

Honestly, I was unpleasantly surprised by such non-obvious behavior, but nothing remains to be noted but to take notes and use TableLayoutPanel with care.

  • As it turned out, invisible (with empty text) Labels appeared in my place from somewhere. Well, then everything according to your description. Thank. - 4per
  • @ 3per and thank you for the interesting question, I also learned something new for myself. - rdorn
  • 2
    @ 3per in general, from personal experience, the constructor in WinForms is better to use at a minimum or during the initial development of the interface, when you need to see what and where. In the code, it is somehow safer and clearer from where the legs grow, if that. - rdorn
  • Do you have any pattern for working with dynamically added controls? The fact is that I am writing now a form that it makes no sense to do in the designer, but if you generate everything yourself, you get a flat code, which is difficult to navigate in terms of structure. Of course, I can eat my FormStructureBuilder. but I could not deny myself the desire to ask how you do it, for example, you. If something is there, then you can re-register as a question. - 4per
  • @ 4per well, I would not call it a pattern, I just collect separate groups of the simplest elements in UserControl with forwarding necessary properties to the outside. In the future, I already operate with these composite entities. For most properties, the default values ​​are suitable, if the setting is complex - you can put it in a separate method or local function. Well, the current initializer syntax helps to structure a setting, which is new Class () {<setting properties>}. If the settings are always the same, you can also use inheritance and package everything into the constructor of the heir. - rdorn