Need C# code converted to VB.NET for Silverlight "Export to CSV" [closed]
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 12 years ago.
Improve this questionI'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<str开发者_运维技巧ing> 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();
}
You may try a free online conversion tool.
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"™. 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
Of course, the best option of all here is to write no new code at all for a problem that's already been solved by thousands of other programmers. You can just go get an existing library that turn this whole mess into two or lines calling into that API.
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
精彩评论