Adding an Array of labels to a Panel - c#

I'm trying to add an array of labels to a panel in my Form.
I chose a label because I could set colors for the text.
If there is a better way, please let me know.
The code below runs fine but will only display one label.
I set a breakpoint and looked at the array before adding and all the
elements are there.
However, only one label actually shows up on the Panel.
Here's the code.
int y = 0;
int index = 0;
Label[] labels = new Label[10];
//Add Spareboard Employees to Spare List
foreach (Employee employee in EmployeeList)
{
labels[index] = new Label();
labels[index].Text = employee.Name;
labels[index].ForeColor = Color.Red;
labels[index].Location = new Point(0, y);
y = y + 10;
++index;
}
// Add the Label control to the form.
SparePanel.Controls.AddRange(labels);
Thanks in advance

The default size of the label is too big and each label's bottom is covering up the top of the label below it. You should add something like this:
labels[index].Size = new Size(50, 12);

maybe
Label[] labels = new Label[10];
needs to be
Control[] labels = new Control[10];

As far as I know, you need to implement the IEnumerable Interface and IEnumerate.Compare() method too, in order to iterate a foreach loop over your Employee object.
public class Employee : IEnumerator
{
//Implement IEnumerate method here
}
I'm not that experienced though so don't take my word for it! I would put more detailed code but I don't have it to hand.

Another possibility (which you were also looking for) is to draw the strings directly on the UI without adding controls. Do it during the paint event of the panel.
private void SparePanel_Paint(object sender, PaintEventArgs e)
{
using (SolidBrush empBrush = new SolidBrush(Color.Red))
{
int y = 0;
foreach (Employee employee in EmployeeList)
{
e.Graphics.DrawString(employee.Name, ((Panel)sender).Font, empBrush, 0, y);
y += 10;
}
}
}

Related

Dynamic button-C#

i have buttons this buttons for categories i want when click in this buttons i want to get rows from database products table and get two data product name and price and set it those values into dynamic buttons in windows application notes i have set location properities for button such like flowLayoutPanel
i have this code
private void btnhotdrink_Click_1(object sender, EventArgs e)
{
//int StartPositition = 100;
//int EndPosition = 10;
DataTable dt = new DataTable();
dt =clsprdcat.GET_HOTDRINK_CATEGORY();
for(int i=0; i < dt.Rows.Count; i++)
{
for (int s=0; s < dt.Columns[4]; s++)
{
Button l = addbuttons(i,s);
flowLayoutPanel1.Controls.Add(l);
//EndPosition +=70;
}
}
}
Button addbuttons(int i)
{
Button l = new Button();
l.Name = "Name" + l.ToString();
l.Text = "label" + l.ToString();
l.ForeColor = Color.White;
l.BackColor = Color.Green;
l.Font = new Font("Serif", 24, FontStyle.Bold);
l.Width = 170;
l.Height = 80;
//l.Location = new Point(start, end);
l.TextAlign = ContentAlignment.MiddleCenter;
l.Margin = new Padding(5);
return l;
}
How Can i Do this
All Buttons are inherently dynamically created at runtime. The Windows Forms Designer works via partial classes: You work on one part. The designer on the other (designer.cs). And at compile time the Designers code is executed by calling "InitializeComponents()" in the Constructor. It can only do exactly the same things you can do.
If you need help with creating buttons yourself, you can always look into the Designer part. Just do take care not to change anything in that part. This System only works because the designer is exclusively writing in the file and compilation/loading of a Project is prone to seizing up if you did manual changes.
As you wrote it right now, this code will create dt.Rows.Count buttons at the same locaiton. Usually the Button creation code it needs access to the loop variable, to adapt the positions of each consequtive element.. Personally I prefer to leave positioning of Elements as much to automatics as possible. These designs simply adapt better to changes in resolution and window size.

Drawing a label array in c#

For my project I would need to create clickable tiles sort of like a grid. To do so I have decided to try using an array of labels and clicking on any one of them would cause a mouse click event corresponding to the label clicked. I don't want to use the Visual Studio drag and drop labels to draw the 220 I need so i decided to create an array of Labels. Here is the code I am using to test out the use of the array of labels:
Label[] Tiles = new Label[10];
for (int i = 0; i != 10; i++)
{
for (int n = 0; n != 22; n++)
{
Tiles[i] = new Label();
Tiles[i].Size = new Size(62, 62);
Tiles[i].Location = new System.Drawing.Point(n * 62 + 118, 106 + i * 62);
Tiles[i].Text = (i+n).ToString();
Tiles[i].Name = (i + n).ToString();
Tiles[i].AutoSize = true;
Tiles[i].Click += new System.EventHandler(this.label1_Click);
}
}
I am using this code In the Form1_Load method but the problem is that it doesn't throw an error but also does not actually get the labels on the Form, it just initializes the labels but does not draw them, does someone know how to actually add them to the Form.
This is really easy to do!
In your innermost for loop, add this line:
this.Controls.Add(Tiles[i]);
It first gets all the controls on the form, then add the label in it!
However, I would advise you to add the labels to a Panel, just because since you're creating a grid, you should probably group the labels together using a Panel.
Create a panel in the designer, call it labelPanel or whatever, and call this method instead in the innermost for loop:
this.labelPanel.Controls.Add(Tiles[i]);
Please note that since the position of the labels are now relative to the panel, you need to adjust them again.
You have to add that labels to the form. like, put one groupbox in your form and named it as group1.
now add Labels to that group.
group1.Controls.Add(Tiles[i]);

Referencing a dynamically created control in a new TabPage

newbie programmer here after hours of searching has left me stumped.
I'm having trouble with referencing a control inside a tab created at RunTime with a button press. Basically what I have is a tabletop RPG calculator, using a Windows Form, that has a tabControl holding tab pages, with each tab page holding user-inputted stats for that individual enemy to be used in calculations.
The problem is that I want the user to be able to click a button to generate a new enemy tab page. Here is my code for generating an enemy tab page with a TextBox.
int enemyNumber = 0;
// Creates a new Enemy Tab
private void button2_Click_1(object sender, EventArgs e)
{
// Create a new TabPage
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
// Add Enemy Name Box
var newEnemyNameBox = new TextBox()
{
Name = "enemyNameBox" + enemyNumber,
Text = "",
Location = new Point(127, 11),
Size = new Size(133, 20)
};
// Add the controls to the new Enemy tab
newTabPage.Controls.Add(newEnemyNameBox);
// Add the TabPage to the TabControl
tabControl1.TabPages.Add(newTabPage);
// Increases the enemy's "reference number" by 1
// So that enemy tabs will be generated in order enemyTab0, enemyTab1, etc.
enemyNumber += 1;
}
This all works nicely. Unfortunately, after this point things have gotten ugly. I need to reference that TextBox named "enemyNameBox" + enemyNumber, and I'm not sure how to do so.
What I did was create "archVariables" to store the values from whatever enemy tab is selected, then use the appropriate archVariable in the program's calculations. IE: archEnemyName. The idea is that whatever tab the user is currently selected on (determined via SelectedIndex) the TextBox from that page will be used for the program's output.
Here are the two things I've tried after researching the matter:
// Attempt 1
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
archEnemyNameBox = ((TextBox)Controls["enemyNameBox" + i]).Text;
}
}
This code simply throws a NullReferenceException when I press the button. So after researching more I tried this:
// Attempt 2
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)));
archEnemyNameBox = tb2.Text;
}
}
This time I got an Error: Cannot convert type 'System.Windows.Forms.Control[]' to 'System.Windows.Forms.TextBox'
I feel like the second method I have here is probably closer to the correct way to do this, but apparently I'm still not getting it right. I've learned a lot by searching the information on stackoverflow and msdn.microsoft but nothing has gotten me past this problem.
Any help would be appreciated.
basically the problem with your second attemp is that enemyTab.Controls.Find("enemyNameBox" + i, true) returns an array of Controls Control[] and you're trying to convert that to a Control here is the problem, you should get the first control in that array and then convert it to a Control so it should be like this:
private void defendCalcButton_Click(object sender, EventArgs e)
{
for (int i = 0; i < tabControl1.SelectedIndex; i++)
{
TextBox tb2 = new TextBox();
tb2 = ((TextBox)(enemyTab.Controls.Find("enemyNameBox" + i, true)[0]));
archEnemyNameBox = tb2.Text;
}
}
but it is not the BestWay to do so it seems that everytime a user adds a new tabPage it will have the same Controls right? so why not create an userControl with any Control you have on your TabPage? so when you press the user press to add a new tab your code should be like so:
private void CreateNewEnemyTab()
{
var newTabPage = new TabPage()
{
Text = "Enemy " + enemyNumber,
};
EnemyTabUserControl enemyTab = new EnemyTabUserControl(enemyNumber);
here the EnemyTabUserControl should have all the components you need;
newTabPage.Controls.Add(enemyTab);
tabControl1.TabPages.Add(newTabPage);
}
and the code to bring the TextBox from the current tab could be as follow (you are going to need to reference LINQ)
using System.Linq;
//First Lets create this property, it should return the selected EnemyTabUserControl inside the tabControl
public EnemyTabUserControl CurrentTab {
get {
return tabControl1.SelectedTab.Controls.OfType<EnemyTabUserControl>().First();
}
}
// then if we make the textbox you want to reference from outside the code we can do this
CurrentTab.NameOfTheTextBox;
Patrick has solved your fundamental problem, but I don't think you need the loop in there at all. Here I've broken the steps out so you can see what needs to happen a little better:
private void defendCalcButton_Click(object sender, EventArgs e)
{
Control[] matches = this.Controls.Find("enemyNameBox" + tabControl1.SelectedIndex.ToString(), true);
if (matches.Length > 0 && matches[0] is TextBox)
{
TextBox tb = (TextBox)matches[0];
archEnemyNameBox = tb.Text;
}
}

C# Order a list of labels by location on screen

How can I order a list of labels by their location on screen?
My labels move around the screen but only in X axis, this is the code that I have but I notice it isn't working.
labels.OrderBy(x => x.Location.X);
Thank you in advance!
Edit: This is how I'm testing if it works or not...
private void actualizarPosicoes() {
labels.OrderBy(x => x.Location.X);
MessageBox.Show(labels.First.Value.Text.ToString());
}
But I want to use it when a label is removed from the screen but first I have to make the OrderBy working.
private void reorder(Label lb) {
labels.OrderBy(x => x.Location.X);
var it = labels.GetEnumerator();
var node = it.Current;
while (it.MoveNext())
{
var nextNode = it.MoveNext();
if (nextNode != null)
{
if (nextNode.Equals(lb))
{
nextNode = it.MoveNext();
it.Current.Location = new Point(node.Right, 0);
}
}
node = it.Current;
}
}
I have a global linkedlist called labels:
private LinkedList<Label> labels;
I suggest you to use FlowLayoutPanel as container to your labels. Removing any label will automatically re-arrange other labels.
Here is sample - assume you have labelsFlowLayoutPanel to host labels and addLabelButton to add labels:
private void addLabelButton_Click(object sender, EventArgs e)
{
Label label = new Label();
label.Text = DateTime.Now.ToLongTimeString();
label.DoubleClick += label_DoubleClick;
labelsFlowLayoutPanel.Controls.Add(label);
}
void label_DoubleClick(object sender, EventArgs e)
{
Label label = (Label)sender;
labelsFlowLayoutPanel.Controls.Remove(label);
label.DoubleClick -= label_DoubleClick;
}
Each time you click addLabelButton new label is created and inserted to the right of other labels. Double-clicking on any label will remove it and re-arrange other labels.
Further info - Windows Forms Controls Lesson 5: How to use the FlowLayout Panel
Without offering any suggestions for a better approach, and to answer the question directly, the problem with you code is as follows:
The OrderBy method with not modify the instance is is called on, it will instead return a new IOrderedEnumerable<TSource> where TSource is the type of object, for example Label. This means that your code should look like this:
labels = labels.OrderBy(x => x.Location.X);
Depending on what type of object labels is, you may need to convert it to a List like so:
labels = labels.OrderBy(x => x.Location.X).ToList();
EDIT: I just realized you are using LinkedList, I would recommend changing this to private List<Label> labels;
I finally did it!
There is the answer if anyone else need it...
private void reorder() {
var it2 = labels.OrderBy(x => x.Location.X);
Label orderLb = it2.ElementAt(0);
for (int i = 1; i < labels.Count; i++)
{
it2.ElementAt(i).Location = new Point(orderLb.Right, 0);
orderLb = it2.ElementAt(i);
}
Thank you all for the help, really appreciate it!

WinForms ComboBox problem

In a Windows Form Application, I have a ComboBox1 which gets initialized in InitializeComponent() function. I add the values into it in a different function.
snippet:
public form1()
{
InitializeComponent();
addDataToDropDowns();
}
The problem I have is that, the rows loaded into the ComboBox1 have many characters(/length) and are not to be seen completely width wise.
Is it possible to have a horizontal scrollbar built into the ComboBox1 so that I can see the hidden part of the row too...??
Any ideas/inputs will be appreciated!
Thanks,
Ivar
There is actually a DropDownWidth property that controls how wide the drop down area is. This way you can have a narrow control that doesn't take up too much space on the form, but a larger drop down area that could extend over as much of the screen as you want.
http://www.codeproject.com/KB/combobox/ComboBoxAutoWidth.aspx
That has code sample that shows how to capture the event and widen the box.
OR, you could have it as a seperate function you manually call, like this:
http://weblogs.asp.net/eporter/archive/2004/09/27/234773.aspx
Combining the links in Caladain's answer, here is the code. It works for both strings and data bound objects. The method cbSample_DropDown() is linked to the DropDown event of the ComboBox.
private void AdjustWidthComboBox(ComboBox comboBox)
{
int width = comboBox.DropDownWidth;
using (Graphics g = comboBox.CreateGraphics())
{
Font font = comboBox.Font;
int vertScrollBarWidth =
(comboBox.Items.Count > comboBox.MaxDropDownItems)
? SystemInformation.VerticalScrollBarWidth : 0;
foreach (object item in comboBox.Items)
{
string valueToMeasure = comboBox.GetItemText(item);
int newWidth = (int)g.MeasureString(valueToMeasure, font).Width + vertScrollBarWidth;
if (width < newWidth)
{
width = newWidth;
}
}
}
comboBox.DropDownWidth = width;
}
private void cbSample_DropDown(object sender, EventArgs e)
{
AdjustWidthComboBox(sender as ComboBox);
}

Categories