Adjust free space in a TableLayoutPanel - c#

I have a TableLayoutPanel that contains a Label and a ComboBox:
var lblDesignGroup = new Label
{
Name = "lblDesignGroup",
Text = "DesignGroup",
Margin = new Padding(0, 50, 0, 0)
};
tlpCallLog.Controls.Add(lblDesignGroup, 0, 0);
var cboDesignGroup = new ComboBox
{
Name = "cboDesignGroup",
DataSource = designGroups,
DisplayMember = "DesignGroupName",
ValueMember = "DesignGroupId",
Margin = new Padding(0, 50, 0, 0),
DropDownStyle = ComboBoxStyle.DropDownList
};
Once I create them, I setup the TableLayoutPanel as:
tlpCallLog.ColumnStyles.Clear();
for (int i = 0; i < tlpCallLog.ColumnCount; i++)
{
tlpCallLog.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
}
tlpCallLog.RowStyles.Clear();
for (int i = 0; i < tlpCallLog.RowCount; i++)
{
tlpCallLog.RowStyles.Add(new RowStyle(SizeType.AutoSize));
}
tlpCallLog.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single;
But for some reason, I have blank space between the Label and the DropDown.
As you can see, the DropDown width adjusts correctly, it does not have any free space, but the Label has it.
I want to remove that space. How can I achieve that?

The Label Control auto-sizes in a default layout (when child of a Form, as a standard scenario, since the Form performs its layout when initialized). When created like this, the AutoSize property is set to true because that's the default value, but the Layout is never performed.
When you explicitly set AutoSize = true, then the layout is performed.
Set both the TableLayoutPanel and the Label to AutoSize:
tlpCallLog.AutoSize = true;
tlpCallLog.RowStyles.Clear();
//[...]
var lblDesignGroup = new Label {
AutoSize = true,
Name = "lblDesignGroup",
Text = "DesignGroup",
Margin = new Padding(0, 53, 0, 0)
};
tlpCallLog.Controls.Add(lblDesignGroup, 0, 0);
var cboDesignGroup = new ComboBox {
//[...]
}
I suggest to set the Label's Margin = new Padding(0, 53, 0, 0): 3 pixels down, to align with the ComboBox text. It's a valid measure with (almost) any Font size.
Another suggestion is to set the [Form].AutoScaleMode = AutoScaleMode.Dpi.

Related

Dynamically created controls names not increasing its value through loops C#

I'm working on a personal project where I'm building a notes generator, and I have this TextBox dynamically created everytimes a button is clicked, it works fine just like how I expected but things gets weird when I tried to name each of these TextBox to different name by using a loop Name = "Note" + i where i is the loop variable.
So what I was expecting to happens is that each of the TextBox names to be something like Note1 Note2 Note3 ... but instead when I retrieved each of the TextBox name to a MessageBox in the same loop which is used to generates the TextBox, the MessageBox throws this: Note 1 Note 1 Note 1 ... instead when I clicked the button thrice.
int curr = 0;
private void guna2Button1_Click(object sender, EventArgs e) {
int top = 25;
int h_p = 170;
curr++;
for(int i=1; i<curr+1; i++) {
// Notes
var new_note = new Guna2TextBox() {
Text = "Title\n",
Name = "Note" + i,
Multiline = true,
AcceptsTab = true,
AcceptsReturn = true,
WordWrap = false,
ScrollBars = ScrollBars.Vertical,
Width = 220,
Height = 110,
BorderRadius = 8,
Font = new Font("Bahnschrift", 13),
ForeColor = Color.White,
FillColor = ColorTranslator.FromHtml("#1E1E1E"),
BorderColor = ColorTranslator.FromHtml("#2C2C2C"),
Location = new Point(450,top)
};
MessageBox.Show(i.ToString());
top += h_p;
flowLayoutPanel1.Controls.Add(new_note);
curr = 0;
}
}
The problem is caused by the fact that inside the loop you set always curr back to zero. And the loop is not needed at all because you want to add a textbox at each click. So you just need to look at the Count property of the FlowLayoutPanel and use that value to prepare the name.
Another problem to solve is how to position the next control inside the panel but again you could calculate easily with a the Count property
private void guna2Button1_Click(object sender, EventArgs e)
{
int nextTop = 25 + (flowLayoutPanel.Controls.Count * 170);
var new_note = new Guna2TextBox() {
Text = "Title\n",
Name = "Note" + flowLayoutPanel.Controls.Count + 1,
.....
Location = new Point(450,nextTop)
};
flowLayoutPanel1.Controls.Add(new_note);
}
I don't think you need to use the For loop in this case. You can just use the variable curr for each textboxes. So instead of the For loop, you can use this :
int top = 25;
int h_p = 170;
curr++;
var new_note = new Guna2TextBox() {
Text = "Title\n",
Name = "Note" + curr,
Multiline = true,
AcceptsTab = true,
AcceptsReturn = true,
WordWrap = false,
ScrollBars = ScrollBars.Vertical,
Width = 220,
Height = 110,
BorderRadius = 8,
Font = new Font("Bahnschrift", 13),
ForeColor = Color.White,
FillColor = ColorTranslator.FromHtml("#1E1E1E"),
BorderColor = ColorTranslator.FromHtml("#2C2C2C"),
Location = new Point(450,top)
top += h_p;
flowLayoutPanel1.Controls.Add(new_note);
};

How to apply Border to Button control programmatically using C#

I am creating a Button control using C# as mentioned below in the code. I have created the rounded border for the button style. I am not able to see any property to assign the Border in the button.
var button = new System.Windows.Controls.Button
{
Name = "BtnOk",
Content = "OK",
Height = 20,
Width = 60,
HorizontalAlignment = HorizontalAlignment.Center,
Background = Brushes.DarkGray,
Foreground = Brushes.WhiteSmoke,
Margin = new Thickness(0,0,0,5)
};
Border border = new Border();
border.CornerRadius = new CornerRadius(3);
How can I apply Border in button programatically?
a button cannot aplly a border. a border can decorate a button:
border.Child = button;
usually Buttons already have a Border inside their Template (ControlTemplate). that Border isn't easily accessible - there is no special property of Button class, but that border can be found in visual tree after template was loaded.
additionally that Border can be customized by default style if you put it in Button.Resources. change CorderRadius using Style.Setter:
var button = new System.Windows.Controls.Button
{
Name = "BtnOk",
Content = "OK",
Height = 20,
Width = 60,
HorizontalAlignment = HorizontalAlignment.Center,
Background = Brushes.DarkGray,
Foreground = Brushes.WhiteSmoke,
Margin = new Thickness(0, 0, 0, 5)
};
var style = new Style
{
TargetType = typeof(Border),
Setters = { new Setter { Property = Border.CornerRadiusProperty, Value = new CornerRadius(3) } }
};
button.Resources.Add(style.TargetType, style);
or using object/collection initializers:
var button = new System.Windows.Controls.Button
{
Name = "BtnOk",
Content = "OK",
Height = 20,
Width = 60,
HorizontalAlignment = HorizontalAlignment.Center,
Background = Brushes.DarkGray,
Foreground = Brushes.WhiteSmoke,
Margin = new Thickness(0, 0, 0, 5),
Resources =
{
{
typeof(Border), new Style
{
TargetType = typeof(Border),
Setters =
{
new Setter { Property = Border.CornerRadiusProperty, Value = new CornerRadius(13) }
}
}
}
}
};
if many buttons should have different CornerRadius, changing Button's Template can be a solution. Change template and set CornerRadius as attached dependency property, like shown in this post: Set a property of a nested element in an WPF style
There is indeed a Border element in the default ControlTemplate for the Button but the easiest way to set the CornerRadius property of it, without having to define a custom template, is to wait until the Button has been loaded and then get a reference to it. Try this:
var button = new System.Windows.Controls.Button
{
Name = "BtnOk",
Content = "OK",
Height = 20,
Width = 60,
HorizontalAlignment = HorizontalAlignment.Center,
Background = Brushes.DarkGray,
Foreground = Brushes.WhiteSmoke,
Margin = new Thickness(0, 0, 0, 5)
};
button.Loaded += (ss, ee) =>
{
Border border = button.Template.FindName("border", button) as Border;
if (border != null)
border.CornerRadius = new CornerRadius(3);
};

Assigning multiple Children to Grid and visualizing them

I'm setting up an Xamarin.Forms app where i am trying to add a "Module", saving it in a list and then visualizing it on Android via grid cells.
The problem is within the visualization. The problem is that i am trying to add multiple children to the same grid cell, but they are overlaying with each other.
public void CreateModuleGrids()
{
foreach (Module item in _mm.ModulesList)
{
gOut.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(100) });
gOut.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
gOut.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(100) });
Label lblBez = new Label();
lblBez.Text = item.Name.ToString();
lblBez.VerticalOptions = LayoutOptions.Center;
lblBez.HorizontalOptions = LayoutOptions.Center;
lblBez.WidthRequest = 151;
lblBez.HeightRequest = 25;
Label lblStatus = new Label();
lblStatus.WidthRequest = 151;
lblStatus.HeightRequest = 25;
if (item.Type == "blind")
{
lblStatus.Text = "100 %";
lblStatus.VerticalOptions = LayoutOptions.Center;
lblStatus.HorizontalOptions = LayoutOptions.Center;
}
else
{
lblStatus.Text = "Closed";
lblStatus.VerticalOptions = LayoutOptions.Center;
lblStatus.HorizontalOptions = LayoutOptions.Center;
}
if (item.Type == "blind")
{
bmp100.WidthRequest = (119);
bmp100.HeightRequest = (117);
bmp100.Aspect = Aspect.AspectFit;
bmp100.VerticalOptions = LayoutOptions.Center;
bmp100.HorizontalOptions = LayoutOptions.Center;
gOut.Children.Add(bmp100, 0, 0);
}
else
{
bmpClosed.WidthRequest = (119);
bmpClosed.HeightRequest = (117);
gOut.Children.Add(bmpClosed, 0, 0);
}
if (item.Type == "blind")
{
ImageButton btnArrowUp = new ImageButton();
btnArrowUp.WidthRequest = 37;
btnArrowUp.HeightRequest = 50;
btnArrowUp.Source = "ArrowUp.png";
btnArrowUp.Aspect = Aspect.AspectFit;
btnArrowUp.VerticalOptions = LayoutOptions.Start;
btnArrowUp.HorizontalOptions = LayoutOptions.Start;
btnArrowUp.Clicked += new EventHandler(this.btnArrowUp_click);
ImageButton btnArrowDown = new ImageButton();
btnArrowDown.WidthRequest = 37;
btnArrowDown.HeightRequest = 50;
btnArrowDown.Source = "ArrowDown.png";
btnArrowDown.Aspect = Aspect.AspectFit;
btnArrowDown.VerticalOptions = LayoutOptions.End;
btnArrowDown.HorizontalOptions = LayoutOptions.End;
btnArrowDown.Clicked += new EventHandler(this.btnArrowDown_click);
gOut.Children.Add(lblBez, 0, 0);
gOut.Children.Add(lblStatus, 0, 0);
gOut.Children.Add(btnArrowDown, 0, 0);
gOut.Children.Add(btnArrowUp, 0, 0);
}
else
{
ImageButton btnOut = new ImageButton();
btnOut.Measure(37, 50);
btnOut.Source = "ArrowLeft.png";
btnOut.Clicked += new EventHandler(this.btnTipOpen_click);
ImageButton btnIn = new ImageButton();
btnIn.Measure(37, 50);
btnIn.Source = "ArrowRight.png";
btnIn.Clicked += new EventHandler(this.btnTipClose_click);
gOut.Children.Add(lblBez, 0, 0);
gOut.Children.Add(lblStatus, 0, 0);
gOut.Children.Add(btnIn, 0, 0);
gOut.Children.Add(btnOut, 0, 0);
}
}
My expectation is having a grid instance that contains a label with the name of the module on the upper part, an imagebutton on the left side, an imagebutton on the right side, an image in the center and last a label under the image that shows the status. Thanks in advance!
The problem is that i am trying to add multiple children to the same
grid cell, but they are overlaying with each other.
The items are overlaying with each other because you add them to the same position in Grid.
To place views in a Grid you'll need to add them as children to the grid, then specify which row and column they belong in.
The last two parameter in function grid.Children.Add specify the position of the item in Grid. For example, there is a Grid with two rows and two Columns, then (0,0) means top left, and (1,1) means bottom right.
// left, top
grid.Children.Add(topLeft, 0, 0);
grid.Children.Add(topRight, 1, 0);
grid.Children.Add(bottomLeft, 0, 1);
grid.Children.Add(bottomRight, 1, 1);
Back to your code, you add all your elements to (0,0), so they will appear in the same position.
gOut.Children.Add(lblBez, 0, 0);
gOut.Children.Add(lblStatus, 0, 0);
gOut.Children.Add(btnIn, 0, 0);
gOut.Children.Add(btnOut, 0, 0);
Another problem in your code is you need a Layout container(Like stacklayout or other layouts) as Jason said to manage your elements. Because you create them in a foreach loop, and in each loop you add the same thing with same position to the gOut.
I think the RIGHT way is create a Grid with your labels and imageButtons with right position in each loop.Then add this Grid to a Layout container(This layout container is use to layout the Grid created in each loop). At last, set this layout container as ContentPage's content, Content = layout container to display your elements.
Have a look at this document may help you and there is also a sample there.

Programmatically Added Textboxes not Appearing

I'm using WinForms in VS15 with C#.
I'm dynamically adding TextBoxs and Labels to my Form based upon a user selected value in a ComboBox (essentially this looks up a value in a data collection which tells my UI what controls it needs).
When I attempt to generate the controls, the Labels appear and layout just fine, however, the TextBoxs are remarkable in there absence.
I've tried fidelling with the MaximumSize and MinimumSize properties to see if they could be messing with something but it doesn't seem to be making any difference.
The code I use for doing this is below (I know the use of the List<Pair<Label,TextBox>> is pretty unecessary but I find it helps readability):
private void GenerateControls(string formType)
{
string[] formParameters = engine.GetFormParameters(formType);
if (formParameters == null) return;
SplitterPanel panel = splitContainer.Panel1;
panel.Controls.Clear();
List<Pair<Label, TextBox>> controlPairs = new List<Pair<Label, TextBox>>();
int tabIndex = 0;
Point labelPoint = panel.Location + new Size(20, 20);
Size initialOffset = new Size(0, 30);
Size horizontalOffset = new Size(40, 0);
Size tBoxSize = new Size(40,20);
foreach (string parameter in formParameters)
{
Label label = new Label
{
Text = parameter,
Tag = "Parameter Label",
Name = $"lbl{parameter}",
Location = (labelPoint += initialOffset)
};
TextBox textBox = new TextBox
{
AcceptsTab = true,
TabIndex = tabIndex++,
Text = "",
Tag = parameter,
Name = $"txt{parameter}",
MaximumSize = tBoxSize,
MinimumSize = tBoxSize,
Size = tBoxSize,
Location = labelPoint + horizontalOffset
};
controlPairs.Add(new Pair<Label, TextBox>(label, textBox));
}
foreach (Pair<Label, TextBox> pair in controlPairs)
{
panel.Controls.Add(pair.First);
panel.Controls.Add(pair.Second);
}
}
I don't believe that my use of Point + Size is the issue as the Point class overrides the + operator like so:
Unfortunately for me the issue appears to be simply that the dX was not a big enough value to prevent the text boxes from being hidden under the labels, I forgot that labels don't have transparent backgrounds.
While I was at it: I've removed the redundant List<Pair<<>>; added support for dynamically adjusting TextBox location based on Label size; and split it out into two separate loops, so my code now looks as below and works just fine:
private void GenerateControls(string formType)
{
string[] formParameters = engine.GetFormParameters(formType);
if (formParameters == null) return;
SplitterPanel panel = splitContainer.Panel1;
panel.Controls.Clear();
int tabIndex = 0;
Point labelPoint = panel.Location + new Size(20, 20);
Size verticalOffset = new Size(0, 30);
Size tBoxSize = new Size(200,20);
int maxLabelLength = 0;
foreach (string parameter in formParameters)
{
Label label = new Label
{
Text = parameter,
Tag = "Parameter Label",
Name = $"lbl{parameter}",
Location = (labelPoint += verticalOffset),
AutoSize = true
};
panel.Controls.Add(label);
if (label.Size.Width > maxLabelLength)
{
maxLabelLength = label.Size.Width;
}
}
Size horizontalOffset = new Size(maxLabelLength + 30, 0);
labelPoint = panel.Location + new Size(20, 20) + horizontalOffset;
foreach (string parameter in formParameters)
{
TextBox textBox = new TextBox
{
AcceptsTab = true,
TabIndex = tabIndex++,
Text = "",
Tag = parameter,
Name = $"txt{parameter}",
MaximumSize = tBoxSize,
MinimumSize = tBoxSize,
Size = tBoxSize,
Location = labelPoint += verticalOffset
};
panel.Controls.Add(textBox);
}
}
Thanks everyone who helped!

Unable to retrieve width of dynamically loaded label with autosize as true

I a bit stuck here. I am trying to dynamically add panel with two labels in it.
The first label must be auto sized and the second label should be positioned to the left of the first one which has a fixed max width and its also auto sized. My code is a follows
pnlSearchResults.SuspendLayout();
Panel pnlRow = new Panel
{
AutoSize = true,
BorderStyle = BorderStyle.FixedSingle,
Padding = new Padding(0),
Margin = new Padding(0),
Top = 0,
Left = 0,
Width = pnlSearchResults.Width - 30,
MaximumSize = new Size(pnlSearchResults.Width - 30, 0)
};
Label lblKey = new Label
{
Name = string.Format("lblKey{0}", 1),
Text = "My label goes here",
AutoSize = true,
TextAlign = ContentAlignment.MiddleLeft,
BorderStyle = BorderStyle.FixedSingle,
Padding = new Padding(0),
Margin = new Padding(0),
Top = 0,
Left = 0,
};
pnlRow.Controls.Add(lblKey);
Label lblValue = new Label
{
Name = string.Format("lblValue{0}", 1),
Text = "And my long text goes here... and it goes on and on and on and on and on and on and on and on and on and on and ...",
AutoSize = true,
Height = 27,
TextAlign = ContentAlignment.MiddleLeft,
BorderStyle = BorderStyle.FixedSingle,
Padding = new Padding(0),
Margin = new Padding(0),
Top = 0,
MaximumSize = new Size(pnlRow.Width - lblKey.Width - 10, 0),
Left = lblKey.PreferredWidth
};
pnlRow.Controls.Add(lblValue);
pnlSearchResults.Controls.Add(pnlRow);
pnlSearchResults.ResumeLayout();
pnlSearchResults.PerformLayout();
I read that calling suspend, resume or perform methods increases performance. And this is what im getting.
As you can see the second label is not getting properly aligned with the first label. Its working fine if i set autosize = false for first label, but I want my first label to be auto sized.
I am not sure what I am missing here, please guide me in the proper way.
This occurs because the label has not yet been drawn and therefore adjusted itself to the correct size. If you want this to work you cannot suspend and resume layouts, you cannot place the controls before the form is shown and you will probably need to call a Refresh in between placing the labels to allow the 1st one to autosize correctly.

Categories