Resolved Help Exporting and Importing CSV

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
I need a way to open a csv file and write the contents of it into a listview component.
I tried but it doesn't work.

Here is my export from listview to csv:

Export to CSV:
        private async void savCoord_Click(object sender, EventArgs e)
        {

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            saveFileDialog.Filter = "CSV|*.csv";

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                using (StreamWriter sw = new StreamWriter(new FileStream(saveFileDialog.FileName, FileMode.Create), Encoding.UTF8))
                using (FileStream fileStream = new FileStream(saveFileDialog.FileName, FileMode.Create))
                using (StreamWriter streamWriter = new StreamWriter(fileStream))
                {
                    foreach (ListViewItem item in PositionsListView.Items)
                    {
                        await sw.WriteLineAsync(string.Format("{0},{1},{2},{3}", item.SubItems[0].Text, item.SubItems[1].Text, item.SubItems[2].Text, item.SubItems[3].Text));
                        MessageBox.Show("Success!", "Notice", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                }

            }
        }

And here is my Import Code (can't figure out how to get it to work)

Import from csv to Listview:
        private void openCoord_Click(object sender, EventArgs e)
        {

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            openFileDialog.Filter = "CSV|*.csv";

            try
            {
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    FileStream srcFS;
                    srcFS = new FileStream(openFileDialog.FileName, FileMode.Open);
                    StreamReader srcSR = new StreamReader(srcFS, System.Text.Encoding.Default);
                    foreach (ListViewItem item in PositionsListView.Items)
                    {
                        string ins = srcSR.ReadLine();
                        string[] row = { "X", "Y", "L/R", };
                        ListViewItem listViewItem = new ListViewItem(row);
                        PositionsListView.Items.Add(listViewItem);


                    }
                    
                    }
            }
            catch (Exception errorMsg)
            {
                MessageBox.Show(errorMsg.Message, "Error reading a file", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
If you don't want to use a third-party library, e.g. CsvHelper, then I suggest that you use the TextFieldParser class to read CSV data. It means referencing the Microsoft.VisualBasic library but that's not the end of the world.

That said, if your data is basic, i.e. values not quoted and no delimiters in the data, then you can read simply use string.Split:
C#:
foreach (var line in File.ReadLines(filePath))
{
    var fields = line.Split(',');

    // Use fields here.
}
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
Why are you creating two StreamWriters when you save the data? You only need one and you don't need to explicitly create a FileStream:
C#:
using (var writer = new StreamWriter(saveFileDialog.FileName))
{
    foreach (ListViewItem item in PositionsListView.Items)
    {
        await writer.WriteLineAsync($"{item.Text},{item.SubItems[1].Text},{item.SubItems[2].Text},{item.SubItems[3].Text}");
You don't even need a StreamWriter:
C#:
await File.WriteAllLinesAsync(saveFileDialog.FileName,
                              PositionsListView.Items
                                               .Cast<ListViewItem>()
                                               .Select(item => $"{item.Text},{item.SubItems[1].Text},{item.SubItems[2].Text},{item.SubItems[3].Text}"));
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
If you don't want to use a third-party library, e.g. CsvHelper, then I suggest that you use the TextFieldParser class to read CSV data. It means referencing the Microsoft.VisualBasic library but that's not the end of the world.

That said, if your data is basic, i.e. values not quoted and no delimiters in the data, then you can read simply use string.Split:
C#:
foreach (var line in File.ReadLines(filePath))
{
    var fields = line.Split(',');

    // Use fields here.
}
Hi, not sure what filepath is from, but I replaced it with "openFileDialog.FileName" but when I try to import data it says the file is in use. What am I missing here?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
If the CSV file you are trying to import is currently opened by Excel, then you there you have it. Excel likes to lock files to prevent other apps from editing things under it, but it also has the side effect of preventing other apps from reading the files as well. (They could have picked locking modes that allow read while locked, but I'm not sure why they didn't choose to do so.)
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
If the CSV file you are trying to import is currently opened by Excel, then you there you have it. Excel likes to lock files to prevent other apps from editing things under it, but it also has the side effect of preventing other apps from reading the files as well. (They could have picked locking modes that allow read while locked, but I'm not sure why they didn't choose to do so.)
the thing is that it isn't opened at all anywhere.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,712
Location
Chesapeake, VA
Programming Experience
10+
Your computer is telling you otherwise.

Looking at your code from post #1, I see that srcFS is never closed.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
Your computer is telling you otherwise.

Looking at your code from post #1, I see that srcFS is never closed.
If the advice from post #2 was taken, as seems to be the case, then that variable should no longer exist anyway, but who knows. This is an example of why, if you change your code and it doesn't work, you need to show us the new code. Don't ask us for help with code that we haven't seen.
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
If the advice from post #2 was taken, as seems to be the case, then that variable should no longer exist anyway, but who knows. This is an example of why, if you change your code and it doesn't work, you need to show us the new code. Don't ask us for help with code that we haven't seen.

Doesn't import anything into the ListView and I'm not sure what to replace "filePath" with

C#:
        private async void savCoord_Click(object sender, EventArgs e)
        {

            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            saveFileDialog.Filter = "CSV|*.csv";

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                using (var writer = new StreamWriter(saveFileDialog.FileName))
                {
                    foreach (ListViewItem item in PositionsListView.Items)
                    {
                        await writer.WriteLineAsync($"{item.Text},{item.SubItems[1].Text},{item.SubItems[2].Text},{item.SubItems[3].Text}");
                    }
                    MessageBox.Show("Success!", "Notice", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }

            }
        }


        private void openCoord_Click(object sender, EventArgs e)
        {

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            openFileDialog.Filter = "CSV|*.csv";

            try
            {
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    FileStream srcFS;
                    srcFS = new FileStream(openFileDialog.FileName, FileMode.Open);
                    StreamReader srcSR = new StreamReader(srcFS, System.Text.Encoding.Default);
                    foreach (var line in File.ReadLines(filePath))
                    {
                        var fields = line.Split(',');


                    }
                    
                    }
            }
            catch (Exception errorMsg)
            {
                MessageBox.Show(errorMsg.Message, "Error reading a file", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
If the advice from post #2 was taken, as seems to be the case, then that variable should no longer exist anyway, but who knows. This is an example of why, if you change your code and it doesn't work, you need to show us the new code. Don't ask us for help with code that we haven't seen.
the string is very simple, its 4 columns, data is stored as such for example

1,2,3,4
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
So the issue is exactly as Skydiver said. I told you to read the file using File.ReadLines and, for some reason, you thought that creating a FileStream and a StreamReader was still a good idea. It's not. You're not using them so why are you creating them?
I'm not sure what to replace "filePath" with
Put your thinking cap on for a moment. I provided you with an example. The point of an example is to demonstrate a principle that you can then apply to your own similar scenarios. If an example demonstrates reading a file and uses a variable named filePath, what do you suppose that represents? It seems fairly obvious that it represents the path of the file that is being read. If you want to apply the demonstrated principle in your own code then you have two choices: either declare a filePath variable and assign the file path to it and then use the example code as is, or else replace filePath in the example code with whatever you have that contains the path of the file.
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
So the issue is exactly as Skydiver said. I told you to read the file using File.ReadLines and, for some reason, you thought that creating a FileStream and a StreamReader was still a good idea. It's not. You're not using them so why are you creating them?

Put your thinking cap on for a moment. I provided you with an example. The point of an example is to demonstrate a principle that you can then apply to your own similar scenarios. If an example demonstrates reading a file and uses a variable named filePath, what do you suppose that represents? It seems fairly obvious that it represents the path of the file that is being read. If you want to apply the demonstrated principle in your own code then you have two choices: either declare a filePath variable and assign the file path to it and then use the example code as is, or else replace filePath in the example code with whatever you have that contains the path of the file.
had a brain fart but its still lingering. The data from the file still won't display in the listview

C#:
        private void openCoord_Click(object sender, EventArgs e)
        {

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            openFileDialog.Filter = "CSV|*.csv";

            try
            {
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string sFileName = openFileDialog.FileName;
                    foreach (var line in File.ReadLines(sFileName))
                    {
                        var fields = line.Split(',');
                    }
                    
                    }
            }
            catch (Exception errorMsg)
            {
                MessageBox.Show(errorMsg.Message, "Error reading a file", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
Look back at post #2. What does it say in the code after calling Split? Are you doing that? The data won't magically appear in the ListView. You have to put it there. My specific advice was about reading the file. I left the part where you put the data you read into the ListView up to you. Given that the documentation for the ListView class contains a code example that adds items, it's not really something that we need to demonstrate here. That's ignoring the fact that there are probably numerous other examples on the web too. Reading a file is something that can be done several different ways and the way you were doing it was suboptimal for the situation, which is why I showed you a specific way to do it.
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
Look back at post #2. What does it say in the code after calling Split? Are you doing that? The data won't magically appear in the ListView. You have to put it there. My specific advice was about reading the file. I left the part where you put the data you read into the ListView up to you. Given that the documentation for the ListView class contains a code example that adds items, it's not really something that we need to demonstrate here. That's ignoring the fact that there are probably numerous other examples on the web too. Reading a file is something that can be done several different ways and the way you were doing it was suboptimal for the situation, which is why I showed you a specific way to do it.
Still stuck now with an error "InvalidArgument=Value of '1' is not valid for 'index'. Parameter name: index"

C#:
        private void openCoord_Click(object sender, EventArgs e)
        {

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = Path.GetPathRoot(Environment.SystemDirectory);
            openFileDialog.Filter = "CSV|*.csv";

            try
            {
                
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    
                    string sFileName = openFileDialog.FileName;
                    foreach (var line in File.ReadLines(sFileName))
                    {
                        var fields = line.Split(',');

                        ListViewItem item = new ListViewItem(line.ToString());

                        item.SubItems.Add($"{item.SubItems[0]},{item.SubItems[1]},{ item.SubItems[2]},{ item.SubItems[3]}");

                    }
                    
                    }
            }
            catch (Exception errorMsg)
            {
                MessageBox.Show(errorMsg.Message, "Error reading a file", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

        }
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
You're not reading the words I'm posting. The specific comment in my code that I directed you to was this:
C#:
// Use fields here.
Here's the code that you just wrote:
C#:
ListViewItem item = new ListViewItem(line.ToString());

item.SubItems.Add($"{item.SubItems[0]},{item.SubItems[1]},{ item.SubItems[2]},{ item.SubItems[3]}");
Where in that code are you using fields? You're not, so you're not doing what I said to do, so why would you expect it to work?

Think about what you're doing. You have a string array containing four elements in the fields variable. You want to create a ListViewItem with those four values. How do you do that? Have you done any research on that? Have you read the documentation I mentioned earlier than contains a relevant code example?

I understand that you're relatively new to this but logic doesn't go out the window because you're new. If you are provided with instructions, follow those instructions. If there's something specific you don't understand, ask about that specifically. Don't just throw stuff at the wall and hope for the best. Don't do something unless there is a logical reason for doing it. Think your way through the steps required first, as though you had to do it manually, then write to implement those steps specifically. If you don't know what the code is supposed to do - that means the steps to get to a result, not just the result itself - then of course you won't be able to write the code to do it.
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
You're not reading the words I'm posting. The specific comment in my code that I directed you to was this:
C#:
// Use fields here.
Here's the code that you just wrote:
C#:
ListViewItem item = new ListViewItem(line.ToString());

item.SubItems.Add($"{item.SubItems[0]},{item.SubItems[1]},{ item.SubItems[2]},{ item.SubItems[3]}");
Where in that code are you using fields? You're not, so you're not doing what I said to do, so why would you expect it to work?

Think about what you're doing. You have a string array containing four elements in the fields variable. You want to create a ListViewItem with those four values. How do you do that? Have you done any research on that? Have you read the documentation I mentioned earlier than contains a relevant code example?

I understand that you're relatively new to this but logic doesn't go out the window because you're new. If you are provided with instructions, follow those instructions. If there's something specific you don't understand, ask about that specifically. Don't just throw stuff at the wall and hope for the best. Don't do something unless there is a logical reason for doing it. Think your way through the steps required first, as though you had to do it manually, then write to implement those steps specifically. If you don't know what the code is supposed to do - that means the steps to get to a result, not just the result itself - then of course you won't be able to write the code to do it.
I seriously have no damn clue, literally no clue, I've looked at the documentation for literally hours and find no correlation to the problem I am having at all, There doesnt seem to even be a way to use fields either. I'm completely lost
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
Think about the steps:
  1. Get the field values from the current line of the file.
  2. Create a ListViewItem containing the field values from 1.
  3. Add the ListViewItem from 2 to the ListView.
You've already got 1 from my code:
C#:
var fields = line.Split(',');
You're creating a ListViewItem here:
C#:
ListViewItem item = new ListViewItem(line.ToString());
but you're not using those field values to populate it. The documentation I directed you to contains this:
C#:
ListViewItem item1 = new ListViewItem("item1",0);
// Place a check mark next to the item.
item1.Checked = true;
item1.SubItems.Add("1");
item1.SubItems.Add("2");
item1.SubItems.Add("3");
so that shows you how to create a ListViewItem, set the Text or the item itself and then add three more subitems. Does that sound a little like what you want to do? You just have to substitute the text values that you already have for the values they use in the example. If you made the effort to use the documentation then you would know that the constructor they are using takes an index for the image but there's another constructor that takes just the text for those not using images. You're also not using check boxes:
C#:
var item = new ListViewItem(fields[0]);

item.SubItems.Add(fields[1]);
item.SubItems.Add(fields[2]);
item.SubItems.Add(fields[3]);
If you made just a little more effort then you'd know that there's another constructor that takes a string array, which fields is:
C#:
var item = new ListViewItem(fields);
Now all that remains is to add the ListViewItem you just created to the ListView:
C#:
PositionsListView.Items.Add(item);
That's following some simple logic and using the information that you already have at your fingertips. Follow instructions when they're provided. Read the relevant documentation. Think about the logic required first and write code to implement that logic specifically. It takes some practice but you need to make sure that you're practicing the right things. This process will become second nature in time.

Now, while that will achieve your aim, it does have an issue. Adding items to a ListView one by one like that is slow, because the control redraws after each item. That's not too big a deal if the number of items is small but performance can be horrendous for large numbers of items. There are two ways to address this issue; one is good and the other better.

The good way is to call BeginUpdate on the control before you start adding items and EndUpdate after you finish. That will prevent the control redrawing between adding items. The better way is to add all the items to a List<ListViewItem> first, then add them all to the ListView in a batch with a call to AddRange:
C#:
var items = new List<ListViewItem>();

foreach (var line in File.ReadLines(filePath))
{
    items.Add(new ListViewItem(line.Split(',')));
}

PositionsListView.Items.AddRange(items.ToArray());
 

ceobros

Member
Joined
Jul 8, 2022
Messages
10
Programming Experience
Beginner
Think about the steps:
  1. Get the field values from the current line of the file.
  2. Create a ListViewItem containing the field values from 1.
  3. Add the ListViewItem from 2 to the ListView.
You've already got 1 from my code:
C#:
var fields = line.Split(',');
You're creating a ListViewItem here:
C#:
ListViewItem item = new ListViewItem(line.ToString());
but you're not using those field values to populate it. The documentation I directed you to contains this:
C#:
ListViewItem item1 = new ListViewItem("item1",0);
// Place a check mark next to the item.
item1.Checked = true;
item1.SubItems.Add("1");
item1.SubItems.Add("2");
item1.SubItems.Add("3");
so that shows you how to create a ListViewItem, set the Text or the item itself and then add three more subitems. Does that sound a little like what you want to do? You just have to substitute the text values that you already have for the values they use in the example. If you made the effort to use the documentation then you would know that the constructor they are using takes an index for the image but there's another constructor that takes just the text for those not using images. You're also not using check boxes:
C#:
var item = new ListViewItem(fields[0]);

item.SubItems.Add(fields[1]);
item.SubItems.Add(fields[2]);
item.SubItems.Add(fields[3]);
If you made just a little more effort then you'd know that there's another constructor that takes a string array, which fields is:
C#:
var item = new ListViewItem(fields);
Now all that remains is to add the ListViewItem you just created to the ListView:
C#:
PositionsListView.Items.Add(item);
That's following some simple logic and using the information that you already have at your fingertips. Follow instructions when they're provided. Read the relevant documentation. Think about the logic required first and write code to implement that logic specifically. It takes some practice but you need to make sure that you're practicing the right things. This process will become second nature in time.

Now, while that will achieve your aim, it does have an issue. Adding items to a ListView one by one like that is slow, because the control redraws after each item. That's not too big a deal if the number of items is small but performance can be horrendous for large numbers of items. There are two ways to address this issue; one is good and the other better.

The good way is to call BeginUpdate on the control before you start adding items and EndUpdate after you finish. That will prevent the control redrawing between adding items. The better way is to add all the items to a List<ListViewItem> first, then add them all to the ListView in a batch with a call to AddRange:
C#:
var items = new List<ListViewItem>();

foreach (var line in File.ReadLines(filePath))
{
    items.Add(new ListViewItem(line.Split(',')));
}

PositionsListView.Items.AddRange(items.ToArray());

My dumbass was overthinking of how I was supposed to do it. My apologies if this problem made you frustrated towards me. Thank You for your help
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,646
Location
Sydney, Australia
Programming Experience
10+
My dumbass was overthinking of how I was supposed to do it.
Not uncommon. That's why I try to focus people on the logic rather than the code. The tendency can be to think about all the code constructs you know and what you can do to use them to get the result you want. That's really the reverse of what you should do. Think about the logic first, break it down into the smallest steps you can and then tackle each step individually, think about the best code construct to perform that step specifically. Each step should be so basic that it takes about three lines of code max, and often just one. If your logic is sound then implementing all the steps correctly will inherently produce the expected result. If it doesn't. you can then step through the code in debugger and examine every step to see which one doesn't do as expected. If they all do as expected and you still don't get the expected result then it's your logic that is at fault. Fear not for this is the process I went through when I started out too.
 

Scottintexas

Member
Joined
Jun 21, 2018
Messages
18
Location
Texas
Programming Experience
5-10
Just something to keep in mind when parsing csv files. Windows assumes that csv files should be opened by Excel. So users will simply double click a csv file and it opens with Excel. After they mess around with the spreadsheet and exit they MAY save the file. Excel will insert commas allover the place to make the file balanced in rows and columns. Watch you parsing for these phantom commas.

Replacing quotes in strings before parsing will make life much more simple. Read the entire file into a single string, use String.Replace(), split on \r\n or commas, whichever the case, all done.
 
Top Bottom