If you turned on Virtualization because the datagrid loading time is terrible, then, here's the solution:
- turn off virtualization
let's assume/call your datagrid's items source binding as "Rows",
public IEnumerable<Row> Rows {get; set;}
Add a property to your "Row" class IsVisible and toggle it when you want to load the data (not when UI thread decides to resolve the binding and load each control)
the reason this works, is because when you load the grid, it checks the binding, all row are invisible, so the UI thread doesn't have to spin through all your rows*columns to create them, it can go to the next thing it needs to do. You on the other hand, can detect when is convenient time to turn those rows into visible, when the View.Visibility is visible, when the ViewModel loaded the data from somewhere, etc. So you are in total control. Below I am iterating through my item source (rows) using a task (in a background thread), yet setting Visibility on the UI thread.
private _isVisible = false;
/// <summary>
/// is false by default, for performance when loading first time.
/// </summary>
public bool IsVisible
{
get { return _isVisible; }
set
{
if (_isVisible == value)
return;
_isVisible = value;
RaisePropertyChanged(() => IsVisible);
}
}
In the View, when datagrid is loading don't allow it to hog the UI thread by putting the iteration of rows in the background thread, then set the Visibility to true. Even though you're on the background thread, IsVisible property changed will trigger you to update.
private void OnGridLoaded(object sender, RoutedEventArgs e)
{
//sample bool checks, you might not need them...
if (firstTimeLoad && !_isDataGridLoaded)
{
Task.Factory
.StartNew(() =>
{
/*first time loading performance tweak*/
foreach (var row in _viewModel.Rows)
ExeOnUi(() => { row.IsVisible = true; });
_firstTimeLoad = false;
})
}
forgot to add ExeOnUi code (you might check access using something else like whateverControl.Dispatcher.CheckAccess, I just use Microsoft.Practices.ServiceLocator):
static void ExeOnUi (Action action)
{
var srv= ServiceLocator.Current.GetInstance<IDispatchService> ();
if (srv.CheckAccess ())
action.Invoke ();
else
srv.Invoke (action);
}