Definition: Having 2D-array of string (about 10 columns, 1,600 rows, fixed length of 7-char) serving as a data source for WPF .NET 4.0 Grid control, the following code snippet has been used in order to populate the Grid with labels displaying values from array. Note: Grid was added to XAML and passed to the function PopulateGrid (see Listing 1.). The visual output is essentially a tabular data representation in read-only mode (no need for two-way binding).
Problem: Performance is a key issue. It took a mind-boggling 3...5 sec to complete this operation running on powerful Intel-i3/8GB-DDR3 PC; therefore, this WPF Grid performance is, IMHO, at least an order of magnitude slower than expected, based on comparison with similar controls/tasks in, e.g. regular WinForm data-aware controls, or even Excel worksheet.
Question 1: if there is a way to improve the performance of WPF Grid in scenario described above? Please direct your answer/potential improvement to the code snippet provided below in Listing 1 and Listing 2.
Question 1a: proposed solution could implement data binding to additional data-aware control, like for example DataGrid
to DataTable
. I've added string[,]
to DataTable dt
converter in Listing 2, so that additional control's DataContext
(or ItemsSource
, whatever) property could be bound to dt.DefaultView
. So, in the simplest form, could you please provide a compact (desirably about couple lines of code as it was done in old-style data-aware controls) and efficient (performance-wise) solution on data-binding of WPF DataGrid
to DataTable
object ?
Many Thanks.
Listing 1. Procedure to populate WPF Grid GridOut
from 2D string[,] Values
#region Populate grid with 2D-array values
/// <summary>
/// Populate grid with 2D-array values
/// </summary>
/// <param name="Values">string[,]</param>
/// <param name="GridOut">Grid</param>
private void PopulateGrid(string[,] Values, Grid GridOut)
{
try
{
#region clear grid, then add ColumnDefinitions/RowsDefinitions
GridOut.Children.Clear();
GridOut.ColumnDefinitions.Clear();
GridOut.RowDefinitions.Clear();
// get column num
int _columns = Values.GetUpperBound(1) + 1;
// add ColumnDefinitions
for (int i = 0; i < _columns; i++)
{
GridOut.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
}
// get rows num
int _rows = Values.GetUpperBound(0) + 1;
// add RowDefinitions
for (int i = 0; i < _rows; i++)
{
GridOut.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
}
#endregion
#region populate grid w/labels
// populate grid w/labels
for (int i = 0; i < _rows; i++)
{
for (int j = 0; j < _columns; j++)
{
// new Label control
Label _lblValue = new Label();
// assign value to Label
_lblValue.Content = Values[i, j].ToString();
// add Label to GRid
GridOut.Children.Add(_lblValue);
Grid.SetRow(_lblValue, i);
Grid.SetColumn(_lblValue, j);
}
}
#endregion
}
catch
{
GridOut.Children.Clear();
GridOut.ColumnDefinitions.Clear();
GridOut.RowDefinitions.Clear();
}
}
#endregion
Listing 2. string[,]
to DataTable
conversion
#region internal: Convert string[,] to DataTable
/// <summary>
/// Convert string[,] to DataTable
/// </summary>
/// <param name="arrString">string[,]</param>
/// <returns>DataTable</returns>
internal static DataTable Array2DataTable(string[,] arrString)
{
DataTable _dt = new DataTable();
try
{
// get column num
int _columns = arrString.GetUpperBound(1) + 1;
// get rows num
int _rows = arrString.GetUpperBound(0) + 1;
// add columns to DataTable
for (int i = 0; i < _columns; i++)
{
_dt.Columns.Add(i.ToString(), typeof(string));
}
// add rows to DataTable
for (int i = 0; i < _rows; i++)
{
DataRow _dr = _dt.NewRow();
for (int j = 0; j < _columns; j++)
{
_dr[j] = arrString[i,j];
}
_dt.Rows.Add(_dr);
}
return _dt;
}
catch { throw; }
}
#endregion
Note 2. It's recommended to replace Label
control w/TextBlock
using its Text property instead of Content as in case of Label
. It will speed up the execution a little bit, plus the code snippet will be forward compatible with VS 2012 for Win 8, which doesn't include Label
.
Note 3: So far I've tried binding DataGrid
to DataTable
(see XAML in Listing 3), but performance is very poor (grdOut
is a nested Grid
, that was used as a container for tabular data; _dataGrid
is a data-aware object type of DataGrid
).
Listing 3. DataGrid
binding to DataTable
: performance was poor, so I've removed that ScrollViewer
and not it's running OK.
<ScrollViewer ScrollViewer.CanContentScroll="True" VerticalScrollBarVisibility="Auto" >
<Grid Name="grdOut">
<DataGrid AutoGenerateColumns="True" Name="_dataGrid" ItemsSource="{Binding Path=.}" />
</Grid>
</ScrollViewer>