method executes.
private void RemoveFiles()
{
// Remove the first selected item by index until none are selected.
while (lstFiles.SelectedIndices.Count > 0)
{
lstFiles.Items.RemoveAt(lstFiles.SelectedIndices[0]);
}
// Remove the first selected item as an object until none are selected.
//while (lstFiles.SelectedItems.Count > 0)
//{
// lstFiles.Items.Remove(lstFiles.SelectedItems[0]);
//}
// Remove the (first) selected item until none are selected.
//while (lstFiles.SelectedItem != null)
//{
// lstFiles.Items.Remove(lstFiles.SelectedItem);
//}
// Remove the selected items by index in reverse order.
//for (int i = lstFiles.SelectedIndices.Count - 1; i >= 0; i--)
//{
// lstFiles.Items.RemoveAt(i);
//}
EnableMenuItems();
}
As long as the list has selected files, the program removes the first of them. When it removes the first selected file file, the indices of any files that come after it in the list decrease by 1. The SelectedIndices list updates so it holds the new indices of the remaining items, and the process can continue. The loop continues removing the first selected item from the list until there are no more selected items.
Another approach that you might try would be to make a variable, say i, loop through the indices of the selected items and remove them from the list. Unfortunately when you do that, the indices update and the variable i increments so it skips entries.
For example, the first time through the loop, the code removes the first selected index. Now the item that was second becomes the first (with index 0), but i increments to 1 so it is past the new first item.
You also cannot use a foreach loop to iterate through the SelectedItems or SelectedIndices list and remove the items in those lists. Whenever you remove an item, that modifies the lists and that messes up the foreach loop.
The code shows a few other strategies that work commented out. If you study them, you should be able to figure out how they work.
Zipping Files
The easiest way to zip files is to put them in the same directory and then use the ZipFile class's CreateFromDirectory method to zip the directory. I didn't take that approach because I wanted to show how to add specific files to a zip file.
When you add files to a zip archive, the file names within the archive are relative. That way when you unzip a directory hierarchy, the files go where they belong.
However, in this example you select individual files, so there isn't a hierarchy. That's fine unless you happen to select two files that have the same name because the zip archive cannot have two entries with the same name.
To solve that problem, the example appends a number to files with the same names. For example, if you select three files named myfile.txt, then inside the zip archive they are named myfile.txt, myfile(1).txt, and myfile(2).txt.
Without further ado, here's the code. When you select the File menu's Save Zip File command, its event handler calls the following SaveZipFile method.
private void SaveZipFile()
{
// Get the Zip file name. If the user cancels, do nothing.
if (sfdZipFile.ShowDialog() != DialogResult.OK) return;
// If the file exists, delete it.
// (The dialog made the user agree to that.)
string zip_file_name = sfdZipFile.FileName;
if (File.Exists(zip_file_name)) File.Delete(zip_file_name);
Cursor = Cursors.WaitCursor;
// Create the Zip archive for update.
using (ZipArchive archive =
ZipFile.Open(zip_file_name, ZipArchiveMode.Update))
{
// Add the files to the archive.
for (int i = 0; i < lstFiles.Items.Count; i++)
{
// Select the file in the list so the user can see progress.
lstFiles.SelectedIndex = i;
lstFiles.Refresh();
// Get the file's name without the path.
string filename = lstFiles.Items[i].ToString();
FileInfo file_info = new FileInfo(filename);
// Make an entry name for the file.
string entry_name = file_info.Name;
for (int version = 1; version < 10000; version++)
{
ZipArchiveEntry old_entry = archive.GetEntry(entry_name);
if (old_entry == null) break;
entry_name =
Path.GetFileNameWithoutExtension(file_info.Name) +
$"({version})" + file_info.Extension;
}
// Add the file to this entry.
archive.CreateEntryFromFile(filename, entry_name);
}
}
lstFiles.SelectedIndex = -1;
Cursor = Cursors.Default;
MessageBox.Show("Done");
}
This code displays a SaveFileDialog to let you select the soon-to-be-created zip file. If you click Cancel, the method returns.
If you pick a file and click Save, the code gets the name of the file and uses File.Exists to see if the file already exists. If it does, the code uses File.Delete to delete it. (The SaveFileDialog asked if you wanted to replace the existing file.)
Next the code uses the ZipFile class's Open method to create a new zip archive (which is stored in the file). The program uses the Update parameter so it can look at the file as it is constructed. Notice that the code places the archive in a using block so it is automatically disposed when the block finishes.
The program then loops through the files in the list box and gets each file's name. It then enters a loop that runs until the code finds a name for this file that is not already in the archive.
Inside the loop, the program uses the archive's GetEntry method to see if the file's name is already in the archive. If the name is not already present, the code breaks out of the loop and uses the current name.
If the name is already in use, the code composes a new file name consisting of the original file's name without the path or extension, followed by the loop number inside parentheses, followed by the original file's extension. The loop continues until the program finds a file name that is not in use.
Notice how the code uses string interpolation $"({version})" to compose the file's number. String interpolation is one of the best features added to C# in several versions. Unfortunately it's a fairly recent development, so you can't use it with older versions of C# (which load and run faster than the newer versions).
Having found a file name that is not already in use, the code uses the archive's CreateEntryFromFile method to add the file to the archive using the recently composed name.
The program then continues to loop through the files in the list box, adding them to the archive.
Summary
This example uses the ZipFile class to add a collection of files to a zip archive. Because of the way it lets you select the files from arbitrary locations, it flattens the files' directory hierarchy and places them all at the same level in the zip file.
Download the example to see additional details and to experiment with the program.
Download the example to experiment with it and to see additional details.