As we dynamically add controls to a web page, its location is automatically adjusted, one control will not place over another.
Can we do the same in windows Form Application.
I have to add dynamic label, textbox and buttons at last of an existing win form.
Take a look at the FlowLayoutPanel control.
Represents a panel that dynamically lays out its contents horizontally or vertically.
When you drop controls on the FlowLayoutPanel, it will take care of automatically spacing one from another. If you dock it to the form, such that it resizes with the form, then it will also handle moving controls to the next row / column as needed, so they don't become hidden beyond the boundaries of the form.
If you want to group some controls together, place them inside a Panel and then use that inside the FlowLayoutPanel. Here's some code to demonstrate:
for (var i = 0; i < 5; i++)
{
var panel = new Panel { BorderStyle = BorderStyle.FixedSingle, Width = 100, BackColor = Color.LightBlue };
panel.Controls.AddRange(
new Control[]
{
new Label { Text = "Title", Location = new Point(0, 0) },
new Label { Text = "Subtitle", Location = new Point(0, 25) }
});
flowLayoutPanel1.Controls.Add(panel);
}
Related
Lately I am working on this personal app, it's form is without border so the Exit Button, Minimize Button and Maximize button, as well as the option to resize and move the form have been custom added. The app contains several User Controls which perform different functions such as Login, a Control Panel for the app and so on. The issue here is that the User Controls, which are placed within a panel (Anchored properly, of course) in the main form are not resizing properly when the window is in a Maximized state.
To detail... the app starts at
this size and while it's manually resized in minimized state the controls have no issue inheriting it's parent's (the panel) size,
like this and this also works if the window is maximized while the control is visible, like this however if the window is already in a maximized state and I call the control with the designated button the control does not resize, it stays to it's minimum dimensions, like so.
At first I thought it may have something to do with the code that resizes the form so I removed everything and made the app with the default windows border and controls, basically set the border Property from none to sizable but that did nothing. Also I have tried accessing the User Control's parent (the panel) using this.Parent and then setting the width and height of the control with Width = this.Parent.Width and Height = this.Parent.Height but the parent returns null for some reason which I am yet to understand. Now, worth mentioning that those user controls are dynamically added (i.e Login loginForm = new Login();) to the panel every time the button is clicked and then Disposed once the control is left.
I looked all over Google for this but found nothing related and at this point I am out of options. I really want the app to be resizable and must resize properly so if anyone has any solutions I am open to anything.
Thanks anticipated.
Meanwhile, for Winforms, I may have an answer.. the key is to use the Resize event of your user control(s), see below. I tested this in Core 3.1 and Net 4.7, you can use an default designer.cs unit with an empty form. It has no issues with resize from maximized state.
My user control for the center app is named CenteredPanel and derived from Panel. Make sure you derive your user controls from Panel and call base() on the constructor, else the docking will not work properly.
Also, your question about parenting: it was not needed to use Parent below, but if you really need it, make sure you assign it !
public partial class Form1 : Form
{
private Panel panel1, panel2, panel3, panel4;
private CenteredPanel panelApp;
public Form1()
{
InitializeComponent();
this.panel1 = new System.Windows.Forms.Panel()
{ Parent = this, Dock = DockStyle.Fill };
this.panel2 = new System.Windows.Forms.Panel()
{ Parent = panel1, Dock = DockStyle.Left, Width = 120, BackColor = Color.FromArgb(60, 60, 60) };
this.panel3 = new System.Windows.Forms.Panel()
{ Parent = panel1, Dock = DockStyle.Fill };
this.panel4 = new System.Windows.Forms.Panel()
{ Parent = panel3, Dock = DockStyle.Top, Height = 60, BackColor = Color.FromArgb(60, 60, 60) };
this.panelApp = new WindowsFormsAppCore.CenteredPanel()
{ Parent = panel3, Dock = DockStyle.Fill, BackColor = Color.FromArgb(90, 90, 90) };
this.panel1.Controls.Add(this.panel3);
this.panel1.Controls.Add(this.panel2);
this.panel3.Controls.Add(this.panelApp);
this.panel3.Controls.Add(this.panel4);
this.Controls.Add(this.panel1);
}
}
public class CenteredPanel : Panel
{
Label label1, label2;
TextBox textbox1;
public CenteredPanel() : base()
{
this.Resize += new System.EventHandler(this.Resizer);
this.label1 = new System.Windows.Forms.Label()
{ Parent = this, AutoSize = true, Name = "label1", ForeColor =Color.White, Text = "Connectare Administrator" };
this.label2 = new System.Windows.Forms.Label()
{ Parent = this, AutoSize = true, Name = "label2", ForeColor = Color.White, Text = "Nume de utilizator" };
this.textbox1 = new System.Windows.Forms.TextBox()
{ Parent = this, AutoSize = true, Name = "textbox1", PasswordChar = '*', Text = "****" };
this.Controls.Add(this.label1);
this.Controls.Add(this.label2);
this.Controls.Add(this.textbox1);
}
public void Resizer(object sender, EventArgs e)
{
Point CenteringAnchor = new Point(Width / 2, Height / 2);
for (int i=0; i<Controls.Count; i++)
{
Control c = Controls[i];
// put your resizing rules here.. this is a very simple one
c.Location = new Point(CenteringAnchor.X - c.Width / 2, -40 + i * ((i == 1) ? 40 : 30) + CenteringAnchor.Y - c.Height / 2);
}
}
}
I am working on a very simple table layout application for getting started with learning C#. I am doing everything programmatic ally ( not through design editor)
I am trying to add scrolling onto the application. It seems to work fine, but it does not seem to start at the top of the horizontal range by default. I tried adding things like Max/min size, autoscroll margins etc., but nothing seems to have the desired effect. I am sure there is something simple I am missing.
Here is my current code as it relates to the problem.
layout = new TableLayoutPanel();
layout.Height = 1075;
layout.Width = 704;
layout.Name = "masterLayout";
layout.Dock = DockStyle.Fill;
layout.AutoScroll = true;
int i = 0;
foreach (Race r in ELECTION_DATA.races.OrderBy(o => o.race_id)) {
layout.Controls.Add(new Label { AutoSize = true, Text =r.race_id, Name=r.race_id, Width=300}, i, 0 );
layout.Controls.Add(new TreeView { AutoSize = true, Text = r.race_id, Name = r.race_id, Height = 1000, Width = 300 }, i,1);
i += 1;
}
Controls.Add(layout);
Here is an image, The Label Control Is not visible because the scroll is offset to the beginning of the tree view.
How can I ensure the scroll always starts at the very top?
The ScrollLayoutPanel has a method called ScrollControlIntoView that will move a specific control inside the panel into view. If you just scroll your first control into the view after you are done filling your panels, then that should ensure that the top is visible. In other words:
// do your loop first...
foreach (...)
{
layout.Controls.Add(...);
}
// then if any controls exist, scroll the first control into view
if (layout.Controls.Count > 0)
{
layout.ScrollControlIntoView(layout.Controls[0]);
}
I have this program that dynamically adds pictureboxes referring to the number of president in the database. How do i put them inside the groupbox and align the pictureboxes inside the groupbox? And the groupbox should stretch if the pictureboxes are many.
I have this codes now :
private void Form1_Load(object sender, EventArgs e)
{
conn.Open();
try
{
cmd = new SqlCommand("SELECT COUNT(Position) FROM TableVote WHERE Position='" + "President" + "'", conn);
Int32 PresCount = (Int32)cmd.ExecuteScalar();
TxtPresCount.Text = PresCount.ToString();
for (int i = 0; i < PresCount; ++i)
{
GroupBox PresGB = new GroupBox();
{
PresGB.Size = new Size(491, 152);
PresGB.Location = new Point(12, 12);
PresGB.Text = "President";
this.Controls.Add(PresGB);
PresGB.SendToBack();
PictureBox PresPB = new PictureBox();
PresPB.Location = new Point(80 + (150 * i) + 20, 50);
PresPB.Size = new Size(75, 75);
PresPB.BorderStyle = BorderStyle.Fixed3D;
PresPB.ImageLocation = "imgPath";
this.Controls.Add(PresPB);
PresPB.BringToFront();
};
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
conn.Close();
}
}
I would want the pictureboxes to be inside the groupbox and align it inside.
Maybe the FlowLayoutPanel control already does what you are trying to do. Just create your picture boxes and add them to a FlowLayoutPanel instead of a GroupBox.
FlowLayoutPanel automatically arranges controls in rows and/or columns depending on the value of its FlowDirection property. Set myFlowLayoutPanel.FlowDirection = FlowDirection.TopDown to get a vertical arranged list.
If you don't want multiple rows or columns set the WrapContents property to false. You can also set the AutoScroll property to true to automatically get scrollbars if the controls don't fit.
If you prefer to have the border of a GroupBox you can still put the FlowLayoutPanel into a GroupBox.
To adjust the space between the picture boxes you can use the Margin property.
This gives you a lot of control over the layout and you don't need to calculate the control positions. Also, if the size of the FlowLayoutPanel changes everything is rearranged automatically.
UPDATE:
I have a few comments on your code:
The curly braces make this look like the syntax of an object initializer - but it isn't.
GroupBox PresGB = new GroupBox(); // this line ends with a semicolon
{
// therefore this is just a block of code not related to new GroupBox()
};
You should remove the curly braces.
The creation of the group box is inside the loop. I doubt that you want a new group box for each picture box. This is the reason why you only see a single picture. Each new group box hides all the previous ones.
You add the picture boxes to the form instead of the group box.
You use "cryptic" names. PresGB and PresPB are very likely to be swapped accidentally. Abbreviations are usually a bad choice for names.
You don't need to call SendToBack or BringToFront since you don't want the controls to overlap anyway.
I don't think GroupBox is a good choice. Of course you can make it bigger if the number of pictures increases but you are limited by the screen and you don't get scollbars if the picture boxes don't fit. Use a FlowLayoutPanel. It has all the "magic" that you are looking for.
Replace your for loop with this piece of code:
var panel = new FlowLayoutPanel();
panel.SuspendLayout(); // don't calculate the layout before all picture boxes are added
panel.Size = new Size(491, 152);
panel.Location = new Point(12, 12);
panel.BorderStyle = BorderStyle.Fixed3D;
panel.FlowDirection = FlowDirection.LeftToRight;
panel.AutoScroll = true; // automatically add scrollbars if needed
panel.WrapContents = false; // all picture boxes in a single row
this.Controls.Add(panel);
for (int i = 0; i < PresCount; ++i)
{
var pictureBox = new PictureBox();
// the location is calculated by the FlowLayoutPanel
pictureBox.Size = new Size(75, 75);
pictureBox.BorderStyle = BorderStyle.FixedSingle;
pictureBox.ImageLocation = "imgPath";
panel.Controls.Add(pictureBox);
}
panel.ResumeLayout();
You can always drop a control on your form, do what you want to do then look at the designer generated code to see how the designer does it (in the "Designer.cs" file). Behind the scenes it is loading all controls and setting all properties via code.
That being said.
Keep in mind that once you put your picturebox inside the groupbox all location coordinate are in relation to the groupbox. So "0,0" is the upper-left corner of the groupbox, not the form.
To anchor your picturebox use the following code (this is just a straight copy-paste from my designer generated code, so you can clean it up a bit):
this.PresPB.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
To dock your picture box (so it fills the entire containing control):
this.PresPB.Dock = System.Windows.Forms.DockStyle.Fill;
You also need to change this line:
this.Controls.Add(PresPB);
to this:
PresGB.Controls.Add(PresPB);
I have a form with a scrollable panel and two controls sitting right on top of each other - one visible one not. Based on a certain condition when that form is activated I might swap the visible properties of the two controls. These controls are at the bottom of the scrollable panel. If when I leave that form I leave it scrolled to the bottom, go change the condition that will cause the controls' visibility to swap and go back to that form the visible control will have dropped about 200px down the page leaving a large gap. Anyone know what could be causing this? I tried resetting the scrollbar position to the top on form close but that just causes a smaller gap and sometimes the control to move higher into other controls. Any ideas?
Here is an example that reproduces the problem. If the mouse is moved over the red label, the visibility of button2 is changed to true which causes the scroll jumps back up to Button1.
public class Form123456 : Form {
public Form123456() {
Controls.Add(new UC1());
}
public class UC1 : UserControl {
Button b1 = new Button { Text = "Button1" };
Label lb = new Label { Text = "_", AutoSize = true, BackColor = Color.Red };
Button b2 = new Button { Text = "Button2", Visible = false };
Button b2b = new Button { Text = "x" };
Button b3 = new Button { Text = "Button3" };
public UC1() {
AutoScroll = true;
Dock = DockStyle.Fill;
b1.Location = new Point(0, 200);
b2.Location = new Point(0, 600);
lb.Location = new Point(70, 600);
b2b.Location = new Point(90, 600);
b3.Location = new Point(0, 800);
Controls.Add(b1);
Controls.Add(b2);
Controls.Add(lb);
Controls.Add(b2b);
Controls.Add(b3);
lb.MouseEnter += delegate {
b2.Visible = true;
};
lb.MouseLeave += delegate {
b2.Visible = false;
};
}
}
}
To fix it, one solution is to add this code:
protected override Point ScrollToControl(Control activeControl) {
return this.AutoScrollPosition;
}
Solution from:
Why does clicking in a text box cause an AutoScroll panel to scroll back to the top?
No repro. Sounds to me that you are doing more than just changing the Visible property. Whenever you assign the Location property, you have to add the AutoScrollPosition to compensate for the scroll state. Post code if this doesn't help.
Have you verified the order that you change visibility of the two controls?
The scroll bars on a container with auto scroll set to true will appear and disappear depending on the position of controls that are outside of the visible area of the control. Controls that are invisible do not count.
So in your case if you make both controls invisible at anytime, the scroll bars will disappear. They will come back when one control is made visible. So to make sure you don't have a jump in scroll bar position and controls position you should make sure that at no time are both controls invisible. Another solution is to have a pseudo-visible control on the container. That is a control that has its visibility set to true but it is not actually visible for the user (for example a dot of the color of the background, a label with no text ...). Position this control in the furthest position x,y and the scroll bars will never disappear..
How do I add padding, or some space between the textboxes when using dockstyle.top property?
for(int i =0; i< 10; i++) {
textboxes[i] = new TextBox();
textboxes[i].Dock = DockStyle.Top;
mypanel.Controls.Add(textboxes[i]);
}
The code above puts textboxes right beneath each other. Can't figure this out without using mass panels or fixed positioning. How to do the following?
1) I would like to add around 10-20pixels between boxes.
2) How to change size (height,width) of the textboxes, since when using dockstyle.top it ignores the size commands ?
With DockStype.Top you can't change the width of your TextBoxes, cause they are docked. You can only change the height. But to change the height of a TextBox you have to set the Multiline = true beforehand.
To get the space between the different boxes you have to put each TextBox within a panel, set the TextBox.Dock = Fill, the Panel.Dock = Top and the Panel.Padding = 10. Now you have some space between each TextBox.
Sample Code
for (int i = 0; i < 10; i++)
{
var panelTextBox = CreateBorderedTextBox();
this.Controls.Add(panelTextBox);
}
private Panel CreateBorderedTextBox()
{
var panel = CreatePanel();
var textBox = CreateTextBox();
panel.Controls.Add(textBox);
return panel;
}
private Panel CreatePanel()
{
var panel = new Panel();
panel.Dock = DockStyle.Top;
panel.Padding = new Padding(5);
return panel;
}
private TextBox CreateTextBox()
{
var textBox = new TextBox();
textBox.Multiline = true;
textBox.Dock = DockStyle.Fill;
return textBox;
}
What i forgot, you can also give a try to the FlowLayoutPanel. Just remove the DockStyle.Top from the panels and put them into the FlowLayoutPanel. Also you should set the FlowDirection to TopDown. Maybe this can also help you to solve your problem, too.
Another work around that suits smaller layouts is to just add a Label control afterwards also docked to the Top, which is not AutoSized, Text=" ", Height=your padding. This is quite useful for the odd bit of padding when using the designer.
I know where you're coming from, this is especially frustrating after returning to WinForms from WPF.
I would suggest using a TableLayoutPanel, in which each TextBox would get its own cell, and adjusting the properties of the cells. This should solve both your padding and size problems.
Another alternative would be to use some more complex layout controls, such as the DevExpress ones (not free).