4
\$\begingroup\$

My little program I'm working on generates charts (GUI) and (on another tab) lists the values my chart is fed from.

2011.01.01 0:00:00     456
2011.01.02 0:00:00     45
2011.01.03 0:00:00     46
2011.01.04 0:00:00     6
2011.01.05 0:00:00     1500
...

When I click on the "Generate Chart" button, after the chart generator code has run, the following method is also invoked (that generates the table above):

void FillGridDetails(List<NameSeries> chartDataIn)

This fills a grid with the values. (DateTime as key, and float as value, nothing special...)

// Creating the rows.
gridDetails.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(30) }); // First row (Date/Time)
int maxCount = chartDataIn[maxIndex].seriesData.Count;
int rowCount = 1;

// Create the memory stream to write the data in (later this will be written to a file if the users clicks the export button).
ms = new MemoryStream(); // This is a class level variable.
StreamWriter writer = new StreamWriter(ms) { AutoFlush = true };
foreach (var aDataRow in chartDataIn[maxIndex].seriesData)
{
    // Adding the first column's rows.
    Label lblDate = new Label() { Content = aDataRow.Key.ToString(dateFormatIn), HorizontalAlignment = HorizontalAlignment.Center };
    gridDetails.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(30) });
    gridDetails.Children.Add(lblDate);
    Grid.SetRow(lblDate, rowCount);
    Grid.SetColumn(lblDate, 0);
    writer.Write(aDataRow.Key.ToString() + ";");

    // Adding the further columns' rows.
    for (int i = 0; i < chartDataIn.Count; i++)
    {
        string value = chartDataIn[i].seriesData.ContainsKey(aDataRow.Key) ? chartDataIn[i].seriesData[aDataRow.Key].ToString() : "-";
        Label lblData = new Label() { Content = value };
        gridDetails.Children.Add(lblData);
        Grid.SetRow(lblData, rowCount);
        Grid.SetColumn(lblData, i + 1);

        if (i == chartDataIn.Count - 1)
        {
            writer.WriteLine(value == "-" ? "" : value);
        }
        else
        {
            writer.Write(value == "-" ? ";" : value + ";");
        }
    }

    rowCount++;
}

On this tab there is a CSV Export button, click:

SaveFileDialog dlg = new SaveFileDialog()
{
    FileName = "EM_ExportedData",
    DefaultExt = ".csv",
    Filter = "CSV comma separated values (.csv)|*.csv"
};

if ((bool)dlg.ShowDialog())
{
    //try
    //{
    using (FileStream file = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write))
    {
        ms.WriteTo(file);
        ms.Dispose();
        btnExportCSV.Content = Strings.CSVExported;
        btnExportCSV.IsEnabled = false;
        MessageBox.Show(String.Format(Strings.successfulSavedCSVFile, dlg.FileName));
    }
}

Could it cause any sort of disadvantages in memory efficiency? I'm thinking of, e.g., not closing the filestream or something. If I close the fileStream, later I can't access the memory stream because that's also closed. Any recommendations for this code part is much appreciated.

(What I wanted to do is this: store the values in the memory or somewhere (right after the generation) and when the user wants to export it is simply written to the file. I was also thinking of copy paste the foreach loop but only with the memory-writing parts.)

\$\endgroup\$
  • \$\begingroup\$ I'm wondering if a simple List<string> to hold the data and IO.File.WriteAllLines to export it, would be just as efficient and a lot simpler to implement. \$\endgroup\$ – tinstaafl May 11 '14 at 3:01
  • \$\begingroup\$ Why don't you write to FileStream directly? \$\endgroup\$ – tia May 11 '14 at 18:39
  • \$\begingroup\$ Ugh, that's an ugly date format. If you want to go year, month, day, I suggest you use -s, to follow ISO 8601, not .s. \$\endgroup\$ – svick May 14 '14 at 16:02
3
\$\begingroup\$

It looks like you are wasting your efforts on premature optimization. Which also results in a quite ugly code.

I think you should separate your grid-filling logic from csv export. There should be no memory streams, steam writers, etc. in your first method. Also notice that you have code duplication in it, so you might want to extract label creation logic to separate method. Ideally you'd want to remove this logic all together and use WPF ListView (or ItemsControl) with data-binding instead.

As for your second method - you do not need any buffers. Just iterate through your data, create strings and write them directly to your FileStream. You might also want to encapsulate this logic in some separate class and perform it in the background thread so your UI does not freeze for large data.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.