looping on parent controls in order - c#

im looping on 24 picturebox on groupbox for displaying something, but it doesn't loop in the right order, it may start at the 18th picture box and here is the code im using
foreach (var pb in groupBox1.Controls)
{
if (pb is PictureBox && previewIndexer < Previewer.Count)
{
try
{
((PictureBox)pb).Image = ...
previewIndexer++;
...
}
catch
{
...
}
}
}
i would want to loop in ascending order of which it loops on picturebox1 first till picturebox24, thanks and have wonderful day

Your problem is ordering controls by name. You need alphanumeric sorting in order to picturebox10 go after picturebox5. This can be done if you will sort pictureboxes by numbers in their names:
foreach (var pb in groupBox1.Controls.OfType<PictureBox>()
.OrderBy(x => Int32.Parse(x.Name.Substring("picturebox".Length))))
{
pb.Image = ...;
}
I would even created some method like (it removes all non-numeric characters from control name and tries to parse rest to integer value):
private int GetControlId(Control control)
{
int id;
string idToParse = Regex.Replace(control.Name, #"\D+", "");
return Int32.TryParse(idToParse, out id) ? id : 0;
}
And used this method in query
var pictureBoxes = groupBox1.Controls.OfType<PictureBox>();
foreach (var pb in pictureBoxes.OrderBy(GetControlId))
{
pb.Image = ...;
}

You can take benefit of LINQ here, suppose you named your pictureboxes as picture1, picture2, ...
foreach (var pb in groupBox1.Controls.OfType<PictureBox>().OrderBy(p=>int.Parse(Regex.Replace(k,"\\D*",""))))
{
if(previewIndexer < Previewer.Count) {
try
{
((PictureBox)pb).Image = ...
previewIndexer++;
...
}
catch
{
...
}
}
}

You can index the Controls property with a string which refers to the name of the control.
This might be the simpliest solution:
for (int i = 1; i <= 24; i++)
{
PictureBox pb = groupBox1.Controls["picturebox" + i.ToString()] as PictureBox;
}

Related

Changing picturebox #N

I want to set up a for loop to change the content of pictureboxes with using like picturebox[i] but I can't figure out how to do that.
My idea was doing something like this, the whole code snippet has a lot of if statements but is similar to this.
for (int i = 0; i < length; i++)
{
pictureBox[i].image = Image.FromFile("../Pics/Salmon.jpg");
}
I don't know how to do it and I cant' find anyone with the same problem. Is there any way how to do this? Or do I have to manually do it.
You can prepare collection first and then iterate it:
var pictureBox = new[] { pictureBox1, pictureBox2, ... };
for (int i = 0; i < pictureBox.Length; i++)
pictureBox[i].Image = Image.FromFile("../Pics/Salmon.jpg");
You can use same approach to have different pictures, I'll let it for you as a kind of exercise ;)
You can't access the PictureBox controls in an array. They are part of the overall control collection on a form. You can loop through all the controls on a form and only act on the picture boxes.
Example:
foreach (Control ctrl in this.Controls)
{
if (ctrl is PictureBox picBox) //determine if the control is a picture box
{
picBox.Image = Image.FromFile("../Pics/Salmon.jpg");
}
}
Or using Linq:
this.Controls.OfType<PictureBox>().ToList()
.ForEach(p => p.Image = Image.FromFile("../Pics/Salmon.jpg"));
Or if you have to act on based on posistion array:
var pics = this.Controls.OfType<PictureBox>().ToArray();
for (int i = 0; i < pics.Length; i++)
{
pics[i].Image = Image.FromFile("../Pics/Salmon.jpg");
}

Filling a dictionary with numbers from textbox and with the textboxes as keys

I'm writing a code where I want to get the numbers from multiple TextBox controls into a collection. Sort it, then change the background color of the textboxes containing the top 3 highest value. This is the dictionary
Dictionary<System.Windows.Forms.TextBox, string> all_cycles = new Dictionary<System.Windows.Forms.TextBox, string>(10);
for (var i = 1; i < 5; i++)
{
all_cycles.Add(((System.Windows.Forms.TextBox)this.Controls.Find("txtendc" + Convert.ToString(i), true)[0]),this.Text);
}
I know that with "this.text" I won't get the textboxes values so that's why I'm asking.
I also tried creating an array which only contains the values of the textboxes which I then use to fill the dictionary. But it always drops an Index was outside the bounds of the array. Or Index was out of range exception. This drops the out of range exception:
List<System.Windows.Forms.TextBox> txtendc = new List<System.Windows.Forms.TextBox>(10);
for (var i = 1; i < 5; i++)
{
txtendc.Add((System.Windows.Forms.TextBox)this.Controls.Find("txtendc" + Convert.ToString(i), true)[0]);
}
int[] endc_content = new int[10];
for (var i = 1;i < 5; i++)
{
endc_content[i]= int.Parse(txtendc[i].Text);
}
I don't have any problem with the coloring, just filling up the dictionary. If you have a better solution than the dictionary collection please tell me.
Thanks
EDIT: This whole code is inside a timer tick event if it matters anything
I would create a list of all of your TextBoxes in code behind when you instantiate your class. Then you could use Linq to get the top 3. Remember that you will need to confirm that all of the textboxes actually contain numbers - ensure that your program doesn't crash if you enter text into one of them. If all of the TextBoxes in the form will be holding these numbers, you could do something like this to validate and sort in one method (not tested):
private List<TextBox> myTextBoxes { get; set; }
public Form1()
{
InitializeComponent();
foreach (Control c in this.Controls)
{
if (c.GetType() == typeof(TextBox))
myTextBoxes.Add((TextBox)c);
}
}
private IEnumerable<TextBox> getTop3()
{
return myTextBoxes.Where(tb => tb.Text.AsEnumerable().All(char.IsDigit)).Select(tb => tb).OrderByDescending(tb => Double.Parse(tb.Text)).Take(3);
}
The first step of the Linq query converts the text to an enumerable of char and ensures that all characters contain digits (instead of letters). The second selects these textboxes. The third parses them into doubles and compares them, highest number first. The last takes the first three in the list (the textboxes containing the highest 3 numbers)
EDIT: Based on your comments, the code could be simplified to:
public Form1()
{
InitializeComponent();
}
private IEnumerable<TextBox> getTop3(string textboxPrefix)
{
List<TextBox> textBoxesToSort = new List<TextBox>();
foreach (Control c in this.Controls)
if (c.GetType() == typeof(TextBox) && c.Name.StartsWith(textboxPrefix))
textBoxesToSort.Add((TextBox)c);
return textBoxesToSort.OrderByDescending(tb => Double.Parse(tb.Text)).Take(3);
}
You can sort those TextBox controls based on values and then select top 3 from the list:
var result = this.Controls.OfType<TextBox>().Select(x =>
{
try { return new { Key = x, Value = Convert.ToInt32(x.Text) }; }
catch { return null; }
})
.Where(x=>x!=null)
.OrderByDescending(x => x.Value)
.Take(3)
.ToDictionary(x => x.Key, x => x.Value);
To test the result, show a message box containing values:
MessageBox.Show(string.Join("\n",
result.Select((x, i) => string.Format("{0}:{1}", i+1, x.Value))));
Above example will be applied on any list of TextBox controls and just for example I used all TextBox controls of the form.
Please try this, hope it helps:
List<System.Windows.Forms.TextBox> all_cycles = new List<System.Windows.Forms.TextBox>(10);
int[] indexOfTextBox = new int[3];
foreach(var cycle in all_cycles)
{
int value = Convert.ToInt16(cycle.Text);
if (value > indexOfTextBox[0])
{
indexOfTextBox[0] = value;
}
else if (value > indexOfTextBox[1])
{
indexOfTextBox[1] = value;
}
else if (value > indexOfTextBox[2])
{
indexOfTextBox[2] = value;
}
}
all_cycles[indexOfTextBox[0]].BackColor = ConsoleColor.Red;
all_cycles[indexOfTextBox[1]].BackColor = ConsoleColor.Blue;
all_cycles[indexOfTextBox[2]].BackColor = ConsoleColor.Green;

WinForm, Dynamic Label delete (Beginner)

I made 10 Labels dynamically.
Now, I'd like to delete them with the same way (dynamically).
What do I have to do ?
Thanks..
for( int i = 1 ; i < 11 ; i++ )
{
var myLabel = new System.Windows.Forms.Label();
myLabel.Text = "dynaLabel_" + i.ToString();
myLabel.Location = new System.Drawing.Point(200, i * 23);
Controls.Add(myLabel);
Application.DoEvents();
Thread.Sleep(199);
}
Just a simple for loop in which you should find out label, remove it from Controls and release allocated resources:
// let's use regular expression to validate name;
// String.StartsWith is also a good choice
using System.Text.RegularExpressions;
...
// while removing, loop backward
for (int i = Controls.Count - 1; i >= 0; --i) {
Label label = Controls[i] as Label;
// Control is Label with specific name
if ((label != null) && Regex.IsMatch(label.Text, "^dynaLabel_[0-9]+$")) {
Controls.RemoveAt(i);
// do not forget to release resources allocated (here is HWND - window handle)
label.Dispose();
}
}
So is this your homework?
using System.Linq;
...
foreach(Control c in Controls.OfType<Label>().ToList())
{
//check if correct label if you need to
Controls.Remove(c);
}
You could use Controls and LINQ Where to do the job:
var labels = Controls.Cast<Control>()
.Where(c => c is Label && c.Text.StartsWith("dynaLabel_"));
foreach (var label in labels){
Controls.Remove(label);
label.Dispose();
}
Since you create the labels as Label and having the Text starting with value of dynaLabel_, your LINQ should also find those specific labels. Thus, you use both c is Label and StartsWith("dynaLabel_") there.

How switch between pictureboxes?

I have many pictureboxes in my project and would like to know if there is code that makes it easy to switch between them in C#, for example:
// I dont' want to have to do this all the time
pictureBox38.Image = slagalica.Properties.Resources.grn_tick;
pictureBox39.Image = slagalica.Properties.Resources.grn_tick;
// I want something like this
int n = 38
pictureBox(n).Image = slagalica.Properties.Resources.grn_tick;
pictureBox(n+1).Image = slagalica.Properties.Resources.grn_tick;
Is it possible? Any code is welcome.
You can loop through all controls and apply settings to those, which are pictureboxes:
foreach(Control control in Controls)
{
if (control is PictureBox)
((PictureBox)control).Image = slagalica.Properties.Resources.grn_tick;
}
Or you can create your custom picturebox and use it instead of default pictureboxes:
public class SlagalicaPictureBox : PictureBox
{
public SlagalicaPictureBox()
{
Image = slagalica.Properties.Resources.grn_tick;
}
}
You might want to create list of Picture boxes. Then you will be able to write something like pictureBoxes[0].Image=img;
Definitely consider what Lazyberezovsky posts. However, if you want to iterate through controls rapidly, you can also construct a control array.
You can easily do this:
List<PictureBox> list = new List<PictureBox>();
list.Add(pictureBox38);
list.Add(pictureBox39);
Then you can iterate through them as follows:
foreach (PictureBox pb in list) {
pictureBox.Image = slagalica.Properties.Resources.grn_tick;
}
If you want to be more clever, you can even create an array with a specified size and do the same.
PictureBox[] pb_array = new PictureBox[50];
pb_array[38] = pictureBox38;
Add them to a list and use the indexer, for example a List<PictureBox> or PictureBox[]:
var dir = new System.IO.DirectoryInfo(imagePath);
List<FileInfo> images = dir.GetFiles("*.jpg", System.IO.SearchOption.AllDirectories).ToList();
List<PictureBox> pictures = new List<PictureBox>();
foreach (FileInfo img in images) {
PictureBox picture = new PictureBox();
picture.Image = Image.FromFile(img.FullName);
pictures.Add(picture);
}
Now you ca access the pictures by index, for example:
var rnd = new Random();
int imgIndex = rnd.Next(0, 38);
PictureBox img = pictures[imgIndex];
Or in a loop
for(int i=0; i<38; i++)
{
PictureBox img = pictures[i];
}
Edit: If you just want to find all PictureBoxes on your form, you can use Enumerable.OfType which filters and casts the controls accordingly:
IEnumerable<PictureBox> allPictures = this.Controls.OfType<PictureBox>();
foreach(var pic in allPictures)
{
//....
}
while(n <= (number of pics)){
n++
pictureBox(n).Image = slagalica.Properties.Resources.grn_tick;
}
Yes i know this is Objective-C, but you get the main idea

MS Chart Control series label positioning

I've got a gantt chart (RangeBar) I've made with the MS Chart control; for some shorter series, the label gets displayed outside the bar; I'd prefer to set it so the label gets stays inside the bar and gets truncated (with ellipsis would be nice). Is there a way of doing this? I've been poking around in the properties of the chart and series for ages now with no success.
I think the property you need to set is BarLabelStyle
eg.
chart.Series["mySeries"]["BarLabelStyle"] = "Center";
See this Dundas page that explains that custom property which should be similar or same for the MS Chart control.
In the end I rolled my own using this (yes it's messy, it'll get tidied up when I have time):
private static void Chart_PostPaint(object sender, ChartPaintEventArgs e)
{
Chart c = ((Chart)sender);
foreach (Series s in c.Series)
{
string sVt = s.GetCustomProperty("PixelPointWidth");
IGanttable ig = (IGanttable)s.Tag;
double dblPixelWidth = c.ChartAreas[0].AxisY.ValueToPixelPosition(s.Points[0].YValues[1]) - c.ChartAreas[0].AxisY.ValueToPixelPosition(s.Points[0].YValues[0]);
s.Label = ig.Text.AutoEllipsis(s.Font, Convert.ToInt32(dblPixelWidth)-dblSeriesPaddingGuess);
}
}
public static string AutoEllipsis(this String s, Font f, int intPixelWidth)
{
if (s.Length == 0 || intPixelWidth == 0) return "";
var result = Regex.Split(s, "\r\n|\r|\n");
List<string> l = new List<string>();
foreach(string str in result)
{
int vt = TextRenderer.MeasureText(str, f).Width;
if (vt < intPixelWidth)
{ l.Add(str); }
else
{
string strTemp = str;
int i = str.Length;
while (TextRenderer.MeasureText(strTemp + "…", f).Width > intPixelWidth)
{
strTemp = str.Substring(0, --i);
if (i == 0) break;
}
l.Add(strTemp + "…");
}
}
return String.Join("\r\n", l);
}
This seems to work quite happily as long as it is the Post_Paint event (If you use the Paint event it will stop ToolTips from showing)

Categories

Resources