Strange behavior while removing image from ListView - c#

I have a ListView which I populate with the contents of an ImageList. When an Item is selected from the list, I check if the file still exists. If it doesn't, I want to remove it both from the Image List (which is private static) and from the ListView.
For some strange reason, which I can't figure out, after removing the selected image from the list, the image right after it disappears and the last image in the list appears twice.
For example, if the list held the following images: IMG1, IMG2, IMG3, IMG4, IMG5 and I remove IMG2 the new list will look like this: IMG1, IMG4, IMG5, IMG5.
Furthermore, if I select the second image from the list (which is now IMG4) and display it in some picture control, IMG3, which was supposed to be in that place will be displayed in the control.
Any ideas what's going on here?
EDIT:
Populating the List view:
private static ImageList stampsImages
if (stampsImages == null)
{
stampsImages = new ImageList();
stampsImages.ImageSize = new Size(125, 75);
}
DirectoryInfo di = new DirectoryInfo(Globals.Directory);
if (di.Exists)
{
FileInfo[] dFiles = di.GetFiles("*.png");
int stampListSize = stampsImages.Images.Count;
for (int i = 0; i < dFiles.Length; i++)
{
int idx = stampsImages.Images.IndexOfKey(dFiles[i].FullName);
if (idx < 0)
{
stampsImages.Images.Add(Bitmap.FromFile(dFiles[i].FullName));
stampsImages.Images[stampListSize].Tag = dFiles[i].FullName;
stampsImages.Images.SetKeyName(stampListSize, dFiles[i].FullName);
stampListSize++;
}
}
}
else di.Create();
for (int i = 0; i < stampsImages.Images.Count; i++)
{
ListViewItem stmp = new ListViewItem("", i);
lvwStamps.Items.Add(stmp);
}
lvwStamps.LargeImageList = stampsImages;
Checking if the file still exists:
private bool IsStampAvailable(int listIdx)
{
bool stampExists = true;
string stampFile = stampsImages.Images.Keys[listIdx];
if (!File.Exists(stampFile))
{
lvwStamps.Items.RemoveAt(listIdx);
stampsImages.Images.RemoveAt(listIdx);
stampExists = false;
}
return stampExists;
}

Every time you remove an image from the ImageList, you'll have to decrement the ImageIndex of each ListViewItem that points to an ImageIndex equal or higher than the ImageIndex of the deleted image. Usually a linear decrement is sufficient starting at the index of the Item after deleted Item (if ImageList and ListViewItems keep a 1:1 relation):
for (int i = lvItem.Index + 1; i < listView1.Items.Count; i++)
listView1.Items[i].ImageIndex--;
Maybe more important is to delete the items AFTER reindexing (after deleting a ListViewItem the ListView must repaint and indicies should be in the right order for this):
int iImageIndex = lvItem.ImageIndex;
int iIndex = lvItem.Index;
for (int i = iIndex + 1; i < listView1.Items.Count; i++) // correct the image indicies
listView1.Items[i].ImageIndex--;
lvItem.Remove(); // repaint
Image img = ImageList1.Images[iImageIndex];
ImageList1.Images.RemoveAt(iImageIndex);
img.Dispose();

The problem is that the ListView items probably remember the index of the image in the ImageList. If you remove an image from the ImageList, the ListView items will point to a wrong image.
Try to reference the images by key instead of by index.
This is a test I made
imageList1.Images.Add("img0", Properties.Resources.img0); // Use key as first argument.
imageList1.Images.Add("img1", Properties.Resources.img1);
imageList1.Images.Add("img2", Properties.Resources.img2);
imageList1.Images.Add("img3", Properties.Resources.img3);
imageList1.Images.Add("img4", Properties.Resources.img4);
imageList1.Images.Add("img5", Properties.Resources.img5);
for (int i = 0; i < 6; i++) {
var item = new ListViewItem(
"Image #" + i, // Text
"img" + i // <== Use key here, not index
);
listView1.Items.Add(item);
}
If I remove an entry with ...
listView1.Items[1].Remove();
imageList1.Images.RemoveAt(1);
... it works correctly.

Related

C# Delete all files in a listbox

I add some files to a listbox on a button:
if (Directory.Exists(DirString))
{
DirectoryInfo dirETC = new DirectoryInfo(DirString);
FileInfo[] Files = dirETC.GetFiles("*.txt");
foreach (FileInfo file in Files)
{
listBox1.Items.Add(file.FullName);
}
If user clicks the delete button, i would like to delete all files in the listbox like this:
try
{
listBox1.BeginUpdate();
for (int i = 0; i < listBox1.Items.Count; i++)
{
string filename = listBox1.Items[i].ToString();
if (File.Exists(filename))
{
File.Delete(filename);
listBox1.Items.Remove(filename);
}
}
listBox1.EndUpdate();
}
catch (Exception)
{
return;
}
The above code works, but its only deleting one file every time i click the delete all button. I think I need to add some kind of foreach loop here to iterate through the items but i'm not sure what is the best way to go about it.
Foreach statement won't allow you to modify a collection you are iterating through, while for will. You just have to be careful with indexes: in your solution after removing the first item (with index=0) the next one become first. Try this approach instead:
listBox1.BeginUpdate();
for (int i = listBox1.Items.Count - 1; i >=0; i--)
{
string filename = listBox1.Items[i].ToString();
if (File.Exists(filename))
{
try
{
File.Delete(filename);
listBox1.Items.Remove(filename);
}
catch (Exception)
{
}
}
}
listBox1.EndUpdate();
When you remove an item from an array that you are iterating over with the for..next statement, you should consider that every time you delete an element, the array count decrease of one element and every element in the array shifts at a lower index. So, when you delete the element at index 0, the previous element that were at index 1 shifts in position 0, but you have already iterated over the index zero, so you end skipping that element and trying to delete the element that was at index 2 before the removal of the item at index 0.
The solution is simple. Loop in reverse order (from highest element to lowest)
for (int i = listBox1.Items.Count-1; i >= 0 ; i--)
{
string filename = listBox1.Items[i].ToString();
if (File.Exists(filename))
{
File.Delete(filename);
listBox1.Items.Remove(filename);
}
}
Copy the contents of the listbox into a string array to prevent errors when remove the items from the listbox in looping.
// Copy the contents of the listbox into a string array
string[] filenameList = listBox1.Items.OfType<string>().ToArray();
try
{
for (int i = 0; i < filenameList.Length; i++)
{
string filename = filenameList[i];
if (File.Exists(filename))
{
File.Delete(filename);
listBox1.Items.Remove(filename);
}
}
}
catch (Exception)
{
}

Fill an array with a loop

I want to get a shorter code than now but I don't know how.
What I do now is like the code below.
arrPictureBox[0] = picChair0;
arrPictureBox[1] = picChair1;
arrPictureBox[2] = picChair2;
arrPictureBox[3] = picChair3;
arrPictureBox[4] = picChair4;
arrPictureBox[5] = picChair5;
arrPictureBox[6] = picChair6;
arrPictureBox[7] = picChair7;
arrPictureBox[8] = picChair8;
arrPictureBox[9] = picChair9;
arrPictureBox[10] = picChair10;
arrPictureBox[11] = picChair11;
(pic) is a picturebox.
But I want less code but I don't know if it possible to do this with a loop (for loop).
for (int i = 0 ; i < arrPictureBox.Length; i++)
{
arrPictureBox[i] = picChair + i;
}
If picChairN is a local variable then there's nothing you can do to simplify it as much as you'd like. The best you can do is
arrPictureBox = new [] { picChair0, picChair1, picChair2, picChair3,
picChair4, picChair5, picChair6, picChair7,
picChair8, picChair9, picChair10, picChair11};
If picChairN is a class member (e.g. a field created by the designer) then you could use reflection, but considering you already have the array method typed out I don't see much benefit.
Let's predict you're on WinForms and the pictureBoxes already exist, then you can use the following:
for (int i = 0; i < arrPictureBox.Length; i++)
{
arrPictureBox[i] = this.Controls["picChair" + i];
}
Which actually does this:
get the first Control (a PictureBox for example) with the given name
add the found control to the array of pictureboxes
EDIT:
It might be useful to check for non existing pictureBoxes:
for (int i = 0 ; i < arrPictureBox.Length; i++)
{
var pb = this.Controls["picChair" + i] as PictureBox;
if (pb != null)
{
arrPictureBox[i] = pb;
}
}
You can use al List like below.
List<string> arrPictureBox = new List<string>();
for (int i = 0; i < 20; i++)
{
arrPictureBox.Add("picChair" + i);
}
var result = arrPictureBox.ToArray();
Hope it helps.
If all the picture boxes are on the same form and are the ONLY picture boxes on the form, you can loop through them with something like the following:
int x = 0;
foreach(Control c in this.Controls)
{
if(c is PictureBox)
{
arrPictureBox[x++] = c
}
}

C# - Chart.Series[index].Points.Label not producing any label of datapoint

I have a chart which has 9 different series to it, only two of which can ever be enabled at the same time.
My user has asked that when the first datapoint is added to the chart, it is labelled 'Start', and this enables the button that will then allow the user to mark 'End'.
During the development of the solution, this wasn't a problem as I had control over which of the series were enabled at any one time. Now the facility has been added for the user to change this in the 'Options' menu (series name and enabled state written to 'Settings' file, to then be read in next time the program begins.
In order to try and do this, I created a List<> 'enabledSeries' in my updateChart function that finds the enabled of the chart series and adds them to the list. This has been done fine, and adding datapoints to the chart works using this method. For some reason, however, Labels now do not appear at the start of each series.
The code for updateChart() and subfunctions is shown below:
public void updateChart(int minutesElapsed)
{
//int latestReading = 0; //local variable to hold va1ue from txtBP.Text
chartVitals.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;
chartVitals.Text = "Vitals Visual";
//Create correct coordinate using reading and time taken
//add newest (last) element in each list to respective chart series, along with value from timer
chartVitals.Series["Heart Rate"].Points.AddXY(minutesElapsed, HRlist.Last());
chartVitals.Series["Ventilation Rate"].Points.AddXY(minutesElapsed, VRlist.Last());
chartVitals.Series["Blood Pressure"].Points.AddXY(minutesElapsed, BPlist.Last());
chartVitals.Series["o2 Saturation"].Points.AddXY(minutesElapsed, BOlist.Last());
chartVitals.Series["ET Co2"].Points.AddXY(minutesElapsed, ETCo2list.Last());
chartVitals.Series["Vaporiser Setting"].Points.AddXY(minutesElapsed, VSlist.Last());
chartVitals.Series["FI Agent"].Points.AddXY(minutesElapsed, FIAlist.Last());
chartVitals.Series["ET Agent"].Points.AddXY(minutesElapsed, ETAlist.Last());
chartVitals.Series["Fresh Gas Flow"].Points.AddXY(minutesElapsed, FGFlist.Last());
//stores all enabled series (MAX 2)
List<string> enabledSeries = new List<string>();
//identify and isolate the enabled series in the chart
identifyEnabled(enabledSeries);
}
public void identifyEnabled(List<string> enabledSeries)
{
//takes name of chart series at current index
string seriesName = "";
//access all items in list
for (int index = 0; index < chartVitals.Series.Count; ++index)
{
//assign name to variable
seriesName = chartVitals.Series[index].Name;
//series with this name is enabled
if (chartVitals.Series[seriesName].Enabled)
{
//add name to list
enabledSeries.Add(seriesName);
}
}
formatEnabled(seriesName, enabledSeries);
}
public void formatEnabled(string seriesName, List<string> enabledSeries)
{
//color series in by index (0 - blue, 1 - red)
string blueSeries = enabledSeries.First();
string redSeries = enabledSeries[enabledSeries.IndexOf(blueSeries) + 1];
//access all elements in enabledSeries
for (int enabledIndex = 0; enabledIndex < enabledSeries.Count; ++enabledIndex)
{
//access all series in chartVitals
for (int seriesIndex = 0; seriesIndex < chartVitals.Series.Count; ++seriesIndex)
{
//when there is item in series
if (chartVitals.Series[seriesName].Points.Count > 0)
{
string start = "[Start]";
//set series type as line
chartVitals.Series[seriesName].ChartType = SeriesChartType.Line;
//apply label to first point of series
chartVitals.Series[seriesName].Points.First().Label = start;
//enable button to mark end
btnOpEnd.Enabled = true;
}
}
}
//apply colours to series
chartVitals.Series[blueSeries].Color = Color.Blue;
chartVitals.Series[redSeries].Color = Color.Red;
}
The code is a bit intricate, but it's commented and should all be there. If anyone can point out what might be causing the problem or a simpler way of doing things, I would really appreciate it!
Thanks,
Mark
Is this what you expected to see?
EDIT: I changed formatEnabled to use seriesIndex instead of seriesName, like below:
public void formatEnabled(string seriesName, List<string> enabledSeries)
{
//color series in by index (0 - blue, 1 - red)
string blueSeries = enabledSeries.First();
string redSeries = enabledSeries[enabledSeries.IndexOf(blueSeries) + 1];
//access all elements in enabledSeries
for (int enabledIndex = 0; enabledIndex < enabledSeries.Count; ++enabledIndex)
{
//access all series in chartVitals
for (int seriesIndex = 0; seriesIndex < chartVitals.Series.Count; ++seriesIndex)
{
//when there is item in series
if (chartVitals.Series[seriesIndex].Points.Count > 0)
{
string start = "[Start]";
//set series type as line
chartVitals.Series[seriesIndex].ChartType = SeriesChartType.Line;
//apply label to first point of series
chartVitals.Series[seriesIndex].Points.First().Label = start;
//enable button to mark end
//btnOpEnd.Enabled = true;
}
}
}
//apply colours to series
chartVitals.Series[blueSeries].Color = Color.Blue;
chartVitals.Series[redSeries].Color = Color.Red;
}

ASP.NET gives multiple control ID error for one control

I want to add a control to a placeholder dynamically, like this:
int fileCount = Convert.ToInt32(lblCount.Text);
for (int i = 0; i<fileCount ; i++)
{
FileUpload fu = new FileUpload();
if(PlaceHolder1.HasControls())
PlaceHolder1.Controls.AddAt(i,fu);
else
PlaceHolder1.Controls.Add(fu);
PlaceHolder1.Controls[i].ID = "123456abcdef" + i;
}
But I get the error
Multiple controls with the same ID '123456abcdef0' were found. FindControl requires that controls have unique IDs.
Why? Only one control should get that ID on each iteration of the loop.
EDIT: Should mention that I haven't actually been able to test the loop, I get the error even when fileCount is 1.
SOLUTION: I called this function from a "foreach" loop in page load when I thought it was outside of it. Still, having the clear() method in mind will remove the necessity of the addat part of the function.
Just do a clear before you start adding:
PlaceHolder1.Controls.Clear();
And your add statements can be simplified as follows:
FileUpload fu = new FileUpload();
fu.Id = "123456abcdef" + i;
PlaceHolder1.Controls.Add(fu);
As per my knowledge, you can try something like below.
int fileCount = Convert.ToInt32(lblCount.Text);
for (int i = 0; i<fileCount ; i++)
{
FileUpload fu = new FileUpload();
fu.ID = "123456abcdef" + i;
PlaceHolder1.Controls.Add(fu);
}
Hope this Helps!!
Change to this:
int fileCount = Convert.ToInt32(lblCount.Text);
for (int i = 0; i<fileCount ; i++)
{
FileUpload fu = new FileUpload();
fu.ID = string.Format("fu_{0}", i);
PlaceHolder1.Controls.Add(fu);
}

Concatenate strings to make Picturebox name

i have lot of pictureboxes named this way: PBr1_1, PBr1_2, ... PBr1_9
I'd like to make loop
for (int i = 0; i < 10; i++)
{
if (Textbox.Text[i].ToString() == "1"){ "PBr1_"+"i".Tag = "cb.png";}
}
so for i=0 => PBr1_0, i=10 => Pbr1_10.
Example i have value in textbox: 0001011101 - then if value in textbox is "1" then i'd like to change picturebox tag.
How to automate this process, using for example loop "for"?
I suppose your controls are on a WinForm (this) and the ones with that name are all pictureboxes.
If so, that's the way ----
for (int i = 0; i < 10; i++)
{
if (Textbox.Text[i].ToString() == "1")
{
Control[] c = this.Controls.Find("PBr1_" + i.ToString(), true);
if(c != null && c.Length > 0) c[0].Tag = "cb.png";
}
}
You can put the picture boxes in to a List<PicutreBox> and iterate over the list.
var pictures = new List<PictureBox>();
pictures.Add(pic1);
pictures.Add(pic2);
//...
for (int i = 0; i < 10; i++)
{
if (Textbox.Text[i].ToString() == "1")
pictures[i].Tag = "cb.png";
}
Dynamic variable names as in your example are not supported.
Create an array (or list) that contain the picture boxes and use those within the for loop.
You can also use reflection but in my opinion it is best not to use that in this case.
If you are using WinForm you can use Control.Find method to locate a control by name
Once you have got the control you can easily change any property

Categories