views:

91

answers:

3

I'm looking to get the following code converted to working VB.NET code. I need to export data from a datagrid into the CSV format that the user can save off, and the code below from David in Dakota would work great, but it's in C#. Any help would be appreciated! This is for a Silverlight 4 site we're working on.

private void exportHistoryButton_Click(object sender, RoutedEventArgs e)
{
    string data = ExportDataGrid(true, historyDataGrid);
    SaveFileDialog sfd = new SaveFileDialog()
    {
        DefaultExt = "csv",
        Filter = "CSV Files (*.csv)|*.csv|All files (*.*)|*.*",
        FilterIndex = 1
    };
    if (sfd.ShowDialog() == true)
    {
        using (Stream stream = sfd.OpenFile())
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.Write(data);
                writer.Close();
            }
            stream.Close();
        }
    }
}

private string FormatCSVField(string data)
{
    return String.Format(
            "\"{0}\"",
            data.Replace("\"", "\"\"\"")
            .Replace("\n", "")
            .Replace("\r", "")
        );
}

public string ExportDataGrid(bool withHeaders, DataGrid grid)
{
    string colPath;
    System.Reflection.PropertyInfo propInfo;
    System.Windows.Data.Binding binding;
    System.Text.StringBuilder strBuilder = new System.Text.StringBuilder();
    System.Collections.IList source = (grid.ItemsSource as System.Collections.IList);
    if (source == null)
        return "";

    List<string> headers = new List<string>();
    grid.Columns.ToList().ForEach(col =>
    {
        if (col is DataGridBoundColumn)
        {
            headers.Add(FormatCSVField(col.Header.ToString()));
        }
    });
    strBuilder
    .Append(String.Join(",", headers.ToArray()))
    .Append("\r\n");

    foreach (Object data in source)
    {
        List<string> csvRow = new List<string>();
        foreach (DataGridColumn col in grid.Columns)
        {
            if (col is DataGridBoundColumn)
            {
                binding = (col as DataGridBoundColumn).Binding;
                colPath = binding.Path.Path;
                propInfo = data.GetType().GetProperty(colPath);
                if (propInfo != null)
                {
                    csvRow.Add(FormatCSVField(propInfo.GetValue(data, null).ToString()));
                }
            }
        }
        strBuilder.Append(String.Join(",", csvRow.ToArray())).Append("\r\n");
    }
    return strBuilder.ToString();
}
+2  A: 

You may try a free online conversion tool.

Darin Dimitrov
+1  A: 

This isn't a question, it's a request. But try this:

http://www.developerfusion.com/tools/convert/csharp-to-vb/

Private Sub exportHistoryButton_Click(sender As Object, e As RoutedEventArgs)
    Dim data As String = ExportDataGrid(True, historyDataGrid)
    Dim sfd As New SaveFileDialog() With { _
        .DefaultExt = "csv", _
        .Filter = "CSV Files (.csv)|.csv|All files (.)|.", _
        .FilterIndex = 1 _
    }
    If sfd.ShowDialog() = True Then
        Using stream As Stream = sfd.OpenFile()
            Using writer As New StreamWriter(stream)
                writer.Write(data)
                writer.Close()
            End Using
            stream.Close()
        End Using
    End If
End Sub

Private Function FormatCSVField(data As String) As String
    Return [String].Format("""{0}""", data.Replace("""", """""""").Replace(vbLf, "").Replace(vbCr, ""))
End Function

Public Function ExportDataGrid(withHeaders As Boolean, grid As DataGrid) As String
    Dim colPath As String
    Dim propInfo As System.Reflection.PropertyInfo
    Dim binding As System.Windows.Data.Binding
    Dim strBuilder As New System.Text.StringBuilder()
    Dim source As System.Collections.IList = TryCast(grid.ItemsSource, System.Collections.IList)
    If source Is Nothing Then
        Return ""
    End If

    Dim headers As New List(Of String)()
    grid.Columns.ToList().ForEach(Function(col) Do
        If TypeOf col Is DataGridBoundColumn Then
            headers.Add(FormatCSVField(col.Header.ToString()))
        End If
    End Function)
    strBuilder.Append([String].Join(",", headers.ToArray())).Append(vbCr & vbLf)

    For Each data As [Object] In source
        Dim csvRow As New List(Of String)()
        For Each col As DataGridColumn In grid.Columns
            If TypeOf col Is DataGridBoundColumn Then
                binding = TryCast(col, DataGridBoundColumn).Binding
                colPath = binding.Path.Path
                propInfo = data.[GetType]().GetProperty(colPath)
                If propInfo IsNot Nothing Then
                    csvRow.Add(FormatCSVField(propInfo.GetValue(data, Nothing).ToString()))
                End If
            End If
        Next
        strBuilder.Append([String].Join(",", csvRow.ToArray())).Append(vbCr & vbLf)
    Next


    Return strBuilder.ToString()
End Function
Meff
I did try that site and the vb code it created wasn't valid. Taking a look at it again now to see if I can get it cleaned up.Thanks.
Uncle Charlie
+1  A: 

Here's a mostly-direct translation. I made a few improvements but left if close enough to original that you should still recognize it. But we can do a lot better, so keep reading after this initial attempt for something better:

Private Sub exportHistoryButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) 
    Dim sfd As New SaveFileDialog() 
    sfd.DefaultExt = "csv"
    sfd.Filter = "CSV Files (*.csv)|*.csv|All files (*.*)|*.*"
    sfd.FilterIndex = 1 

    If sfd.ShowDialog() Then
        Using writer As New StreamWriter(sfd.OpenFile())
           writer.Write(ExportDataGrid(True, historyDataGrid))
        End Using
    End If 

End Sub

Private Function FormatCSVField(ByVal data As String) As String
    Return String.Format("""{0}""", _
         data.Replace("""", """""").Replace(vbLf, "").Replace(vbCr, ""))
End Function 

Public Function ExportDataGrid(ByVal withHeaders As Bool, ByVal grid As DataGrid) As String
    Dim source As System.Collections.IList = TryCast(grid.ItemsSource, System.Collections.IList) 
    If source Is Nothing Then Return "" 

    Dim builder As New System.Text.StringBuilder()

    If withHeaders Then
        Dim headers As new List(Of String)()
        For Each col As DataColumn In grid.Columns.Where(Function(c) TypeOf c is DataGridBoundColumn)
            headers.Add(FormatCSVField(col.Header.ToString()))
        Next col
        builder.Append(String.Join(",", headers.ToArray())).Append(vbCrLf)
    End If

    For Each row in source
        Dim csvRow As New List(Of String)()
        For Each col As DataGridBoundColumn in grid.Columns.Where(Function(c) TypeOf col Is DataGridBoundColumn) 
            Dim propInfo As System.Reflection.PropertyInfo = _ 
                  row.GetType().GetProperty(col.Binding.Path.Path)
            If propInfo IsNot Nothing Then  
                csvRow.Add(FormatCSVField(propInfo.GetValue(row, Nothing).ToString())) 
            End If 
        Next col 

        builder.Append(String.Join(",", csvRow.ToArray())).Append(vbCrLf) 
    Next row 

    Return builder.ToString()
End Function 

The big problem with this code is that when you put your entire grid into a single string variable, you create an object that will very likely end up on the Large Object Heap. This is a "Bad Thing"TM. One of the things that makes it so bad is that it often doesn't look bad in your performance testing. It's the kind of problem that won't show up until production. But trust me when I say we can do better.

If this were still going to be C# I'd refactor it to use an iterator block to yield return the string for one row at a time, and then write that out. Since it's VB, we'll refactor it to accept a TextWriter and write directly to that:

Private Sub exportHistoryButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) 
    Dim sfd As New SaveFileDialog() 
    sfd.DefaultExt = "csv"
    sfd.Filter = "CSV Files (*.csv)|*.csv|All files (*.*)|*.*"
    sfd.FilterIndex = 1 

    If sfd.ShowDialog() Then
        Using writer As New StreamWriter(sfd.OpenFile())
            ExportDataGrid(historyDataGrid, writer) ''# Notice the change here
        End Using
    End If 

End Sub

Private Function WriteCSVField(ByVal data As String, ByVal writer As TextWriter) As String
    ''#This would likely do a _lot_ better with a state machine to iterate over
    ''# each character rather than create 3 new strings for every field, 
    ''# but we'll let it slide for now
    writer.Write(""""c)
    writer.Write(data.Replace("""", """""").Replace(vbLf, "").Replace(vbCr, ""))
    writer.Write(""""c)
End Function 

''# Notice the new function signature
Public Sub ExportDataGrid(ByVal grid As DataGrid, ByVal writer As TextWriter, Optional ByVal WithHeaders As Boolean = True)
    Dim source As System.Collections.IList = TryCast(grid.ItemsSource, System.Collections.IList) 
    If source Is Nothing Then Return "" 

    Dim delimiter As String
    If WithHeaders Then
        delimiter = ""
        For Each col As DataColumn In grid.Columns.Where(Function(c) TypeOf c is DataGridBoundColumn)
            writer.Write(delimiter)
            WriteCSVField(col.Header.ToString(), writer)
            delimiter = ","
        Next col
        writer.Write(vbCrLf)
    End If

    For Each row In source
        delimiter = ""
        For Each col As DataGridBoundColumn in grid.Columns.Where(Function(c) TypeOf col Is DataGridBoundColumn) 
            writer.Write(delimiter)
            delimiter = ","

            ''# I'm pretty sure this could be refactored to avoid the reflection,
            ''# But I also think there's a bug in your code here, so I'll
            ''# leave the direct translation for now
            Dim propInfo As System.Reflection.PropertyInfo = _ 
                  row.GetType().GetProperty(col.Binding.Path.Path)
            If propInfo IsNot Nothing Then  
                WriteCSVField(propInfo.GetValue(row, Nothing).ToString(), writer) 
            End If 
        Next col 
        writer.Write(vbCrLf)
    Next row 
End Sub
Joel Coehoorn