I have a TabControl, in each TabItem there is a DataGrid. Each DataGridRow has a button on the DataGridRowHeader, which expands and collapses the RowDetails.
As I use the row details expansion functionality, so have set the VirtualizingPanel ScrollUnit to Pixel, so scrolling is a bit more natural.
The program captures which rows have been expanded in an ObservableCollection<int> which holds the row index to expand.
When I toggle to a new TabItem and back to the original TabItem, it loses which rows have been expanded.
Upon the DataContext being changed, I'd like to use the ObservableCollection to reset the expanded rows. I have tried this in the DataGrid DataContextChanged event.
public class DataGridBehaviors : Behavior<DataGrid>{
protected override void OnAttached(){
base.OnAttached();
this.AssociatedObject.DataContextChanged += DataGrid_DataContextChanged;
}
protected override void OnDetaching(){
this.AssociatedObject.DataContextChanged -= DataGrid_DataContextChanged;
base.OnDetaching();
}
private void DataGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){
ModuleGeometry oldModuleGeometry = (ModuleGeometry)e.OldValue;
ModuleGeometry newModuleGeometry = (ModuleGeometry)e.NewValue;
Task.Factory.StartNew(() => {
PerformRowExpansions(newModuleGeometry);
});
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
if (scrollViewer != null){
/*
* do some stuff
*/
}
}
private void PerformRowExpansions(ModuleGeometry newModuleGeometry){
ObservableCollection<int> tempExpandedIndexes = new ObservableCollection<int>(newModuleGeometry.ExpandedIndexes);
newModuleGeometry.ExpandedIndexes = new ObservableCollection<int>();
if (this.Dispatcher.CheckAccess()){
foreach (int index in tempExpandedIndexes)
{
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}
else{
this.Dispatcher.Invoke(new Action(() =>{
foreach (int index in tempExpandedIndexes){
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}));
}
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++){
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
child = GetVisualChild<T>(v);
if (child != null)
break;
}
return child;
}
}
However, it doesn't like doing this on rows that are not currently in the VirtualizingPanel.
How can I accomplish this?
Should I just do the expand the currently virtualized rows?
Should the DataGrid ScrollViewer be manipulated programmatically as well? How can I account for the rows not visible in the ScrollViewer?
Is there another object, other the DataGridRow that I should be setting the RowDetails Visibility to?