ListView spacing between items/ordering by groups - c#

How do I make some spacing between some items in my listView? I have a program, that lists all files in their respective folders, but now the list looks like:
---FolderName1---
fileName1
fileName2
fileName3
---FolderName2---
fileName1
fileName2
And so on...
The view mode I am using is List, and I would like to have some spacing where the folder names are, but I would like to avoid having to add another empty line, just to have a bit of space between the last file name and the folder name, and the first file. How do I achieve that?
for (int i = 0; i < folderNum; i++)
{
listView1.Items.Add(folderNum[i].Name);
for (int j = 0; j < fileNum; j++)
{
listView1.Items.Add(fileNum[j].Name);
}
}

You can use View=Details and create Groups. They are taller but unfortunately I found no way to set their Font etc..
You can add the Groups while you add the Items or before or later. You can add more Groupsor change to which an item belongs at any time. You can also remove a Group; in that case all its Items get move to the 'default' Group.
Here is an example:
Prepare a few data:
string root = #"D:\scrape\patterns";
string mask = "*10*";
List<string> folders = Directory.GetDirectories(root).ToList();
List<string> files = Directory.GetFiles(root, mask, SearchOption.AllDirectories).ToList();
Prepare the ListView:
listView1.View = View.Details;
listView1.Columns.Add("Png Files");
Now add Groups:
// 1st group: add root search folder..:
listView1.Groups.Add(root,root);
// .. and all 1st level nested folders as more (possible) groups:
foreach (string f in folders) listView1.Groups.Add(f, f);
Finally insert Items:
// insert items with groups:
foreach (string f in files)
{
string key = Path.GetDirectoryName(f);
string text = Path.GetFileName(f);
var lvi = listView1.Items.Add(text);
lvi.Group = listView1.Groups[key];
}
Note that you can change the Group an Item belongs to at any time..:
listView2.Items[2].Group = listView2.Groups[1];
A few notes:
If you look closely you can see that the Item I moved was assigned to a Group which was empty before. Those are not displayed at all.
Any Item without a Group is added to a Group "default", which is displayed at the top. You can't change the name, so it is best to create one of your own and assign 'ungrouped' items to it. I used the name of the search root for this.
I know of no way to style the groups beyond HeaderText and HeaderAlignment; there seem to be no properties nor are they drawn in any of the DrawXXX events. There are strangley incomplete hints at doing it in an DrawVisualStyleElementListViewGroup1 event or function but I couldn't find out how to use it..

Related

How can I iterate through selected ComboBoxes controls with a loop in C#?

I am working in a simple C# form. I have a total of 20 ComboBoxes . 10 ComboBoxes will contain similar data type and will have very similar name (CB1, CB2, CB3, ... CB10). Each ComboBox was loaded with a list of 5 elements A,B,C,D,E (by this I meant that I added those values to each of the 10 "CB" ComboBoxes). My intend is that the user can have the ability to select items (one out of A,B,C,D,E) from either 1 combobox, or 2 comboboxes, . . . ., or all of them (10 comboboxes).
I wish to store the data from the ComboBoxes where an item was selected in a list or array. For that I would like to iterate through the 10 ComboBoxes (the comboboxes which names are CB1, CB2, CB3, ..., CB10), check if the an item was selected in the combobox, and store the value selected in the ComboBox into a list (in the code below the list is called symbols). At the end the length of my symbols list (number of items) will depend on how many ComboBoxes the user selected from. Here is the code I am using:
List<string> symbols = new List<string>();
for (int i = 1; i <= 10; i++)
{
var my_comboBox = this.Controls["CB" + i.ToString()];
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
when I run the code I get the following error.
Object reference not set to an instance of an object.
Could anyone please explain what I am doing wrong? I got the code from another question that was posted and answered, below is the link to that question. Thanks in advance.
How can I iterate all ComboBoxes controls with a loop in C#?
I also tried the other alternative proposed on the answers to the questions cited. But it did not work. It does not go through the foreach loop (no error is shown though). Here is the code for option 2.
List<string> symbols = new List<string>();
var comboBoxes = this.Controls.OfType<ComboBox>().Where(x => x.Name.StartsWith("CB"));
foreach (var cmbBox in comboBoxes)
{
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
Again if anyone can please provide me with ideas to what I am doing wrong that would be very nice. Thanks in advance once more.
The ComboBoxes belong to the form as shown in the picture below. In there the ComboBoxes are called component_CB1, component_CB2, component_CB3, ..., component_CB10 (I changed the name of the ComboBoxes in the question to CB to make it easier to understand).
screenshot of my solution explorer
Thanks to everyone who contributed to find the answer to this problem. Please read the comments to identify the contributors.
The answer is that you can iterate through selected ComboBoxes in C#. However for this to work you need to make sure you know to what container control your ComboBoxes are added.
To know to container control your ComboBoxes are added to, go to View → Other Windows → Document Outline. You can see if those controls are directly children of the form or they are children of another container control.
If the ComboBoxes are added to your form directly, then here there are two alternatives to iterate through the ComboBoxes:
ALTERNATIVE 1: (ComboBoxes added to the form directly)
List<string> symbols = new List<string>();
for (int i = 1; i <= 10; i++)
{ //CB is the begining of the name of the comboboxes CB1, CB2, ... CB10
var my_comboBox = this.Controls["CB" + i.ToString()];
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
ALTERNATIVE 2: (ComboBoxes added to the form directly)
List<string> symbols = new List<string>();
//CB is the begining of the name of the comboboxes CB1, CB2, ... CB10
var comboBoxes = this.Controls.OfType<ComboBox>().Where(x=>x.Name.StartsWith("CB"));
foreach (var cmbBox in comboBoxes)
{
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
If the ComboBoxes are added to a different control container (e.g. a tab in a TabControl as in my initial case), you must specify the control container name rather than "this". Here are two alternatives taking as example the ComboBoxes Cb1, CB2, CB3, ..., CB10 that are added to a tab called tab1.
ALTERNATIVE 1: (ComboBoxes added to a tab of a TabControl)
List<string> symbols = new List<string>();
for (int i = 1; i <= 10; i++)
{//CB is the begining of the name of the comboboxes CB1, CB2, ... CB10
var my_comboBox = tab1.Controls["CB" + i.ToString()];
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
ALTERNATIVE 2: (ComboBoxes added to a tab of a TabControl)
List<string> symbols = new List<string>();
//CB is the begining of the name of the comboboxes CB1, CB2, ... CB10
var comboBoxes = tab1.Controls.OfType<ComboBox>().Where(x
=>x.Name.StartsWith("CB"));
foreach (var cmbBox in comboBoxes)
{
if (null != my_comboBox.SelectedItem)
{ symbols.add(my_comboBox.Text); }
}
Thanks for the help again. I hope this can be useful for others.

ArgumentOutOfRangeException when looping with foreach and counter variable into ListView?

I have a multiselect OpenFileDialog which is looping through every line in multiple files and keeping count in order to do specific work by Index. How do I stop it from giving me an ArgumentOutOfRangeException when loading more than 1 file? Listview is already populated with items and subitems collection into two additional headers. The two files combined will only be loading about 6 items into column [1].
public void LoadStudents()
{
var ofdLoadStudents = new OpenFileDialog();
ofdLoadStudents.Multiselect = true;
int Counter = 0;
if (ofdLoadStudents.ShowDialog() == DialogResult.OK)
{
foreach (string studentList in ofdLoadStudents.FileNames)
{
foreach (string Students in File.ReadAllLines(studentList))
{
//[Period 1] | [ReadAllLines Data]
//listview already populated with 10 items, and subitems with "" as the item.
//only loading total of 6 lines with 2 files, into [1] column.
listViewStudents.Items[Counter].SubItems[1].Text = Students;
Counter++;
}
}
}
}
An "ArgumentOutOfRangeException" can be caused when attempting to access an element outside of an collection. For instance, let's say you have a List with 5 ints inside of it. Now, let's say you try to access element 7. There is no element seven, so you will get an ArgumentOutOfRangeException.
There are two places I see in your above code that could cause this problem, and they are both on the same line:
listViewStudents.Items[Counter].SubItems[1].Text = Students;
The first problem location is the listViewStudents.Items[Counter] part. The Items object in listViewStudents is a collection which must have objects added to it before accessing them. If you don't add any objects to "Items", or if your Counter variable gets too large you will try to access an element of the Items object which does not exist, so you will get the error. This is where I think the problem most likely is. Where are you adding items to the listViewStudents.Items collection? Is it somewhere else in your code? Make sure this is initialized before trying to access the elements. Also, if you are adding them somewhere else in your code, how do you know that the number of lines in the text file you are reading does not exceed the number of elements in the Items collection? These are the things you need to take in to account when dealing with any type of collection.
The second problem location is in the SubItems[1] part. SubItems is also a collection, and if it is not initialized with at least two elements (you're accessing the second element by calling SubItems[1], it starts with SubItems[0]) then you will also get an ArgumentOutOfRangeException.
So your problem is not with your foreach loops, they look fine.
EDIT:
I quickly wrote some code that implements what I think you're trying to do. Are you trying to read a list of student names and add them to a WinForm ListView control? If so, this code will do that.
public void LoadStudents()
{
var ofdLoadStudents = new OpenFileDialog();
ofdLoadStudents.Multiselect = true;
int Counter = 0;
if (ofdLoadStudents.ShowDialog() == DialogResult.OK)
{
foreach (string studentList in ofdLoadStudents.FileNames)
{
foreach (string Students in File.ReadAllLines(studentList))
{
//[Period 1] | [ReadAllLines Data]
//has about 10 items | all "" fields.
//only loading total of 6 lines with 2 files combined.
listViewStudents.Items.Add(new ListViewItem(new string[] { Counter.ToString(), Students })); //This is the new code
Counter++;
}
}
}
}
This will lead to a listView which displays a series of numbers 0,1,2... up to the number of lines in the text file.
If you want to display the student name instead then flip around the Students and Counter.ToString() elements in the array.
listViewStudents.Items.Add(new ListViewItem(new string[] { Counter.ToString(), Students }));

Alphabatize all images pulled from a resx file C#

I have a drop down combo box that changes the image of a picture box. The images are stored in a resx file with an array that counts them, so that if I ever decide to add more, I need only to update the combo box, and add the images to the resx file. The problem I'm having is that when I update the image with the combo box, the images in the resx aren't in alphabetical order, but they do change the image on the picture box.
Here is my code
ResourceSet cardResourceSet = Properties.Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
attributes = new Image[cardCount];
cardCount = 0;
foreach (DictionaryEntry entry in cardResourceSet)
{
string resourceKey = (string)entry.Key;
object resource = entry.Value;
cardCount++;
}
attributes = new Image[cardCount];
cardCount = 0;
foreach (DictionaryEntry entry in cardResourceSet)
{
attributes[cardCount] = (Image)entry.Value;
cardCount++;
}
if (attributeBox.SelectedIndex != -1)
{
this.cardImage.Image = attributes[attributeBox.SelectedIndex];
}
How can I go about making it sort the resources in the resx alphabetically?
The return type of GetResourceSet, ResourceSet, implements IEnumerable, so you should be able to run some LINQ ordering on it:
foreach (var entry in cardResourceSet.Cast<DictionaryEntry>().OrderBy(de => de.Key))
{
}
Since you're iterating over it twice (though I'm not really sure of the point of the first for-loop, unless there's other code), you may want to assign the sorted results to a separate variable:
var sortedCardResourceSet.Cast<DictionaryEntry>().OrderBy(de => de.Key).ToList();
foreach (var entry in sortedCardResourceSet)
{
...
}
...

How to rename a selected text from a file?

I've made a program where you can add text over textbox and it adds it into a listbox. Now to actually save it on the next time when the application launches, I decided to store it into some text.ini file.
Now that the next time the application launches (form load), using the StreamReader it reads all the text from that file and displays it into a listbox.
I have decided to implement an edit functionality (select the item from listbox -> right click -> edit), and rename the file (in case something was misspelled).
My problem is this:
If I have 1 item in the listbox and rename it, it works perfectly fine.
But if I have 2 or more items in the listbox but decide to rename only 1, it removes all text from the file and renames only the selected item.
This is my code:
string itemName = (string)lBoxRadioLinks.SelectedItem;
var renameFile = Interaction.InputBox("Enter a new name for the file:", "Rename File", itemName);
// Gets the selected item full name
var url = lBoxRadioLinks.GetItemText(lBoxRadioLinks.SelectedIndex);
string lineToRename = url.ToString();
// Selected file
//MessageBox.Show(lineToRename);
// ACTUALLY rename the text
string[] lines = File.ReadAllLines("IO/sites.ini");
List<string> list = new List<string>();
foreach (string line in lines)
{
if (line != lineToRename)
list.Add(line);
}
// Have to convert the string into array for an actual rename
string[] arr = new string[] { renameFile };
// Visually rename the file (display in listbox asap)
lBoxRadioLinks.DataSource = arr;
// Renames the file
File.WriteAllLines("IO/sites.ini", arr);
Obviously the method "WriteAllLines" will rename the whole text inside of a file, but I don't want that.
How can I rename a text that is at a certain line, without effecting other lines in a file?
I don't really know what you're asking, but I'll give this my best shot.
If we're understanding each other when we agree that you want to replace certain text in a text file, and if you're actually okay loading the whole thing into memory, then you might as well just use the string.Replace method.
Dictionary<string, string> changesToMake = ...;
var contents = File.ReadAllText(filename);
foreach(var change in changesToMake)
{
contents = contents.Replace(change.Key, change.Value);
}
File.WriteAllText(filename, contents);
As for the problem you're having with multiple items in the ListBox, you're only referencing the SelectedItem and SelectedIndex properties, so nothing should get changed anywhere other than at those...files? I'm still not even sure of what's in that ListBox. If you want to alter more than one at a time, you should be using the pluralized forms of each of those properties to access all of the selected items and not just the first.

Change column in ListView

My application contains ListView with files that i am handling and have 3 columns: file name, length and status.
Inside my for loop i am handling file after file and want in this case to change the status column from wait which is the value at the beginning to in process.
is it possible to change one column ?
lvFiles is my ListView
for (int i = 0; i < lvFiles.Items.Count; i++)
{
//here i am do things with my file
}
Here i am adding files into my ListView:
ListViewItem item = new ListViewItem(new string[] { new FileInfo(filePath).Name, duration, "Waiting" });
Use SubItems property of ListViewItem:
foreach(ListViewItem item in lvFiles.Items)
item.SubItems[2].Text = "Waiting";
You can try something like this if you know the specific Column for example Address would be
colString[2] you can do a single line
string[] colString = new string{ "Starting", "Paused", "Waiting" };
int colIndex = 0;
foreach (ColumnHeader lstViewCol in lvFiles.Columns)
{
lstViewCol.Text = colString[colIndex];
colIndex++;
}
for single column you stated you wanted the 3rd column then you could something like this
lvFiles.Colunns[2] = "waiting";

Categories

Resources