How to fix ListView.LargeImageList showing images twice - c#

I am working on a piece of software, which compares memes and helps users organize memes on their computer. As a part of this I am using Windows.Forms to build a UI. This UI lets the user add folders to be checked for images, which can be compared to a set of known meme templates.
My issue arises when I try to show the user the found images. To do this I am using a ListView and the property LargeImageList to contain a tuple of the image and the name of the image file.
Here is the piece of code in question:
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
var ic = new ImageCollection();
var fbd = new FolderBrowserDialog();
fbd.Description = "Select meme folder or image.";
if (fbd.ShowDialog() == DialogResult.OK)
{
string[] files = Directory.GetFiles(fbd.SelectedPath);
foreach (var file in files)
{
if (!ic.CheckIfImage(file)) continue;
imageList1.Images.Add(Image.FromFile(file));
}
foreach (var file in files)
{
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
}
}
}
This is an example of what the user sees when they first load in a folder. When the user tries to load in another folder this happens. It shows the images from the first folder, with the names of the image files from the second folder.
Does anyone know a fix for this issue? I have tried a variety of options in order to get around the issue. All from trying to clear the ImageList used to contain the images, to trying my hand at controlling when the ListView updates. None of this has worked. I have also tried googling the issue, but with no luck of finding a fix.
Thank you in advance.

If you want to show the content of a single folder at the time, then dispose of the objects in your ImageList.
If you instead want to show the content of more than one folder, you need to specify the new index of the image added. You're instead adding a new Item in the ListView using the same index reference:
int i = 0;
//(...)
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
The indexer (i) always starts from 0, thus the ListView Item will use the images in your Imagelist starting from the Image at Index[0] each time. The new images won't ever be shown.
You can use the ImageList.Images.Count value, representing the number of Images already added to the ImageList, as base and increment the indexer starting from this value:
private void button1_Click(object sender, EventArgs e)
{
int i = imageList1.Images.Count;
var ic = new ImageCollection();
var fbd = new FolderBrowserDialog();
fbd.Description = "Select meme folder or image.";
if (fbd.ShowDialog() == DialogResult.OK)
{
foreach (var file in Directory.GetFiles(fbd.SelectedPath))
{
if (!ic.CheckIfImage(file)) continue;
imageList1.Images.Add(new Bitmap(file, true));
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
}
}
}
If you allow to remove an Image from the ListView, you should also remove it from the ImageList: this implies that you need to re-index all the ListView Items starting from the Item that follows the one removed.
Remember to dispose of the Images you remove from the ImageList.

Related

how to go next line in ListBox?

hey guys i wanna to load a File path in my listBox.
everything is ok
i just have a problem that is when i Close the app and then open it again
loaded files are in the one lane and it recognize them as one item in listBox
i tried to use "\n" , "\r" none of these works...
so what u guys suggest?
(i save user changes in App Setting to read them later)
private void Form1_Load(object sender, EventArgs e)
{
if (Properties.Settings.Default.FileList != string.Empty)
{
fileListBox.Items.Add(Properties.Settings.Default.FileList);
}
UnlockForm f2 = new UnlockForm();
if (Properties.Settings.Default.PasswordCheck == true)
f2.ShowDialog();
else
return;
}
private void button1_Click_1(object sender, EventArgs e)
{
op = new OpenFileDialog();
op.Title = "Select your stuff";
op.Filter = "All files (*.*)|*.*";
if (op.ShowDialog() == DialogResult.OK)
{
fileName = op.FileName;
fileListBox.Items.Add(fileName);
}
Properties.Settings.Default.FileList += fileName+"\n";
Properties.Settings.Default.Save();
}
When creating the property in settings designer:
Set the name to whatever you want, for example Files
Set the type as System.Collections.Specialized.StringCollection
Set the scope as User
If you want to have some default values, use ... in Value cell to edit default value.
Then you can easily set it as DataSource of the ListBox.
listBox1.DataSource = Properties.Settings.Default.Files;
Also to add some values:
Properties.Settings.Default.Files.Add("something");
Properties.Settings.Default.Save();
If you added something to the Files, if you want the ListBox shows the changes, set DataSource to null and then to Files again.
It looks like you have defined your FileList as String in our App Settings. There are two ways you can approach this.
a) Using FileList as Collection.
You can change the Type of FileList to StringCollection in your App Settings. Then, you can add items to your list as follows
fileListBox.Items.AddRange(Properties.Settings.Default.FileList.Cast<string>().ToArray());
b) Using FileList as String.
If you really want to retain Properties.Settings.Default.FileList as string, you would need to split it on the run using your delimiter character (let's say ';')
fileListBox.Items.AddRange(Properties.Settings.Default.FileList.Split(new[] { ';' },StringSplitOptions.RemoveEmptyEntries));
In your case, a collection might be a better approach unless you have specific reasons outside the scope of OP to use string.

Showing strings in ListView columns dynamically - not working

My goal is the following:
The user enters the stuff as shown in this picture. The 3 items (source file, destination directory and if file exists) are saved in a string array. User clicks OK.
After clicking OK the previous window is closed and the user is taken to the main form shown in this picture. The previously mentioned source file and the destination directory are shown in the table.
I am using the following code to show the entered file & directory in the table:
private void okButton_Clicked(object sender, EventArgs e)
{
//saving user's input
userInput = new string[3];
userInput[0] = sourceFileTextBox.Text;
userInput[1] = destinationDirComboBox.SelectedItem.ToString();
userInput[2] = ifFileExistsComboBox.SelectedItem.ToString();
//creating a new ListView object - the object is derived from the ListView Class
and has absolutely nothing in the constructor or anywhere
Classes.ListViewDerivative lvd = new Classes.ListViewDerivative();
ListViewItem item1 = new ListViewItem(userInput[0]);
item1.SubItems.Add(userInput[1]);
lvd.Items.AddRange(new ListViewItem[] { item1 });
this.DialogResult = DialogResult.OK;
this.Hide();
}
It doesn't work. The table is empty no matter where I put this code - I've tried to put this adapted code in the ListViewDerivative constructor, a function in the ListViewDerivative Class and in the editFileEntry(first picture) class. The right text is saved in the array but is not shown in the table. Please help!
the lvd variable is not used anywere. You have to add the 'lvd' -control to your mainview. You can use the Designer for this and in the Dialog Result of your "FilePickerDialog" you can assign the results to your datagrid.
some pseudo code for your MainView
void Config_Clicked()
{
ConfigDlg dlg = new ConfigDlg();
if(dlg.ShowDialog() == OK)
{
this.myListView1.Items.Add(dlg.userInput[0]);
}
}

Strip leading characters from a directory path in a listbox in C#

So i am attempting to teach myself C#, I have a program that I originally wrote in batch and am attempting to recreate in C# using WPF. I have a button that allows a user to set a directory, that directory selected is then displayed in a text box above a listbox which adds every subfolder, only first level, to the listbox. Now all this works fine but it writes out the entire directory path in the listbox. I have been trying to figure out how to strip the leading directory path off the list box entries for over an hour to no avail. Here is what I have so far:
private void btn_SetDirectory_Click(object sender, RoutedEventArgs e)
{
//Create a folder browser dialog and set the selected path to "steamPath"
var steamPath = new FolderBrowserDialog();
DialogResult result = steamPath.ShowDialog();
//Update the text box to reflect the selected folder path
txt_SteamDirectory.Text = steamPath.SelectedPath;
//Clear and update the list box after choosing a folder
lb_FromFolder.Items.Clear();
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f);
}
}
Now I tried changing the last line to this, and it did not work it just crashed the program:
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f.Substring(f.LastIndexOf("'\'")));
}
I am fairly certain that the LastIndexOf route is probably the right one but I am at a dead end. I apologize if this is a dumb question but this is my first attempt at using C#. Thanks in advance.
This can solve your issue
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
// string[] strArr = f.Split('\\');
lb_FromFolder.Items.Add(f.Split('\\')[f.Split('\\').Length-1]);
}
You can use this code:
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f.Remove(0,folderName.Length));
}

Replacing listBox items during repopulation

I'm building a contact manager program and I have a button that when pressed, updates a listBox of names. Unfortunately, I'm expecting my user to not always be putting the names in before closing the program. I have decided to make new contacts in the list appear as random numbers before a name is entered, so some of my items will be numbers and some will be names. I can't seem to get the names to show up, though. I've been storing the names as strings inside of text files contained inside folders of unique random numbers. What I want is to be able to load all the folder names into the list, then check to see if a name is associated with the item, and if it is, replace the number with the respective name. The following code is as far as I have gotten before I got stumped.
private void button5_Click(object sender, EventArgs e)
{
//populate the list of people
listBox1.Items.Clear();
string[] dirs = Directory.GetDirectories(#"C:\PersonalManager\");
foreach (string dir in dirs)
{
//replace numbered items with names
for (int i = 0; i < listBox1.Items.Count; i++)
{
String Text = Convert.ToString(listBox1.Items[i]);
Text = Text.Replace(File.ReadAllText(#"C:\PersonalManager\"+listBox1.Items[i]+#"\first.txt")); //first.txt is the file containing the name
listBox1.Items[i] = Text;
}
listBox1.Items.Add(Path.GetFileName(dir));
}
}
I'm also pretty sure there is an overload bug in there somewhere, as Visual Studio refuses to compile it. Maybe I should go about this in a different way? What would any of you suggest? I have searched all through Google, and Bing but have yet to figure out what I'm doing wrong. Sorry if my code is a mess, it's my first time.
P.S. I'm a beginner, so I can't wrap my head around too much code without comments every now and then.
I'm pretty sure that this line is breaking your code:
Text = Text.Replace(File.ReadAllText(#"C:\PersonalManager\"+listBox1.Items[i]+#"\first.txt"));
The string.Replace does not have a overload with just one parameter. It has always two.
Just to give you a starting point:
private void button5_Click(object sender, EventArgs e)
{
//populate the list of people
listBox1.Items.Clear();
string[] dirs = Directory.GetDirectories(#"C:\PersonalManager\");
foreach (string dir in dirs)
{
var filePathToRead = Path.Combine(dir, "first.txt");
var allTextOfTheFile = File.ReadAllText(filePathToRead);
// now you can work with the content of the file
listBox1.Items.Add(Path.GetFileName(dir));
}
}

Filling Listview & Imagelist selected item confusion c#

I new to programming and C# and seemed to have worked myself into a bit muddle with the above.
What i am trying to do is create a front end for the living room media pc nothing to fancy to start with as i understand this is a mamoth task for a total noobie like me.
Ive flapped about and am totally fine with launching external exe's,storing/loading resouces ect.. and been very happy with my results for my 2 week surfing.
So im starting off my project by just launching an emulator to start with and what i would like to do is scan a folder for zip files and image files and if it finds matching image and zip files it displays an image in a list view for each zip found.
So i populate my listboxes like this and get my 2 listboxes showing the stuff i want to see.
PopulateListBox(listBox1, "\\SomePath\\", "*.zip");
PopulateListBox(listBox2, "\\Images\\", "*.jpg");
private void PopulateListBox(ListBox lsb, string Folder, string FileType)
{
DirectoryInfo dinfo = new DirectoryInfo(Folder);
FileInfo[] Files = dinfo.GetFiles(FileType);
foreach (FileInfo file in Files)
{
lsb.Items.Add(file.Name);
}
}
So i now have my 2 listboxes and can see i have game1.zip and game1.jpg, great now i can populate my listview with the game1 image and launch the emulator he say's simple.
This is how i am currently populating the listview.
PopulateListView();
private void PopulateListView()
{
if (listBox1.Items.Contains("game1.zip"))
{
if (File.Exists("\\Images\\game1.jpg"))
{
imageList1.Images.Add(Image.FromFile("\\Images\\game1.jpg"));
listView1.Items.Add("", 0);
}
}
if (listBox1.Items.Contains("game2.zip"))
{
if (File.Exists("\\Images\\game2.jpg"))
{
imageList1.Images.Add(Image.FromFile("\\Images\\game2.jpg"));
listView1.Items.Add("", 1);
}
}
}
This is how i am currently launching and it works ok.
// launch item
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (listView1.Items[0].Selected == true)
{
string rom = "\\" + listBox1.Items[0].ToString();
// Launch code in here
}
if (listView1.Items[1].Selected == true)
{
string rom = "\\" + listBox1.Items[1].ToString();
// Launch code in here
}
}
So what the problem you may ask ?
Instead of keep typing in all that info for each item i want to use some kind of statment if possible and i dont know what to search for which does not help.
The image name will allways match the zip name just need to loose the file extensions so to populate the listview somthing like this.
if (listbox1 item = listbox2 item)
{
add to imagelist and listview automaticly with same index
}
Then i want to be able to launch just using somthing like this.
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
string rom = "\\" + listview.selected.item.tostring;
// Launch code in here
}
Hope im making sense im at my witts end.
Regards
Derek
If i understand correctly, you want to read, match and show programmatically all images and zips that are the same and when the user clicks a list view row to start the rom. This can be done as follows:
read all images - there is no need to check if the file exists, because if it does not, then GetFiles will not find it!
read all roms - same as above!
take all names that have both image and zip file (that are the same)
fill image list and list view at the same time
bind list view item with the correct image
store rom location
handle mouse click on row and fetch rom location
do something with rom file
The code:
public ListForm()
{
InitializeComponent();
// path to images and roms
const string location = #"d:\temp\roms\";
// bind image list with list view
listViewControl.SmallImageList = imageList;
// get all images without extension
var images = System.IO.Directory.GetFiles(location, "*.gif").Select(f => System.IO.Path.GetFileNameWithoutExtension(f)).ToList();
// get all roms without extension
var zips = System.IO.Directory.GetFiles(location, "*.zip").Select(f => System.IO.Path.GetFileNameWithoutExtension(f)).ToList();
// find all entries (images and zips) that have the same name
var matching = images.Intersect(zips);
var imageIndex = 0;
// fill image list and list view at the same time and store rom location
foreach (var match in matching)
{
// path to file without extension
var file = System.IO.Path.Combine(location, match);
// add image to image list
imageList.Images.Add(match, Bitmap.FromFile(string.Format("{0}.gif", file)));
// create list view item
var lvi = new ListViewItem(match);
// and set list view item image
lvi.ImageIndex = imageIndex;
// store rom location
lvi.Tag = string.Format("{0}.zip", file);
imageIndex++;
// and show
listViewControl.Items.Add(lvi);
}
}
private void listViewControl_MouseClick(object sender, MouseEventArgs e)
{
// when user clicks an item, fetch the rom location and go
var item = listViewControl.GetItemAt(e.X, e.Y);
if (item != null)
{
var pathToRom = item.Tag as string;
// do something with rom ...
}
}
There is no need to handle the image list and the list view separately. The output looks like:
you can Populate the images using the names from the listView:
private void PopulateListView()
{
foreach (string curr in listBox1.Items)
{
string changed = "\\Images\\" + curr.Replace(".zip", ".jpg");
if (File.Exists(changed))
{
imageList1.Images.Add(Image.FromFile(changed));
listView1.Items.Add("", 1);
}
}
}
and use the selected item in the MouseDoubleClick event
// launch item
private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
string selected = (string) listBox1.SelectedItem;
string rom = "\\" + selected;
// Launch code in here
}
If I'm understanding your problem correctly, you can switch out your tower of if statements with this:
var item = listView1.SelectedItem
string rom = "\\" + item.ToString()
// Launch code in here
And to populate it, you can change this:
if (listBox1.Items.Contains("game2.zip"))
{
if (File.Exists("\\Images\\game2.jpg"))
{
imageList1.Images.Add(Image.FromFile("\\Images\\game2.jpg"));
listView1.Items.Add("", 1);
}
}
To this:
List<string> files = new List<string> { "game1.zip", "game2.zip" };
var index = 0;
foreach (var file in files)
{
if (File.Exists("\\Images\\" + file))
{
imageList1.Images.Add(Image.FromFile("\\Images\\" + file.Replace(".zip", ".jpg")));
listView1.Items.Add("", index);
index++;
}
}

Categories