Filling Listview & Imagelist selected item confusion c# - 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++;
}
}

Related

How to fix ListView.LargeImageList showing images twice

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.

Force File Browser to Sort files by Name

I have a button that opens a file browser and select multiple files, then adds them to a ListView.
How can I force the Browser Dialog Box to always sort the files by name before being added to the ListView?
Sometimes windows defaults to Date Modified or another sorting method besides Name.
Note: I have full file paths in a List, and just file names in the ListView.
private void btnInput_Click(object sender, RoutedEventArgs e)
{
// Open Select File Window
Microsoft.Win32.OpenFileDialog selectFiles = new Microsoft.Win32.OpenFileDialog();
selectFiles.Multiselect = true;
// Process Dialog Box
Nullable<bool> result = selectFiles.ShowDialog();
if (result == true)
{
// Add Path+Filename to List
foreach (String file in selectFiles.FileNames)
{
lstFilesPaths.Add(file);
}
// Add List Filename to ListView
lsvFiles.Items.Clear();
foreach (String name in fileList)
{
lsvFileNames.Items.Add(Path.GetFileName(name));
}
}
}
The filebrowser itself won't sort its results by filename, you will need to do that before using them.
Given lstFilesPaths is a List of strings you're saving the selected file paths to for use elsewhere, try this to sort the list by file name adding:
foreach (var name in lstFilesPaths.Select(f => Path.GetFileName(f)).OrderBy(s => s))
{
lsvFileNames.Items.Add(name);
}
Or, if you'd like both your list of file paths and the list view of file names sorted, try this:
// Add Path+Filename to List
lstFilesPaths.AddRange(selectFiles.FileNames.OrderBy(f => Path.GetFileName(f)));
// Add List Filename to ListView
lsvFiles.Items.Clear();
foreach (var name in lstFilesPaths.Select(f => Path.GetFileName(f)))
{
lsvFileNames.Items.Add(name);
}
try changing to:
lstFilePaths.AddRange(selectFiles.FileNames.OrderBy(x => x))
lsvFileNames.Items.Clear();
lstFilePaths.ForEach(x => lsvFileNames.Items.Add(Path.GetFileName(x)));

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));
}

How to simulate a file explorer using ListView in c#?

I'm pretty new to C# and Windows Form programming. I'd like to create a simple Windows Form which has a ListView initially populated with some items. Then, if I double click on an item I'd like to show some other sub-items. Apparently there will be a '...' item at the top which take me to the parent item. Can someone help me writing this simple windows form?
p.s. I'm not very familiar with C# Windows Form application architecture (Form.Designer.cs, Form1, Program.cs, etc)
If you want to use the ListView you could populate it when you double Click a Directory.
What you need is
A double Click event
DirectoryInfo
FileInfo
When programming a Windows Forms application you should consider dividing your business logic from your view logic keywords here are Model view controller or Model View Presenter.
Lets begin
I would create a DirectoryController class which handles some of your logic.
Your Forms class can handle the view logic then for example the double click event.
You could create a method which generates an ImageList so that you can use it in your ListView to have the correct Image for your File or Directory
private static ImageList GetFolderImageList()
{
Image folderImg = Image.FromFile(#"C:\Windows\WinSxS\amd64_microsoft-windows-printing-fdprint_31bf3856ad364e35_6.3.9600.17415_none_493b0b9e590044a1\folder.ico");
ImageList imgList = new ImageList();
imgList.Images.Add(folderImg);
return imgList;
}
I used an Image from the Windows directory just so that this will initially work. But you can use an Image you want to show when having a directory in your ListView. By the way this will only demonstrate how to handle directories and not files, but from this point you should be able to handle files by yourself.
After you created the ImageList you can create Initialize the ListView.
private void InitializeListView(ImageList imgList)
{
lvExplorer.DoubleClick += new System.EventHandler(this.lvExplorer_DoubleClick);
lvExplorer.SmallImageList = imgList;
lvExplorer.LargeImageList = imgList;
}
My ListView is named lvExplorer and I added it already to the Windows Form in the designer. I used the Dockstyle to fill the whole form.
With this two functions you should be able to Initialize the ListView.
Now you need to populate the ListView.
private void ShowDirectoriesInListView(string path)
{
DirectoryInfo info = new DirectoryInfo(path);
DirectoryInfo parent = info.Parent;
if (parent != null)
{
lvExplorer.Items.Add(new System.Windows.Forms.ListViewItem("...", 0));
}
foreach (DirectoryInfo dInfo in info.GetDirectories())
{
lvExplorer.Items.Add(new System.Windows.Forms.ListViewItem(dInfo.Name, 0));
}
}
With the ShowDirectoriesInListView method you can add your Directories to the ListView. The path parameter is the directory you want to show.
If the directory has a parent we add the "..." directory. A root directory normally has no parent so we won't add it then.
Now lets get to the point how we get the directory that we passed into the ShowDirectoriesInListView method.
As mentioned before we want to split view from business logic
So I created a controller class
class DirectoryController
{
private string _currentDirectory;
public DirectoryController(string beginDirectory)
{
_currentDirectory = beginDirectory;
}
public string AddDirectoryAndGetPath(string path)
{
if (path.Equals("..."))
{
int lastIndex = _currentDirectory.Length;
if (_currentDirectory.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
lastIndex = _currentDirectory.LastIndexOf(Path.DirectorySeparatorChar);
_currentDirectory = _currentDirectory.Remove(lastIndex);
}
lastIndex = _currentDirectory.LastIndexOf(Path.DirectorySeparatorChar) + 1;
_currentDirectory = _currentDirectory.Remove(lastIndex);
}
else
{
_currentDirectory = string.Format("{0}{1}{2}", _currentDirectory, path, Path.DirectorySeparatorChar);
}
return _currentDirectory;
}
}
The most important function of this class is the AddDirectoryAndGetPath method.
If the method gets a string with the contents "..." we remove the last directory name from our current directory otherwise we will add it.
Now coming back to the double click event.
You did set up a Event handler in your InitializeListView method
The method which is called should have the same name as the method you passed as argument into the constructor of the EventHandler.
private void lvExplorer_DoubleClick(object sender, EventArgs e)
{
string path = lvExplorer.SelectedItems[0].Text;
OpenDirectory(path);
}
When an item is double clicked the method is called and the SelectedItems list should be filled with one Item. I should mention here that it is possible that you have more than one item or none in the selectedItems array so please add some error handling into this method or set the MultiSelect property of the ListView to false.
The text property of the selected Item is filled with the directory name of the Item you clicked because we used the directory name in the ShowDirectoriesInListView method.
Afterwards the OpenDirectory method is called which clears the ListView and adds all the directories of the selected directory to it.
private void OpenDirectory(string path)
{
try
{
lvExplorer.Items.Clear();
string newPath = _controller.AddDirectoryAndGetPath(path);
ShowDirectoriesInListView(newPath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
In the end you will have a Windows forms class like this
public partial class Form1 : Form
{
private DirectoryController _controller;
public Form1()
{
InitializeComponent();
ImageList imgList = GetFolderImageList();
InitializeListView(imgList);
_controller = new DirectoryController("C:");
OpenDirectory("");
}
private void InitializeListView(ImageList imgList)
{
lvExplorer.DoubleClick += new System.EventHandler(this.lvExplorer_DoubleClick);
lvExplorer.SmallImageList = imgList;
lvExplorer.LargeImageList = imgList;
}
private static ImageList GetFolderImageList()
{
Image folderImg = Image.FromFile(#"C:\Windows\WinSxS\amd64_microsoft-windows-printing-fdprint_31bf3856ad364e35_6.3.9600.17415_none_493b0b9e590044a1\folder.ico");
ImageList imgList = new ImageList();
imgList.Images.Add(folderImg);
return imgList;
}
private void lvExplorer_DoubleClick(object sender, EventArgs e)
{
string path = lvExplorer.SelectedItems[0].Text;
OpenDirectory(path);
}
private void OpenDirectory(string path)
{
try
{
lvExplorer.Items.Clear();
string newPath = _controller.AddDirectoryAndGetPath(path);
ShowDirectoriesInListView(newPath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void ShowDirectoriesInListView(string path)
{
DirectoryInfo info = new DirectoryInfo(path);
DirectoryInfo parent = info.Parent;
if (parent != null)
{
lvExplorer.Items.Add(new System.Windows.Forms.ListViewItem("...", 0));
}
foreach (DirectoryInfo dInfo in info.GetDirectories())
{
lvExplorer.Items.Add(new System.Windows.Forms.ListViewItem(dInfo.Name, 0));
}
}
}
From here on you should have the knowledge how to use the ListView and add your Files.
SubItems
If you want a detail view you could alter your InitializeListView method
private void InitializeListView(ImageList imgList)
{
lvExplorer.DoubleClick += new System.EventHandler(this.lvExplorer_DoubleClick);
lvExplorer.SmallImageList = imgList;
lvExplorer.LargeImageList = imgList;
CreateHeaders();
lvExplorer.View = View.Details;
}
private void CreateHeaders()
{
ColumnHeader header;
header = new ColumnHeader();
header.Text = "DirectoryName";
lvExplorer.Columns.Add(header);
header = new ColumnHeader();
header.Text = "LastWriteTime";
lvExplorer.Columns.Add(header);
}
We create here two header columns and set the View property of our ListView to Details.
If you now add subItems to your ShowDirectoriesInListView method you have a very nice detail view of your directories.
private void ShowDirectoriesInListView(string path)
{
DirectoryInfo info = new DirectoryInfo(path);
DirectoryInfo parent = info.Parent;
if (parent != null)
{
lvExplorer.Items.Add(new System.Windows.Forms.ListViewItem("...", 0));
}
foreach (DirectoryInfo dInfo in info.GetDirectories())
{
ListViewItem item = new System.Windows.Forms.ListViewItem(dInfo.Name, 0);
item.Tag = dInfo.Name;
//Add some subitems here
System.Windows.Forms.ListViewItem.ListViewSubItem subItem = new ListViewItem.ListViewSubItem();
subItem.Text = dInfo.LastWriteTime.ToShortDateString();
item.SubItems.Add(subItem);
lvExplorer.Items.Add(item);
}
}
These sub-items (listed in the ListViewItem.SubItems collection) are a bit confusing, if you are using a ListView for the first time. The items represent the first column of your ListView, where as the sub-items represent the columns to the right. So the sub-items are not, as you could think, the sub-directories you might have in mind.
This post shows you how you can handle the double click event on a item: https://stackoverflow.com/a/12874269/880990
Look into the DirectoryInfo and FileInfo classes of the System.IO namespace for file operations.

tree View fullpath stripping

Here is the application on codeplex all i did is created a new text box and trying to get path of current node selected into this text box, but i am getting extra things which i dont need at all,
Link to application,
Codeplex app
Code Line i am using is ,
TextBox1.Text = nodeCurrent.FullPath;
and output i am getting is something like this,
My Computer\C:\\Documents and Settings\Administrator\Desktop
My Computer here is Root Node, which i dont need, all i want is
C:\Documents and Settings\Administrator\Desktop
Picture added
Here is the function i am using it
private void tvFolders_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
//Populate folders and files when a folder is selected
this.Cursor = Cursors.WaitCursor;
//get current selected drive or folder
TreeNode nodeCurrent = e.Node;
string newPath = getFullPath(nodeCurrent.FullPath);
tbDirectory.Text = newPath;
//clear all sub-folders
nodeCurrent.Nodes.Clear();
if (nodeCurrent.SelectedImageIndex == 0)
{
//Selected My Computer - repopulate drive list
PopulateDriveList();
}
else
{
//populate sub-folders and folder files
PopulateDirectory(nodeCurrent, nodeCurrent.Nodes);
}
this.Cursor = Cursors.Default;
}
It looks to me like the getFullPath method in that code will do exactly what you want. It strips the MyComputer\ string and returns the rest. Write:
string newPath = getFullPath(nodeCurrent.FullPath);
add the following line in the code and it will remove recurring "\" in the path
newPath = newPath.Replace("\\\\", "\\");

Categories