Microsoft Forms docking to top-left. C# - c#

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;

Related

How do I dynamically layer objects ontop of eachother in Windows Forms?

I'm using a FlowLayoutPanel to dynamically display an array of PictureBoxes and Labels. They're going in the order: PictureBox-Label-PictureBox-Label and so on. I need those labels to appear above each picture so they'll match, basically layring them ontop of picturebox margins. I tried using Controls.SetChildIndex(temp, 2); but it seem to just swap the postion of the picturebox. I also tried using temp.BringToFront(); but then all the pictureboxes being displayed at the top of the panel and all the labels are below (I need each label to match each picturebox above them). Here's the code:
public void RunMeta()
{
Label mostPickedLabel = new Label();
mostPickedLabel.Text = "Most picked heroes";
flowLayoutPanel1.Controls.Add(mostPickedLabel);
mostPickedLabel.Margin = new Padding(15, 0, 1000, 0);
mostPickedLabel.Font = new Font("Lucida Sans Unicode", 15);
mostPickedLabel.ForeColor = Color.DarkCyan;
mostPickedLabel.Size = new Size(200, 30);
foreach (var mostPickedHero in FetchDataFromDota2Site.MostUsedHeroesAndImages)
{
PictureBox temp = new PictureBox();
temp.ImageLocation = mostPickedHero.ImageSource;
temp.SizeMode = PictureBoxSizeMode.StretchImage;
temp.Left = temp.Width * flowLayoutPanel1.Controls.Count;
temp.Margin = new Padding(15, 30, 15, 30);
flowLayoutPanel1.Controls.Add(temp);
flowLayoutPanel1.AutoScroll = true;
Label heroName = new Label();
heroName.Text = mostPickedHero.MostPickedHeroName;
heroName.Font = new Font("Lucida Sans Unicode", 8);
heroName.ForeColor = Color.White;
flowLayoutPanel1.Controls.Add(heroName);
}
}

BackgroundImage layout to Bottom

I have a task, that can be easily completed with html/css, but I cant figure out how to do it with C# WindowsForms.
I need to create container with dinamic height and with 3 types of BackgroundImage's. To do so I created panel1 and set BackgroundImageLayout of "body.jpg" to tile.
Then I put panel2 and set BackgroundImageLayout of "top.jpg" to none.
So I need to create only one container with "bottom.jpg" but here is the problem.
Image is tall and it must have layout to bottom, but I cant set BackgroundImage of panel3 to Bottom and I cant dock PictureBox to bottom, because in that case it will prevent to set content overlay.
P.S. Sorry if you cant understand some of my explanations - my native language is Russian.
Here is what constuctor created for me:
//
// Charsheet_bg
//
this.Charsheet_bg.AutoSize = true;
this.Charsheet_bg.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("Charsheet_bg.BackgroundImage")));
this.Charsheet_bg.Controls.Add(this.Charsheet_top);
this.Charsheet_bg.Dock = System.Windows.Forms.DockStyle.Left;
this.Charsheet_bg.Location = new System.Drawing.Point(0, 0);
this.Charsheet_bg.Margin = new System.Windows.Forms.Padding(0);
this.Charsheet_bg.MinimumSize = new System.Drawing.Size(285, 0);
this.Charsheet_bg.Name = "Charsheet_bg";
this.Charsheet_bg.Size = new System.Drawing.Size(285, 488);
this.Charsheet_bg.TabIndex = 30;
//
// Charsheet_top
//
this.Charsheet_top.BackColor = System.Drawing.Color.Transparent;
this.Charsheet_top.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("Charsheet_top.BackgroundImage")));
this.Charsheet_top.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.Charsheet_top.Dock = System.Windows.Forms.DockStyle.Fill;
this.Charsheet_top.Location = new System.Drawing.Point(0, 0);
this.Charsheet_top.MinimumSize = new System.Drawing.Size(285, 200);
this.Charsheet_top.Name = "Charsheet_top";
this.Charsheet_top.Size = new System.Drawing.Size(285, 488);
this.Charsheet_top.TabIndex = 31;
And screenshot will be in 5 mins.
Screenshot with tooltips

How to get FlowLayoutPanel.AutoSize to work with FlowBreak

I have a problem with a FlowLayoutPanel and I don't know how to solve it.
I'm placing two FlowLayoutPanels inside another; the second inner flp has 3 buttons inside.
The properties from FlowLayoutPanel child are:
FlowDirection = LeftToRight;
AutoSize = true;
AutoSizeMode = GrowAndShrink;
WrapContents = true;
Now I set for each button the FlowBreak property to true, however the behavior I see is not the one I want, I want the FlowLayoutPanel to shrink to the width of the buttons,
Changing FlowDirection to UpToDown is not an option.
Anyone know why the AutoSize is not working?
this is the code.
//
//FlowLayoutPanel1
//
this.FlowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.FlowLayoutPanel1.Controls.Add(this.FlowLayoutPanel3);
this.FlowLayoutPanel1.Location = new System.Drawing.Point(84, 77);
this.FlowLayoutPanel1.MinimumSize = new System.Drawing.Size(10, 10);
this.FlowLayoutPanel1.Name = "FlowLayoutPanel1";
this.FlowLayoutPanel1.Size = new System.Drawing.Size(308, 265);
this.FlowLayoutPanel1.TabIndex = 0;
//
//FlowLayoutPanel3
//
this.FlowLayoutPanel3.AutoSize = true;
this.FlowLayoutPanel3.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.FlowLayoutPanel3.Controls.Add(this.Button1);
this.FlowLayoutPanel3.Controls.Add(this.Button2);
this.FlowLayoutPanel3.Controls.Add(this.Button3);
this.FlowLayoutPanel3.Location = new System.Drawing.Point(127, 3);
this.FlowLayoutPanel3.MinimumSize = new System.Drawing.Size(10, 10);
this.FlowLayoutPanel3.Name = "FlowLayoutPanel3";
this.FlowLayoutPanel3.Size = new System.Drawing.Size(162, 87);
this.FlowLayoutPanel3.TabIndex = 1;
//
//Button1
//
this.FlowLayoutPanel3.SetFlowBreak(this.Button1, true);
this.Button1.Location = new System.Drawing.Point(3, 3);
this.Button1.Name = "Button1";
this.Button1.Size = new System.Drawing.Size(75, 23);
this.Button1.TabIndex = 0;
this.Button1.Text = "Button1";
this.Button1.UseVisualStyleBackColor = true;
//
//Button2
//
this.FlowLayoutPanel3.SetFlowBreak(this.Button2, true);
this.Button2.Location = new System.Drawing.Point(3, 32);
this.Button2.Name = "Button2";
this.Button2.Size = new System.Drawing.Size(75, 23);
this.Button2.TabIndex = 1;
this.Button2.Text = "Button2";
this.Button2.UseVisualStyleBackColor = true;
//
//Button3
//
this.Button3.Location = new System.Drawing.Point(3, 61);
this.Button3.Name = "Button3";
this.Button3.Size = new System.Drawing.Size(75, 23);
this.Button3.TabIndex = 2;
this.Button3.Text = "Button3";
this.Button3.UseVisualStyleBackColor = true;
It is a bug, it's been around for a very long time. The issue is that the layout engine for FlowLayoutPanel calculates the width of the first row wrong, including the width of the 2nd control, even though it got wrapped to the second row.
The workaround is silly but effective, add a dummy Panel with a Width of 0 after the 1st control. If you are doing this with the designer then drop it first and drag it in the right place, to the right of the 1st control. Then set its Margin to (0, 0, 0, 0) and Size to (0, 0) in the Properties window.
I don't believe the FlowLayoutPanel was designed to do what you're trying to do.
A TableLayoutPanel would probably be better suited. Add a TableLayoutPanel with a single column, and add each button to a row.
Edit: I found a hackish work around. After the first button, create a Panel with the size of 0,0 and the margin of 0,0. Make sure that FlowBreak is set to false.
Edit: You only need to create one panel, after the first button, not one for each.
Not a solution, but a workaround. It looks like you are trying to simulate behavior of TableLayoutPanel by using flow breaks in FlowLayoutPanel. Did you try using TableLayoutPanel instead? According to your screenshots in the comments, it should work perfectly for your needs.

When is Panel.Size updated after adding controls when Panel.AutoSize = true?

I'm creating a GUI in C# using WinForms.
I'm trying to position programaticaly created panels one below the other. As the content of these panel can vary depending on their content, I'm using Panel.AutoSize to let WinForms perform the correct resizing.
The problem is: if I'm using Panel.Height (or Panel.Size.Height) right after populating the Panel, the value returned is always my default value. The resizing do occur, as I can see when launching the app, but I just don't know when.
Here's a simplified version of what I'm doing:
this.SuspendLayout();
int yPos = 0;
foreach (String entry in entries)
{
Panel panel = new Panel();
panel.SuspendLayout();
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
panel.BackColor = System.Drawing.SystemColors.Window; // Allows to see that the panel is resized for dispay
panel.Location = new System.Drawing.Point(0, yPos);
panel.Size = new System.Drawing.Size(this.Width, 0);
this.Controls.Add(panel);
Label label = new Label();
label.AutoSize = true;
label.Location = new System.Drawing.Point(0, 0);
label.MaximumSize = new System.Drawing.Size(panel.Width, 0);
label.Text = entry;
panel.Controls.Add(label);
panel.ResumeLayout(false);
panel.PerformLayout();
yPos += panel.Height; // When breaking here, panel.Height is worth 0
yPos += label.Height; // This works perfectly, label.Height was updated according to the text content when breaking at that point
}
this.ResumeLayout(false);
this.PerformLayout();
So the real question is: How can I get the updated Panel.Size after adding controls to it, to get its proper height value?
Note: I know I can use the TextBox height, but I find it inelegant and impractical, as in my actual code there are more controls in the Panel and I need to use that panel height a few lines below.
What I beleive is happening is that the Size of the Panel will be determined when you do PerformLayout on its Parent. You can make it work like you are wanting by moving the panel's parent SuspendLayout / ResumeLayout code into the Loop.
int yPos = 0;
foreach (String entry in entries)
{
this.SuspendLayout();
Panel panel = new Panel();
panel.SuspendLayout();
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
panel.BackColor = System.Drawing.SystemColors.Window; // Allows to see that the panel is resized for dispay
panel.Location = new System.Drawing.Point(0, yPos);
panel.Size = new System.Drawing.Size(this.Width, 0);
this.Controls.Add(panel);
Label label = new Label();
label.AutoSize = true;
label.Location = new System.Drawing.Point(0, 0);
label.MaximumSize = new System.Drawing.Size(panel.Width, 0);
label.Text = entry;
panel.Controls.Add(label);
panel.ResumeLayout(true);
this.ResumeLayout(true);
yPos += panel.Height; // When breaking here, panel.Height is worth 0
//yPos += label.Height; // This works perfectly, label.Height was updated according to the text content when breaking at that point
}
this.PerformLayout();

C# Add Controls To Panel In a Loop

I wish to add a button for every line in a file to a panel.
My code so far is:
StreamReader menu = new StreamReader("menu.prefs");
int repetition = 0;
while(!menu.EndOfStream)
{
Button dynamicbutton = new Button();
dynamicbutton.Click += new System.EventHandler(menuItem_Click);
dynamicbutton.Text = menu.ReadLine();
dynamicbutton.Visible = true;
dynamicbutton.Location = new Point(4+repetition*307, 4);
dynamicbutton.Height = 44;
dynamicbutton.Width = 203;
dynamicbutton.BackColor = Color.FromArgb(40,40,40);
dynamicbutton.ForeColor = Color.White;
dynamicbutton.Font = new Font("Lucida Console", 16);
dynamicbutton.Show();
menuPanel.Controls.Add(dynamicbutton);
repetition++;
MessageBox.Show(dynamicbutton.Location.ToString());
}
menu.Close();
The problem is that only the first control gets created.
The code looks fine but there could be a following situations.
1.You might have only one entry in the file, so you are experiencing only One Button added to the panel.
2.Your panel width is smaller than the sum of all the dynamic buttons width.
I suspect no 2 is the main reason that is causing problem.
So, I recommend that you use FlowLayoutPanel. To add a dynamic content as it automatically layout all the child controls.
Each time it is generating the same name for dynamic controls. That's the reason why it is showing only the last one. It simply overwrites the previous control each time.
int x = 4;
int y = 4;
foreach(PhysicianData pd in listPhysicians)
{
x = 4;
y = panPhysicians.Controls.Count * 30;
RadioButton rb = new RadioButton();
rb.CheckedChanged += new System.EventHandler(rbPhysician_CheckedChanged);
rb.Text = pd.name;
rb.Visible = true;
rb.Location = new Point(x, y);
rb.Height = 40;
rb.Width = 200;
rb.BackColor = SystemColors.Control;
rb.ForeColor = Color.Black;
rb.Font = new Font("Microsoft Sans Serif", 10);
rb.Show();
rb.Name = "rb" + panPhysicians.Controls.Count;
panPhysicians.Controls.Add(rb);
}
Try this code
StreamReader menu = new StreamReader("menu.prefs");
var str = menu.ReadToEnd();
var items = str.Split(new string[] {"\r\n" } , StringSplitOptions.RemoveEmptyEntries);
foreach (var item in items)
{
Button dynamicbutton = new Button();
dynamicbutton.Click += new System.EventHandler(menuItem_Click);
dynamicbutton.Text = item;
dynamicbutton.Visible = true;
dynamicbutton.Location = new Point(4+repetition*307, 4);
dynamicbutton.Height = 44;
dynamicbutton.Width = 203;
dynamicbutton.BackColor = Color.FromArgb(40,40,40);
dynamicbutton.ForeColor = Color.White;
dynamicbutton.Font = new Font("Lucida Console", 16);
dynamicbutton.Show();
menuPanel.Controls.Add(dynamicbutton);
repetition++;
}
The problem with Panel and similar controls other than the FlowLayoutPanel is when you create a control and a second one, the second is created at the same position if you are not changing it's location dynamically or setting it according to the other already added controls. Your control is there, it's in the back of the first control.
A flowLayoutPanel is better as it will add the controls next to each other as you add them while compromising more finer control at their positioning.
I also have similar problems with panels. For what you are doing it could be useful to just add strings to a listbox rather than using labels and a panel. That should be simpler.

Categories