dynamically created labels overlap each other - c#

I want to create labels that will follow one another. I have a grid name WordTemplateLayout to which I want to add the labels. I add them dynamically on the wpf window constructor after InitializeComponent() is called. Here is the method creating the labels:
private void CreateWordTemplate()
{
IList<char> template = CurrentGame.Template;
double widthPerBox = WordTemplateLayout.Width / template.Count;
//template.Count being a number, irrelevant to the question
for (int i = 0; i < template.Count; i++)
{
var templateVisual = new Label();
templateVisual.Name = "c" + i;
templateVisual.Width = widthPerBox;
templateVisual.Height = WordTemplateLayout.Height;
templateVisual.Background = new SolidColorBrush(Colors.Aqua);
WordTemplateLayout.Children.Add(templateVisual);
}
}
the problem being, that what actually appends is that instead of the labels lining up one after the other, they overlap each other:
The aqua box is all the labels overlap each other
so what I am asking, is how can I make the labels line up (horizontally) instead of to overlap?

As others have pointed out, you're better off using a StackPanel, or learning how to use viewmodels and data binding. Having said that, to answer your direct question, here's how you'd do it programmatically.
**NOTE: Ignore the 5 that I'm passing in to the methods, and instead use your template.Count. This is was just for me to get it to work on my end.
public MainWindow()
{
InitializeComponent();
CreateGridLayout(5);
CreateWordTemplate(5);
}
// Define the Grid layout
// If you want the labels to follow one another horizontally, define columns.
// If you want them stacked vertically, instead define rows.
private void CreateGridLayout(int count)
{
for (int i = 0; i < count; i++)
{
WordTemplateLayout.ColumnDefinitions.Add(new ColumnDefinition());
}
}
private void CreateWordTemplate(int count)
{
double widthPerBox = WordTemplateLayout.Width / 5;
for (int i = 0; i < 5; i++)
{
var templateVisual = new Label();
templateVisual.SetValue(Grid.ColumnProperty, i); // SET YOUR COLUMN HERE!
templateVisual.Name = "c" + i;
templateVisual.Width = widthPerBox;
templateVisual.Height = WordTemplateLayout.Height;
templateVisual.Background = new SolidColorBrush(Colors.Aqua);
templateVisual.Content = templateVisual.Name;
WordTemplateLayout.Children.Add(templateVisual);
}
}

you need to use another layout so the new elements will get in normal order. try stackPanel.
you can use grid and give each new label Row=index.
you can give each new label margin, like newLabel.margin-top = index*50

Related

Tricky : Array of controls dynamically created causing problems

I put "Tricky" in the title because I'm aware that it will be hard to understand precisely what I want but I'll try to be clear.
I have a textBox with an event for TextChange which allow me to create the number of texboxes the user want (we will call it tChange).
Here is a part of the code for this event :
int tester;
bool flag = false;
if (!Int32.TryParse(tChange.Text, out tester))
{
flag = false;
return;
}
else
{
num = Convert.ToInt16(tChange.Text);
flag = true;
}
if (flag == true)
{
if (num >= 1)
{
for (int i = 0; i < num; i++)
{
this.Size = new Size(590, 225 + 105 * i);
textBoxesQ[i] = new TextBox();
this.Controls.Add(textBoxesQ[i]);
textBoxesQ[i].Size = new Size(45, 20);
textBoxesQ[i].Location = new Point(25, 100 + 100 * i - 1);
}
}
}
So the user enter the value and everything is OK. If he wants to change the number in the tChange, no problem too! The form is resize and the TextBoxes are created. However if he does this (change the value of tChange), everything goes wrong! Errors like
Index out of range
or I can't get the values from the TextBoxes etc..
I started thinking that the TexBoxes were created in front of the previous ones and the error came from this, so I tried to put the new ones to front, bring the old ones to front but none worked..
textBoxesQ[i].BringToFront();
textBoxesQ[i].SendToBack();
I also tried to delete the old ones before creating the new but I think that my code was wacky and it didn't work at all.
textBoxesQ[i].Dispose();
EDIT : As #Dr. Stitch said, It may come from not reinitializing the TextBoxes each time the text in tChange is changed. Now I just need to figure out how to make it happen.
You need to do three things when the number changes:
Remove any existing textboxes
Create a new array with the new size, and save a reference to that new array into your field (textBoxesQ)
Initialize the array
So something like:
if (textBoxesQ != null)
{
foreach (var textBox in textBoxesQ)
{
Controls.Remove(textBox);
}
}
textBoxesQ = new TextBox[size];
for (int i = 0; i < size; i++)
{
var textBox = new TextBox
{
Size = new Size(45, 20);
Location = new Point(25, 100 + 100 * i - 1);
};
Controls.Add(textBox);
textBoxesQ[i] = textBox;
}

How to reverse a FlowLayoutPanel?

I'm trying to reverse the order of Controls in a FlowLayoutPanel.
I tried converting the ControlCollection to an array and then reversed that and cleared the ControlCollection and then readded the Controls. But this doesn't seem to have the planned effect.
Here's the code I use:
private static void ReverseLayout(Control control, bool suspend = true) {
if (suspend) control.SuspendLayout();
Control[] newCC = new Control[control.Controls.Count];
control.Controls.CopyTo(newCC, 0);
Array.Reverse(newCC);
control.Controls.Clear();
//control.Controls.AddRange(newCC);
for (int i = 0; i < newCC.Length; i++) {
newCC[i].Location = new System.Drawing.Point(); // maybe? no :\
newCC[i].TabIndex = i; // maybe? no :\
control.Controls.Add(newCC[i]);
}
if (suspend) control.ResumeLayout(false);
}
Your code seems more complicated than it needs to be. Try putting the controls in a List<Control> and then call reverse on it, put the collection back:
int firstTabIndex = flp.Controls[0].TabIndex;
List<Control> controls = flp.Controls.Cast<Control>().ToList();
flp.Controls.Clear();
controls.Reverse();
flp.Controls.AddRange(controls.ToArray());
For the TabIndex property, you would have to reapply the value:
for (int i = 0; i < flp.Controls.Count; ++i) {
flp.Controls[i].TabIndex = firstTabIndex + i;
}

C# display components based on name

Is it possible to set a components visible attribute based on its name?
I have 12 "master" components (comboboxes) if you want to call them that and based on the selection in these I want to display anywhere from 1 to 16 textboxes. These are named in numeric order such as combobox1_textbox_0, combobox1_textbox_1 and so on. What I would like to do ideally is take the index of the combobox and pass it as a parameter to a method that sets the textboxes visible attribute to visible/hidden depending on the index passed into the method.
Is this possible? in pseudocode or what you call it I would like it to work something like this:
private void methodToSetVisibleAttribute(int indexFromMainComboBox)
{
for(int i = 0; i < 15; i++)
{
if(i < index)
{
combobox1_textbox_+i.Visible = true;
}
else
{
combobox1_textbox_+i.Visible = false;
}
}
}
I could do panels or something for the choices but seeing as all the selections from the combobox will use the same textboxes but in different amounts it seems like alot of work to make a panel for every possible selection not to mention difficult to expand the program later on.
Assuming you are using Windows Forms and not WPF, you can use ControlCollection.Find() to find controls by name:
var textBox = this.Controls.Find(string.Format("combobox1_textbox_{0}", i), true).OfType<ComboBox>().FirstOrDefault();
if (textBox != null)
textBox.Visible = (i < index);
else
Debug.Assert(false, "textbox not found"); // Or throw an exception if you prefer.
I'll suggest an alternative to your approach, maybe not quite what you're looking for:
Place your combo boxes in a List<ComboBox> and you can access them by an index number.
List<ComboBox> myCombos = new List<ComboBox>();
for (int i = 0; i < 16; i++)
{
ComboBox cb = new ComboBox();
//do what ever you need to do here. Set its location, add items, etc.
Form1.Controls.Add(cb); //Alternatively add it to another container.
myCombos.Add(cb); //Now it's in a list.
}
Modify them like this:
for(int i = 0; i < 15; i++)
{
if(i < index)
{
myCombos[i].Visible = true;
}
else
{
myCombos[i].Visible = false;
}
}
Or even more succintly:
for(int i = 0; i < 15; i++)
{
myCombos[i].Visible = i < index;
}

How to access labels from loop and change their text

I have list where are 6 sentences which I want to put in 6 different labels.
All six labels are named Slot0Sentence, Slot1Sentence, Slot2Sentence...
This is how I loop
for (int i = 0; i < ls.Count; i++)
{
Slot0Sentence.Text = ls[i];
}
However I dont know how to access other labels.
If there would be normal string I would do Slot + i + Sentence but in this case this dont work.
with an array of labels you can control their properties. you don't need design here, you can do that with code.
Label[] l = new Label[6];
int x = 20;
for (int i = 0; i < l.Length; i++)
{
l[i] = new Label();
l[i].Name = "Hello " + i.ToString();
l[i].Text = "Hello " + i.ToString();
l[i].Location = new Point(x, 10);
x += 100;
}
you can change the names and text to whatever you like.
I'd just use Children property of parent container (Grid, StackPanel,..). This gives you a collection which supports indexes. Additionally, in case you have different controls, use if statement
if(element in Label)
{
element.Text = ...
}

Creating C# winform simple dynamic controls

private void CreateNewControl()
{
List<Control> list = new List<Control>();
TableLayoutPanel layout = new TableLayoutPanel();
layout.Dock = DockStyle.Fill;
this.Controls.Add(layout);
layout.ColumnCount = 3;
layout.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
for (int i = 0; i < 9; i++)
{
if (wantedType == DevExpress.XtraEditors.CheckEdit)
{
CheckBox chk = new CheckBox();
chk.Tag = i;
layout.Controls.Add(chk);
layout.AutoScroll = true;
}
if (wantedType == LabelControl)
{
Label chk = new Label();
chk.Tag = i;
layout.Controls.Add(chk);
layout.AutoScroll = true;
}
// I want to set the columnwidth of the layout so that when the labels are displayed they do not get clustered and look exactly like when displaying the checkboxes.How do I do it?
In general, what I do is:
Use the IDE in a 'prototype' project, to create a form with the controls in the positions that I want
Look at the source code created by the IDE (in the MyFormName.Designer.cs file) to see what source code is generated by the IDE to creat these controls
Create my own form in my real project, with hand-coded code that's based on what I learned from the prototype which I created using the IDE
// Loop through all the controls you want to add.
// Add a integer field that measures the highest width of each control like
int _iMaxWidth = 0;
for (int i=0; i < TotalControls.Count; ++i)
{
if ( control[i].Width > _iMaxWidth)
_iMaxWidth = control[i].Width
}
// Then you'll know what the width size of the column should be.
Col.Width = iMaxWidth + 2; // +2 to make things a little nicer.

Categories

Resources