I need to add controls programmatically to a custom control and position them in a certain layout. I thought it would be easy enough create a replica at design time inside a panel and then use the generated code to build them in another panel at runtime.
The dimensions such as widths, height & size are not working as expected between runtime vs design time. Why is this?
Eg below has 2 design time panels. The panel on the left contains design time controls and the one on the right runtime controls.
this.dateTimePicker1.Size = new System.Drawing.Size(219, 26); sets the width = 219
However, at runtime dtp2.Size = new System.Drawing.Size(219, 26); is too long and I have to use dtp1.Width = 150; instead. Why 150 and not 219?
RunTime Control Code:
private void BuildControls()
{
//
// dateTimePicker1
//
DateTimePicker dtp1 = new DateTimePicker();
dtp1.Location = new System.Drawing.Point(21, 35);
dtp1.Name = "dateTimePicker1";
//dtp1.Size = new System.Drawing.Size(219, 26);
dtp1.Width = 150; //Not 219 as expected?
dtp1.TabIndex = 1;
panel2.Controls.Add(dtp1);
// dateTimePicker2
//
DateTimePicker dtp2 = new DateTimePicker();
dtp2.Location = new System.Drawing.Point(21, 108);
dtp2.Name = "dateTimePicker2";
dtp2.Size = new System.Drawing.Size(219, 26); //Copying design time is too wide
//dtp1.Width = 150;
dtp2.TabIndex = 2;
panel2.Controls.Add(dtp2);
}
DesignTime Control Code:
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.label1 = new System.Windows.Forms.Label();
this.dateTimePicker1 = new System.Windows.Forms.DateTimePicker();
this.dateTimePicker2 = new System.Windows.Forms.DateTimePicker();
this.label2 = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.dateTimePicker2);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.dateTimePicker1);
this.panel1.Controls.Add(this.label1);
this.panel1.Location = new System.Drawing.Point(26, 36);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(288, 514);
this.panel1.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(17, 21);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(87, 20);
this.label1.TabIndex = 0;
this.label1.Text = "Start Date:";
//
// dateTimePicker1
//
this.dateTimePicker1.Location = new System.Drawing.Point(21, 44);
this.dateTimePicker1.Name = "dateTimePicker1";
this.dateTimePicker1.Size = new System.Drawing.Size(219, 26);
this.dateTimePicker1.TabIndex = 1;
//
// dateTimePicker2
//
this.dateTimePicker2.Location = new System.Drawing.Point(21, 108);
this.dateTimePicker2.Name = "dateTimePicker2";
this.dateTimePicker2.Size = new System.Drawing.Size(219, 26);
this.dateTimePicker2.TabIndex = 3;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(17, 85);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(81, 20);
this.label2.TabIndex = 2;
this.label2.Text = "End Date:";
//
// panel2
//
this.panel2.Location = new System.Drawing.Point(450, 260);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(288, 290);
this.panel2.TabIndex = 1;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(850, 710);
this.Controls.Add(this.panel2);
this.Controls.Add(this.panel1);
this.Name = "Form1";
this.Text = "Form1";
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
}
Your problem is with Autoscaling.
This code:
this.dateTimePicker1.Size = new System.Drawing.Size(219, 26);
May not imply that this.dateTimePicker1.Size is really 219 x 26. Why? Because of this line here, from .designer.cs:
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
After that line an in-memory scaling is performed. This scaling is executed on all child controls right after this line:
this.groupBox1.PerformLayout();
After that, this.dateTimePicker1.Size will change to something closer to 150 in width. You may also notice that designer code does not match what's displayed in the properties pane when control is selected.
Solution, Part 1
Add something to the form to cause a change in the designer file and save it. This will at cause .designer.cs code to match the screen DPI and you won't see any inconsistencies any more. It seems that your DPI setting is higher than the one used when creating the form - if this is accidental, correct your Windows DPI setting to 96 or 100%.
Solution, Part 2
Once your form designer matches your screen DPI, you will see that all the size and location properties have been changed and that there is a new value set on AutoScaleDimensions, write down this value because this is the dimension that matches your screen DPI.
Now, whenever you want your controls' locations and sizes to respect the screen DPI you have to place your control logic in something like this:
// Your referential DPI setting (96DPI in this case)
this.AutoScaleDimensions = new SizeF(6F, 13F);
// TODO: Place your code here
// Setting of your users
this.AutoScaleDimensions = this.CurrentAutoscaleDimensions;
This would cause whatever controls placed in between to be scaled to whatever DPI is currently on the screen.
Note that 6F, 13F are values most people have for 96 DPI setting (default 100% zoom in Windows). This is why I asked to write down your value so you can use that instead.
If you still don't find this obnoxious you can also read up this question which has extra info: Creating a DPI-Aware Application.
Important Note
I forgot to mention something - if you work in a team with source control software, take extra care because whenever you save something in designer it's going to alter every Size and Location to match your own setting (as explained above). This shouldn't cause problems but you should be always aware of this.
try changing
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
to
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
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;
I have a simple System.Windows.Forms.Label in a System.Windows.Forms.Form.
I want to dynamically resize the label to fit text loaded runtime, while keeping it Anchored to the right and bottom of its parent form.
According to the MSDN Documentation:
It is “always true” that the Location Property remains constant (i.e., that the top left position of the Control will never change).
It is “always true” that the Anchor property is respected when AutoSize is true (i.e., that the Location Property—the top-left corner—will be modified so that the Anchored Sides maintain their initial distance from the edges of their parent controls).
From my reading of this, I would expect that the second truth overrides the first when Anchor is anything but AnchorStyles.None.
However, this doesn't seem to bear out in practice.
Consider the following:
// From ExampleForm.Designer.cs
this.label = new System.Drawing.Label();
this.label.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.label.AutoSize = true;
this.label.Location = new System.Drawing.Point(600, 400);
this.label.Size = new System.Drawing.Size(170, 20);
this.label.Text = "[Populated at Runtime]";
this.label.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
// ...
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.label);
// ...
// Sometime after Form Initialization, this is called
void PopulateLabel() {
var oldRight = label.Right;
label.Text = "Hey here's some new text. It's pretty long so the control will have to resize";
// Without this next line, the Right Anchor distance is not maintained.
// label.Left -= (label.Right - oldRight);
System.Diagnostics.Debug.Assert(label.Anchor.HasFlag(AnchorStyles.Right) && label.Right == oldRight, "The label didn't stay anchored to the right");
}
Obviously I can work around this by tracking the distance manually, as above.
I just wonder if there isn't some way this is “supposed” to work that I'm doing wrong.
The one observation I have to offer is this: it works if the label is not anchored to the bottom.
Do I need to call Suspend/Resume/PerformLayout on the Label? on the Form?
Are the docs wrong?
Am I being foolishly naïve or completely misunderstanding something?
Do I need some sort of intermediary Control for this to work and the docs assume I know this?
To address some possible complications that show up in similar questions (or that I dreamt up):
rightToLeft is false,
Dock is DockStyle.None,
the label's Parent is the form itself, not an intermediary panel or other control.
the Margin seems irrelevant
Anchoring to the Top or Bottom seems irrelevant to Right not working.
System.Windows.Form.Button works as expected. I haven't tested other controls.
Try using a TableLayout, it tends to obey the Control layout properties better. E.g:
public class MyForm : Form {
Label label = new Label() { BackColor = Color.Blue, ForeColor = Color.White };
public MyForm() {
TableLayoutPanel panel = new TableLayoutPanel() { BackColor = Color.Green };
panel.ColumnCount = 1;
panel.RowCount = 1;
panel.Controls.Add(label, 0, 0);
panel.Dock = DockStyle.Bottom;
panel.AutoSize = true;
panel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
panel.Controls.Add(label);
//this.label.Anchor = AnchorStyles.Right | AnchorStyles.Top;// | AnchorStyles.Bottom; // ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
//this.label.Dock = DockStyle.Fill;
this.label.Dock = DockStyle.Right;
this.label.AutoSize = true;
this.label.Margin = Padding.Empty;
//this.label.Location = new System.Drawing.Point(600, 400);
//this.label.Size = new System.Drawing.Size(170, 20);
this.label.Text = "[Populated at Runtime]";
//this.label.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
// ...
//this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
//this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
//this.Controls.Add(this.label);
this.Controls.Add(panel);
// ...
Button btn = new Button { Text = "Change text" };
btn.Click += delegate {
PopulateLabel();
};
Controls.Add(btn);
}
// Sometime after Form Initialization, this is called
void PopulateLabel() {
var oldRight = label.Right;
label.Text = "Hey here's some new text. It's pretty long so the control will have to resize";
// Without this next line, the Right Anchor distance is not maintained.
// label.Left -= (label.Right - oldRight);
//System.Diagnostics.Debug.Assert(label.Anchor.HasFlag(AnchorStyles.Right) && label.Right == oldRight, "The label didn't stay anchored to the right");
}
}
I'm working in a c# application in winform.
I saw that there is no Style for element (unlike WPF). But is there a way to simply set all the labels to a specific design ?
Actually I do :
public partial class myControl : UserControl
{
private Color LabelColor = Color.Indigo;
private Color LabelFont = new System.Drawing.Font("Arial",
18F,
System.Drawing.FontStyle.Regular,
System.Drawing.GraphicsUnit.Point,
((byte)(0)));
public myControl()
{
InitializeComponent();
//Set design
designLabels();
}
private void designLabels()
{
List<Label> labelsToStyle = new List<Label>();
labelsToStyle.Add(labelName);
labelsToStyle.Add(labelAge);
labelsToStyle.Add(labelSize);
foreach (Label l in labelsToStyle)
{
l.ForeColor = LabelColor;
l.Font = LabelFont;
l.Dock = DockStyle.Fill;
}
}
}
It works but it doesn't display correctly in designer (I have to run application to see my design). And maybe it exists a simplest way ?
As per my comment the easiest is to create a custom control and use that one in your windows.
here how simple it is. Simply override the label and set in constructor the default value you want
public class DesignLabel : Label
{
public DesignLabel()
{
ForeColor = Color.Indigo;
Font = new System.Drawing.Font("Arial", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
}
}
then compile once and go to your design view, open your toolbox and you will see the "DesignLabel", simply drag drop on the window and that's it. If you change default in the class it will be changed all over the place.
If you want to change the style already at design time so that you can see in in the Form.cs[design] you need to edit the Form.Designer.cs file! But this you have to do label for label by hand. I saved in this example the Font and Color in the project properties (sorry for the german version):
in my example I have 3 Labels. In the Form.Designer.cs file you can add the properties:
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(41, 15);
this.label1.TabIndex = 0;
this.label1.Text = "label1";
this.label1.Font = Properties.Settings.Default.LabelFont;
this.label1.ForeColor = Properties.Settings.Default.LabelColor;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 68);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 15);
this.label2.TabIndex = 1;
this.label2.Text = "label2";
this.label2.Font = Properties.Settings.Default.LabelFont;
this.label2.ForeColor = Properties.Settings.Default.LabelColor;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 122);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(41, 15);
this.label3.TabIndex = 2;
this.label3.Text = "label3";
this.label3.Font = Properties.Settings.Default.LabelFont;
this.label3.ForeColor = Properties.Settings.Default.LabelColor;
The result looks like this:
DISCLAIMER
I would not recommend this approach! Editing the Form.Desginer.cs file is never a good idea! I would stick to the run time changes. If you want to change all Labels just filter the this.Controls for it and foreach through the collection like this:
this.Controls.OfType<Label>().ToList().ForEach(lbl =>
{
lbl.Font = LabelFont;
lbl.ForeColor = LabelColor;
//lbl.Dock = DockStyle.Fill; // uncommented, because I could see only 1 Label
});
The result will be the same.
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.
I have a winform app in which I have a panel control.
I want to be able to scroll inside the panel and place controls vertically more then the current height of the control and then have a scroll which will help me to see all the controls, how can I achieve that?
This is the designer code as well, in case someone wants to take a look at the code:
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// panel1
//
this.panel1.AutoScroll = true;
this.panel1.BackColor = System.Drawing.SystemColors.ControlLightLight;
this.panel1.Location = new System.Drawing.Point(12, 12);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(267, 365);
this.panel1.TabIndex = 0;
//
// Form2
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(456, 410);
this.Controls.Add(this.panel1);
this.Name = "Form2";
this.Text = "Form2";
this.ResumeLayout(false);
}
Since you have AutoScroll = true, you shouldn't have to do anything. Any control that you place in the panel that is below the visible boundary will automatically create the appropriate scroll distance in the panel.
If you want to manually override that, set AutoScroll = false and set the size of the canvas yourself using the AutoScrollMinSize property, example:
panel1.AutoScrollMinSize = new Size(0, 1200);
You might want to consider anchoring the panel to the four sides of the form as well, or dock-fill, since it looks like a resizable form. Again, the panel will handle the scrollbar size for you.
Try this out for loading other forms in panels of MDIForm. It works perfectly.
myForm.TopLevel = false;
myForm.AutoScroll = true;
main_panel.Controls.Clear();
main_panel.Controls.Add(myForm);
main_panel.AutoScrollMinSize = new Size(0, myForm.Height);
myForm.Show();