views:

104

answers:

2

I have a very large comma-seperated text-file. I need to display this in a WPF Datagrid, which method would result in the highest performance of loading all the data to the grid? I'm only aware of two methods:

  • Using a Datatable, and adding each line as row (looks like overkill)
  • Using an ObservableCollection, creates object for each line (looks like overkill)

Is there a third method to fill the Datagrid, which would give higher performance?

+1  A: 

Depends on what you intend to do with the data. If the data is going to be read only, you can define a List and set it as the ItemsSource for the datagrid. Then the datagrid will automatically create all the rows to represent items in the list.

If the data is to be manipulated you can use a BindingList<> or an ObservableCollection<>.

How is "creates object for each line" an overkill? You have to represent a line/row of data as an object. That's just common sense, it's not possible to do it any other way.

Basically, if performance is what you are after then the datagrid's ItemsSource has to be as light weight as possible. A DataTable might be an overkill, if you don't intend to use all of it's features. A List<> on the other hand is as light weight as possible.

Also note that the WPF datagrid by default uses virtualization, which helps performance as well. There are some caveats thow, like don't put the datagrid inside a stackpanel otherwise the virtualization effect goes away - you can google about this.

One final thing, by my experience the .NET4 WPF builtin datagrid has a serious design bug. Basically each DataGridRow consumes 1MB of memory even for the simplest of data. With virtualization and only showing 30 rows, it's not a huge deal braker - only 30MB of memory consumed. But take a thousand rows, turn off virtualization and the memory consumption is 3GB!!!! On contrast, a WinForm datagridview, consumes more than 10x less memory with the same data. Even with virtualization disabled a thousand rows only takes 30MB...

I've opened a bug about this in Connect, but the specialized experts have yet to look into this issue. Not sure if the WPF toolkit datagrid behaves any better...

Marko
Thanks Marko! I changed everything to use List(Of) instead of Datatables. My new problem is, that using List(Of) I lost my ability to sort my Datagrid columns, but that's a whole different topic ;)
Joshua
You can use ObservableColletion then, that supports sorting.
Marko
Do you also happen to know if there is a way to emulate the .RowFilter property of the Dataview? Because i lost my ability to use the SQL-like queries.
Joshua
"SQL-like queries" == LINQ. Once you have selected a filter criteria (search box / combobox), then depending on your needs, you can either requery the original datasource and filter out the results or have the entire datasource in memory and create a new list from that every time you filter (essentially have two lists -> 1=OriginalSource; 2=Filtered/Copied => binded to datagrid). The first approach uses less overall memory, but might be slower to fetch data. The second approach uses more memory, but should be significantly faster.
Marko
A: 

You can bind to a DataView if you load a comma delimited file using OleDb.
Note: I did not test a large dataset.

Here is the binding to the DataGrid:

<Window x:Class="DatagridBackgroundWorker.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
    Loaded="Window_Loaded"
    Title="Main Window" Height="400" Width="800">
  <DockPanel>
    <Grid>
        <WpfToolkit:DataGrid  
            ItemsSource="{Binding Path=GridData, Mode=OneWay}" >
        </WpfToolkit:DataGrid>
    </Grid>
  </DockPanel>
</Window>

Here is the ViewModel:

public class MainViewModel : ViewModelBase
{
  public MainViewModel()
  {
     // name of the file
     string fileName = "MyData.txt";

     // location of the file
     string filePath = Environment.CurrentDirectory;
     string connection = @"Provider=Microsoft.Jet.OleDb.4.0; Data Source = " +
                         filePath +
                         ";Extended Properties=\"Text;HDR=Yes;FMT=Delimited\"";

     OleDbConnection conn = new OleDbConnection(connection);
     conn.Open();
     OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [" + fileName + "]", conn);
     adapter.Fill(_ds);
  }

  private DataSet _ds = new DataSet("MyDataSet");
  public DataView GridData
  {
     get
     {
        return _ds.Tables[0].DefaultView;
     }
  }
}

Here is the data file i used:

name,site,extra
jeff,codinghorror,stackoverflow
joel,joelonsoftware,stackoverflow
zamboni,secondbeach,stackoverflow
Zamboni