I am trying to dynamically add radiobuttons to a form (so I can change their values when the user clicks a button) but then I added Location information for the individual buttons they no longer appear at all. I can see the outline of the GroupBox on the form, as well as several hundred pixels on every side.
private void AddQ1()
{
questionBox = new System.Windows.Forms.GroupBox();
questionBox.Location = new System.Drawing.Point(1200, 250);
questionBox.Size = new System.Drawing.Size(400, 700);
questionBox.Text = "To What extent is this person... striking a pose?";
RadioButton radioButton1;
for (int i = 1; i < 6; i++)//opt 1,2,3,4,5
{
radioButton1 = new System.Windows.Forms.RadioButton();
radioButton1.CheckedChanged += new EventHandler(radioButton_CheckedChanged);
radioButton1.Tag = i.ToString();
radioButton1.Text = i.ToString();
radioButton1.Location = new System.Drawing.Point(1200, (250+(10*i)));
questionBox.Controls.Add(radioButton1);
rbList.Add(radioButton1);
}
Controls.Add(questionBox);
}
Location is the Point that represents the upper-left corner of the control relative to the upper-left corner of its container.
try
radioButton1.Location = new System.Drawing.Point(0, (250+(10*i)));
The location of the radio buttons will not be visible in your code above.
The location property is relational with the container
see
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.location%28v=vs.110%29.aspx
Related
I'm fairly new to programming and this is my first Forms program.
I'm trying to place buttons inside a panel in a manner that they get automatically sorted by alphabetical order.
At first I was trying with a FlowLayoutPanel, which displayed the buttons with the correct size and placement, but not sorted by Alphabetical order:
With FlowLayoutPanel. Ideal Size and placement, but no sorting.
I also had a few problems while trying to automatically scale it to screen size.
After failing with a FlowLayoutPanel, I tried just a normal panel. It fixed all my previous problems, but would stretch the buttons when docked:
With a normal panel. Perfect sorting and scaling, but stretching.
Here is the code, if it matters:
Button modButton = new Button();
modButton.Location = new System.Drawing.Point(3, 3);
modButton.Name = skinName;
modButton.Size = new System.Drawing.Size(200, 200);
modButton.TabIndex = 0;
modButton.Text = skinName;
modButton.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
modButton.UseVisualStyleBackColor = true;
modButton.BackgroundImageLayout = ImageLayout.Zoom;
modButton.Dock = System.Windows.Forms.DockStyle.Left;
modButton.Click += new System.EventHandler(this.modButton_Click);
modButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText;
this.panel2.Controls.Add(modButton);
modList.Add(skinName);
And for the panel:
this.panel2.AutoScroll = true;
this.panel2.AutoSize = true;
this.panel2.BackColor = System.Drawing.SystemColors.Control;
this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel2.Location = new System.Drawing.Point(0, 0);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(636, 450);
this.panel2.TabIndex = 7;
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'm trying dynamically add panels within a panel dependent on the count of people in a list using the following code when the form loads:
private void Form1_Load(object sender, EventArgs e)
{
const int xConst = 2;
var people = new List<string>
{
"Person1",
"Person2",
"Person3",
"Person4",
};
var y = 2;
for (var x = 0; x < people.Count; x++)
{
var newpan = new MyPanel
{
BorderStyle = BorderStyle.None,
Height = 25,
Width = panel1.Width - 5,
Location = new Point(xConst, y)
};
var newlbl = new Label
{
BorderStyle = BorderStyle.None,
AutoSize = false,
Text = people[x],
Font = new Font("Segoe UI", 9.5F, FontStyle.Bold, GraphicsUnit.Point, ((byte)(0))),
Size = new Size(75,20),
Location = new Point(newpan.Location.X + 2, newpan.Location.Y + 2),
};
var newbtn = new Button
{
FlatStyle = FlatStyle.Flat,
FlatAppearance = { BorderSize = 0 },
UseVisualStyleBackColor = true,
Text = #"+",
Size = new Size(15,21),
Location = new Point(newpan.Width - 20,newpan.Location.Y - 1),
Font = new Font("Segoe UI", 9.0F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)))
};
newpan.Controls.Add(newlbl);
newpan.Controls.Add(newbtn);
panel1.Controls.Add(newpan);
y += 27;
}
}
The problem is that if I specify the Location property in both the button and the label, only the first iteration of the labels and buttons show up for the Person1 iteration. But, if I leave the Location property out, they all show up. The problem with that is that I have a custom panel that overrides some stuff allowing me to put a customer border and color around the panels, and if I don't specify a location, the labels and buttons aren't positioned correctly on the panel, so it covers my border and looks sloppy.
Can someone help me figure out why this is happening? I've stepped through the program completely and watched all the values I can think of increment accordingly in the watch window. All the panels show up correctly, so I don't understand why their respective labels and buttons don't show up when I specify the location.
Looking at your code I notice that you are setting your newpan height to 25, and its position is offset by 27 with each iteration. You also are using
'Location = new Point(newpan.Location.X + 2, newpan.Location.Y + 2)
to set the location of your button and label within your newpan panel. newpan.Location is referenced in the coordinates of your panel1, your button and label's location is referenced in the coordinates of your newpan panel therefore after the first iteration of your For statement your label and buttons y location value is 29 which is greater than the height of your newpan panel making it not able to be seen, the next iteration after that will be y will be 56 and so forth. Each content control, in this case your panels will have its own coordinate system, the easiest fix would be to do something like this:
'Location = new Point( 2, 2) //for your label
'Location = new Point(newpan.Width - 20, - 1) //for your button
The other alternative is to do like jmcilhinney suggests and make an UserControl with your button and label already in position, you would then create individual instances of it and assign it to your panel1.
I try to create simple app with 2 columns using SpliterContainer and control panel with buttons. And I would like that on every screen it will look good. That's why I decided to use relative position of elements.
I read documentation and different forums, but I get something strange. Second column of splitter doesn't appear at all.
Please, can you help me find the reason of that problem?
private void Form1_Load(object sender, EventArgs e)
{
WindowState = FormWindowState.Maximized;
int screenWidth = Screen.PrimaryScreen.Bounds.Width;
int screenHeight = Screen.PrimaryScreen.Bounds.Height;
//set form size
this.Size = new Size(screenWidth, screenHeight);
//set button panel size
const double percentOfHeightPanel = 0.05;
int heightOfPanelButton = Convert.ToInt32(screenHeight * percentOfHeightPanel);
this.panel_button.Size = new System.Drawing.Size(screenWidth, heightOfPanelButton);
this.panel_button.Location = new Point(0, 0);
//set splitContainer size
int widthOfContainer = Convert.ToInt32(0.5 * screenWidth);
int heightOfContainers = Convert.ToInt32(screenHeight * (0.95));
splitContainer1.Panel1.MinimumSize = new Size(widthOfContainer, heightOfContainers);
splitContainer1.Panel2.MinimumSize = new Size(widthOfContainer, heightOfContainers);
splitContainer1.Location = new Point(0, heightOfPanelButton);
//this.splitContainer1.Panel2MinSize = screenWidth - widthOfContainer;
//set textBox size
this.textBox1.Multiline = true;
this.textBox1.Location = new Point(0, heightOfPanelButton);
this.textBox1.MinimumSize = new System.Drawing.Size(widthOfContainer, heightOfContainers);
this.textBox2.Multiline = true;
this.textBox2.Location = new Point(widthOfContainer, heightOfPanelButton);
this.textBox1.MinimumSize = new System.Drawing.Size(widthOfContainer, heightOfContainers);
}
If you want two have two splitter panels of the same size set
splitContainer1.SplitterDistance =
(splitContainer1.Width - splitContainer1.SplitterWidth) / 2;
Then set
splitContainer1.IsSplitterFixed = true;
You can set these two properties manually at design time. The user will then not be able to resize the panels and the panels will automatically resize to be of same size.
Consider using a TableLayoutPanel instead.
If further, the two sides should look the same, place your controls on a UserControl and place two instances of them into the two panels with a docked property set to Fill.
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