I am writing my Windows Forms app. And I have some problem with TableLayoutContainer element. Or rows are superpose (without needed scroll bar) or there is a big interval between first and second row.
I need a container with dynamic changing sizes according to Form size, with auto vertical scroll (if the container's size to big). Please help me to correct my code or container's properties.
Label LabelG = new Label[len];
NumericUpDown NumberControlBars = new NumericUpDown[len];
for (int i = 0; i < len; i++)
{
TablePanelContainer.RowCount++;
TablePanelContainer.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F));
LabelG[i] = new System.Windows.Forms.Label();
LabelG[i].Name = "Label" + i.ToString();
LabelG[i].Size = new System.Drawing.Size(40, 23);
LabelG[i].Text = Groups[i].ToString();
LabelG[i].Dock = DockStyle.Right;
LabelG[i].Anchor = (AnchorStyles.Right | AnchorStyles.Top);
LabelG[i].TextAlign = ContentAlignment.MiddleRight;
TablePanelContainer.Controls.Add(LabelG[i], 0, i);
NumberControlBars[i] = new System.Windows.Forms.NumericUpDown();
NumberControlBars[i].Name = "Label" + i.ToString();
NumberControlBars[i].MaximumSize = new System.Drawing.Size(40,23);
NumberControlBars[i].Text = "0";
NumberControlBars[i].Dock = DockStyle.Left;
NumberControlBars[i].Anchor = (AnchorStyles.Left | AnchorStyles.Top);
TablePanelContainer.Controls.Add(NumberControlBars[i], 1, i);
}
Properties
Bug
I have recreated your functionality in a small app. These are the settings for your TablePanelLayout control
Anchor: Top, Bottom, Left, Right
AutoScroll: True
AutoSize: False
Specially the AutoSize setting to false is important. If you don't do that the control will resize itself to the height needed to accommodate all rows. Because the container has enough space in that case to show everything it will not show the scrollbars. It doesn't care that its size doesn't fit on the form.
This is what the designer should look like:
To overcome the quirks with the first row I adapted the Style of that first row. It looks like the designer plays some tricks here. Your code will look like this.
tableLayoutPanel1.SuspendLayout();
// adapt styling of first row
if (tableLayoutPanel1.RowStyles.Count > 0)
{
tableLayoutPanel1.RowStyles[0].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[0].Height = 25F;
}
for(int i=0; i<100; i++)
{
var lbl = new Label();
lbl.Text = i.ToString();
tableLayoutPanel1.Controls.Add(lbl, 0, i);
var num = new NumericUpDown();
tableLayoutPanel1.Controls.Add(num,1 ,i);
tableLayoutPanel1.RowCount++;
}
tableLayoutPanel1.ResumeLayout();
When run this is the result:
Related
I use this code to add some labels to a windows form in c#:
Label[] lbl = new Label[temp+1];
for (int i = 0; i <= temp; i++) {
lbl[i] = new Label();
lbl[i].Text = "" + i;
lbl[i].Location = new Point(30 + (i * unit), 380);
lbl[i].Visible = true;
this.Controls.Add(lbl[i]);
}
There is not a serious problem but my code works for temps less than 5 and for temps greater than 5 it shows only the first one.
What you think? where is the problem?
Make the labels automatically adjust their size to their content by setting their AutoSize property to true:
lbl[i] = new Label();
lbl[i].Text = "" + i;
lbl[i].Location = new Point(30 + (i * unit), 380);
lbl[i].Visible = true;
lbl[i].AutoSize = true;
this.Controls.Add(lbl[i]);
Without this, the labels have a fixed size. When this fixed size is greater than unit, the labels overlap and hide each other's text. With more labels to add, unit becomes smaller and then smaller than the default width of the labels when temp is ≥ 5.
Alternatively, you could set the labels' width to unit to make sure that they do not overlap.
The problem
I'm dynamically adding Buttons to the WinForm. As I do so, I'm repositioning existing Buttons to prevent overlap. The AutoSize property is being used to automatically set Width.
For longer text (that pushes Buttons beyond their default Width), the below code doesn't work.
For example:
b.Width is 75 before AutoSize is set
b.Width is 75 after AutoSize is set
When shifting other Buttons, it shifts them by b.Width + buffer = 83
However after addButton()completes, the AutoSize kicks in and sets the width to 150, overlapping the next Button which is only 83 pixels away instead of 158.
AutoSize appears to change the size of the control too late for it to be of use. How can I make it happen immediately?
Attempt 1 - Code
public void addButton(string text)
{
const int buffer = 8;
//Construct new button
Button b = new Button();
b.Text = text;
b.AutoSize = true;
b.Location = new Point(0, 0);
//Shift over all other buttons to prevent overlap
//b.Width is incorrect below, because b.AutoSize hasn't taken effect
for (int i = 0; i < Controls.Count; i++)
if (Controls[i] is Button)
Controls[i].Location = new Point(Controls[i].Location.X + b.Width + buffer, Controls[i].Location.Y);
Controls.add(b);
}
Attempt 2
Searched Google and StackOverflow for the following:
c# autosize immediately
c# autosize fast
c# autosize not working
Attempt 3
Asking here.
Last Resort
If nothing else works, a timer could be set to reposition Buttons on each tick. However this is very sloppy design, and doesn't aid in learning the intricacies of AutoSize. I'd like to avoid this workaround if possible.
The AutoSize and AutoSizeMode mode are applied only when the control is parented to the another control or form.
So invoke first
Controls.Add(b);
Now the b.Size will the adjusted accordingly and can be used in the calculations.
Alternatively, instead of Size property you can use the GetPreferredSize method to get the correct size without actually applying AutoSize and use it inside the calculations:
var bSize = b.GetPreferredSize(Size.Empty);
//Shift over all other buttons to prevent overlap
//b.Width is incorrect below, because b.AutoSize hasn't taken effect
for (int i = 0; i < Controls.Count; i++)
if (Controls[i] is Button)
Controls[i].Location = new Point(Controls[i].Location.X + bSize.Width + buffer, Controls[i].Location.Y);
The FlowLayoutPanel control does this work for you.
Place one on your form and try adding buttons in the following manner:
Button b = new Button();
b.AutoSize = true;
b.Text = text;
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel1.Controls.Add(b);
flowLayoutPanel1.Controls.SetChildIndex(b, 0);
flowLayoutPanel1.ResumeLayout();
You can subscribe to the Resize event of the last button added. This will allow you to accurately change the locations of all of the buttons because now all of the buttons have been AutoSized.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var button1 = NewButton(0);
button1.Location = new Point(10, 10);
var button2 = NewButton(1);
button2.Location = new Point(button1.Right, 10);
var button3 = NewButton(2);
button3.Location = new Point(button2.Right, 10);
button3.Resize += (s, e) =>
{
button2.Location = new Point(button1.Right, 10);
button3.Location = new Point(button2.Right, 10);
};
Controls.Add(button1);
Controls.Add(button2);
Controls.Add(button3);
}
private Button NewButton(int index)
{
return new Button()
{
Text = "ButtonButtonButton" + index.ToString(),
AutoSize = true
};
}
}
I have the problem that the text inside my panel gets cut of strangely. The panel is located inside a textbox. But even if I replace the textbox by a flowlayoutpanel, I have the same issue.
Code:
List<string> list = datenbank.FerienAuswahl(monat, jahr);
int i = 0;
//Create Panel
try
{
//Fill Panel
do
{
Label panel = new Label();
panel.Name = "panel" + i;
panel.Height = 30;
panel.Width = 400;
panel.AutoSize = false;
panel.TextAlign = ContentAlignment.MiddleCenter;
panel.ForeColor = Color.Black;
panel.Text = list[i];
Label ferien = new Label();
panel.Controls.Add(ferien);
tbFerien.Controls.Add(panel);
i++;
} while (i < list.Count);
}
catch { }
Result:
I have already tried to change the width of the panel. But as result I only get a messed up alignment of the text.
The only settings of the textbox I have changed are these:
Multiline: True
TextAlign: Center
Size: 359; 125
Does Someone know what else I could try ?
These lines worry me:
Label panel = new Label();
Label ferien = new Label();
panel.Controls.Add(ferien);
tbFerien.Controls.Add(panel);
It seems to me you are adding one label to another. That's not good. Use a Panel or TableLayoutPanel instead of the actual panel and make sure you have your positioning good.
I have lots of buttons on flowlayoutpanel, and then there's text labels to break the flow. Last button before label and label itself has SetFlowBreak. All works kind of fine, but what I don't understand, is why there is so much space under the text label? If form is resized so narrow that there's only one column of buttons, then the unwanted space disappears. Can someone explain how that space can be removed?
Code:
public Form1()
{
InitializeComponent();
for (int i = 1; i <= 100; i++)
{
Button button = new Button();
button.Text = i.ToString();
button.Width = 150;
button.Height = 50;
button.Margin = new Padding(5);
flowLayoutPanel1.Controls.Add(button);
if (i % 10 == 0)
{
flowLayoutPanel1.SetFlowBreak(button, true);
Label label = new Label();
label.Text = "Some random text";
label.AutoSize = true;
label.Margin = new Padding(5, 5, 0, 0);
label.BackColor = ColorTranslator.FromHtml("#ccc");
flowLayoutPanel1.Controls.Add(label);
flowLayoutPanel1.SetFlowBreak(label, true);
}
}
}
And couple of images to show what I mean:
Image1: Strange space under the Label
Image2: No space under the Label when the form is resized (this is how I'd like this to work)
Thank you Hans! I thinks this is a real answer, as it solved my problem: (quote from comments)
It is a bug, same one as this one. The extra space is the height of the next label. The workaround is exactly the same, just add a dummy control with a Width of 0 after the label. – Hans Passant
So first I removed flowbreak after the real label:
flowLayoutPanel1.SetFlowBreak(label, true);
And then replaced it with the following code, and the mysterious space disappeared!
Label dummyLabel = new Label();
dummyLabel.Width = 0;
dummyLabel.Height = 0;
dummyLabel.Margin = new Padding(0, 0, 0, 0);
flowLayoutPanel1.Controls.Add(dummyLabel);
flowLayoutPanel1.SetFlowBreak(dummyLabel, true);
for (int i = 0; i < 200; i++)
{
Control control = new Control();
control = new CheckBox();
Size size = control.Size;
Point point = new Point(20, 22);
control.Location = point;
int width = size.Width + 5;
i += width;
list.Add(control);
}
foreach(Control c in list)
{
}
how do I create a new instance of checkbox? Because this way I am getting just one checkbox each time. I want to get three checkbox in each row.
Is this winforms? A first point: you don't need the new Control() each time (you simly discard it anyway when you new CheckBox(). How exactly do you want the layout to appear? Can you describe it a bit more please?
I imagine TableLayoutPanel might be a reasonable start...
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Form form = new Form();
TableLayoutPanel layout = new TableLayoutPanel();
layout.Dock = DockStyle.Fill;
form.Controls.Add(layout);
layout.AutoScroll = true;
layout.ColumnCount = 3;
// size the columns (choice just to show options, not to be pretty)
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
layout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
layout.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
for (int i = 0; i < 200; i++)
{
CheckBox chk = new CheckBox();
chk.Text = "item " + i;
layout.Controls.Add(chk);
}
Application.Run(form);
}
Otherwise, you'll need to manually set the Location (or Top and Left) of each; not simple.
Your code has problems. Let's work from sample code rather than a lesson. I'll create a Panel first, nice if you want to remove the checkboxes you created. You'd probably be interested in the user clicking a checkbox so lets add an event for that. Start a new WF project and drop a button on the form. Double click it, then paste this code:
private void button1_Click(object sender, EventArgs e) {
// Give the 3 checkboxes a decent spacing
int height = this.Font.Height * 3 / 2;
// Create the panel first, add it to the form
Panel pnl = new Panel();
pnl.Size = new Size(100, 3 * height);
pnl.Location = new Point(10, 5);
this.Controls.Add(pnl);
// Make three checkboxes now
for (int ix = 0; ix < 3; ++ix) {
CheckBox box = new CheckBox();
box.Size = new Size(100, height);
// As pointed out, avoid overlapping them
box.Location = new Point(0, ix * height);
box.Text = "Option #" + (ix + 1).ToString();
box.Tag = ix;
// We want to know when the user checked it
box.CheckedChanged += new EventHandler(box_CheckedChanged);
// The panel is the container
pnl.Controls.Add(box);
}
}
void box_CheckedChanged(object sender, EventArgs e) {
// "sender" tells you which checkbox was checked
CheckBox box = sender as CheckBox;
// I used the Tag property to store contextual info, just the index here
int index = (int)box.Tag;
// Do something more interesting here...
if (box.Checked) {
MessageBox.Show(string.Format("You checked option #{0}", index + 1));
}
}
It looks like you get your 200 instances, all placed at the same point.
Instantiate 3 new checkboxes inside your loop body, set their properties accordingly and add each of them to the list. After the code above is complete, you will have 600 checkboxes.
list.Add(Control1);
list.Add(Control2);
list.Add(Control3);
I am not sure about what you are trying to do, but I cleaned up your code a bit:
for (int i = 0; i < 200; i++)
{
Control control = new CheckBox();
control.Location = new Point(20, 22);
i += control.Size.Width + 5;
list.Add(control);
}
You should not add a new instance to the list if you want to add the control you just made.
Also:
Control control = new Control();
control = new CheckBox();
Is a bit redundant. Also to not get one control at the same spot multiple times you should alter the point. Hope this helps